Redis: Takes JSON/YAML file that allows for renaming of commands

This commit is contained in:
Ricky Diaz Gomez 2019-06-17 15:23:56 -04:00
parent 043ca87528
commit f0d9070733
8 changed files with 112 additions and 19 deletions

@ -0,0 +1,4 @@
[
"PING",
"AUTH password!"
]

@ -0,0 +1,2 @@
- PING
- AUTH password!

@ -0,0 +1,6 @@
{
"PING": "RENAMED-PING",
"ECHO": "RENAMED-ECHO",
"AUTH": "RENAMED-AUTH",
"INFO": "RENAMED-INFO"
}

@ -0,0 +1,4 @@
PING: RENAMED-PING
ECHO: RENAMED-ECHO
AUTH: RENAMED-AUTH
INFO: RENAMED-INFO

@ -1,5 +1,5 @@
rename-command PING "" rename-command PING ""
rename-command DEBUG "RENAMED-DEBUG" rename-command DEBUG "RENAMED-DEBUG"
rename-command ECHO "RENAMED-ECHO" rename-command ECHO "RENAMED-ECHO"
rename-command INFO "" rename-command INFO "RENAMED-INFO"
rename-command CLIENT "RENAMED-CLIENT" rename-command CLIENT "RENAMED-CLIENT"

@ -6,11 +6,11 @@ echo "redis/setup: Tests setup for redis"
CONTAINER_TAG="zgrab_redis" CONTAINER_TAG="zgrab_redis"
configs="password renamed default" configs="default password renamed"
for cfg in $configs; do for cfg in $configs; do
CONTAINER_NAME="zgrab_redis_$cfg" CONTAINER_NAME="zgrab_redis_$cfg"
RUN_ARGS="--rm --name $CONTAINER_NAME -td $CONTAINER_TAG redis-server //usr/local/etc/redis/${cfg}.conf" RUN_ARGS="--rm --name $CONTAINER_NAME -td $CONTAINER_TAG redis-server //usr/local/etc/redis/${cfg}.conf"
# If the container is already running, use it. # If the container is already running, use it.
if docker ps --filter "name=$CONTAINER_NAME" | grep -q $CONTAINER_NAME; then if docker ps --filter "name=$CONTAINER_NAME" | grep -q $CONTAINER_NAME; then
@ -18,7 +18,7 @@ for cfg in $configs; do
exit 0 exit 0
fi fi
# If it is not running, try launching it -- on success, use that. # If it is not running, try launching it -- on success, use that.
echo "redis/setup: Trying to launch $CONTAINER_NAME..." echo "redis/setup: Trying to launch $CONTAINER_NAME..."
if ! docker run $RUN_ARGS; then if ! docker run $RUN_ARGS; then
echo "redis/setup: Building docker image $CONTAINER_TAG..." echo "redis/setup: Building docker image $CONTAINER_TAG..."

@ -4,6 +4,7 @@ set -e
MODULE_DIR=$(dirname $0) MODULE_DIR=$(dirname $0)
ZGRAB_ROOT=$MODULE_DIR/../.. ZGRAB_ROOT=$MODULE_DIR/../..
ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output
CONTAINER_DIR="./integration_tests/redis/container"
mkdir -p $ZGRAB_OUTPUT/redis mkdir -p $ZGRAB_OUTPUT/redis
@ -16,6 +17,8 @@ for cfg in $configs; do
echo "redis/test: Testing $CONTAINER_NAME" echo "redis/test: Testing $CONTAINER_NAME"
CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh redis > "$ZGRAB_OUTPUT/redis/${cfg}-normal.json" CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh redis > "$ZGRAB_OUTPUT/redis/${cfg}-normal.json"
CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh redis --inline > "$ZGRAB_OUTPUT/redis/${cfg}-inline.json" CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh redis --inline > "$ZGRAB_OUTPUT/redis/${cfg}-inline.json"
CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh redis --mappings "$CONTAINER_DIR/mappings.json" > "$ZGRAB_OUTPUT/redis/${cfg}-normal-mappings.json"
CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh redis --inline --mappings "$CONTAINER_DIR/mappings.json" > "$ZGRAB_OUTPUT/redis/${cfg}-inline-mappings.json"
done done
for cfg in $configs; do for cfg in $configs; do

@ -13,24 +13,28 @@
package redis package redis
import ( import (
"encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"path/filepath"
"strings" "strings"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/zmap/zgrab2" "github.com/zmap/zgrab2"
"gopkg.in/yaml.v2"
) )
// Flags contains redis-specific command-line flags. // Flags contains redis-specific command-line flags.
type Flags struct { type Flags struct {
zgrab2.BaseFlags zgrab2.BaseFlags
// TODO: Take a JSON/YAML file with a list of custom commands to execute? // TODO: Take a JSON/YAML file with a list of custom commands to execute? array?
// TODO: Take a JSON/YAML file with mappings for command names? CustomCommands string `long:"custom-commands" description:"Pathname for JSON/YAML file that contains extra commands to execute."`
AuthCommand string `long:"auth-command" default:"AUTH" description:"Override the command used to authenticate. Ignored if no password is set."` Mappings string `long:"mappings" description:"Pathname for JSON/YAML file that contains mappings for command names."`
Password string `long:"password" description:"Set a password to use to authenticate to the server. WARNING: This is sent in the clear."` Password string `long:"password" description:"Set a password to use to authenticate to the server. WARNING: This is sent in the clear."`
DoInline bool `long:"inline" description:"Send commands using the inline syntax"` DoInline bool `long:"inline" description:"Send commands using the inline syntax"`
Verbose bool `long:"verbose" description:"More verbose logging, include debug fields in the scan results"` Verbose bool `long:"verbose" description:"More verbose logging, include debug fields in the scan results"`
} }
// Module implements the zgrab2.Module interface // Module implements the zgrab2.Module interface
@ -39,7 +43,9 @@ type Module struct {
// Scanner implements the zgrab2.Scanner interface // Scanner implements the zgrab2.Scanner interface
type Scanner struct { type Scanner struct {
config *Flags config *Flags
commandMappings map[string]interface{}
customCommands []string
} }
// scan holds the state for the scan of an individual target // scan holds the state for the scan of an individual target
@ -124,6 +130,10 @@ func (flags *Flags) Help() string {
func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error { func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error {
f, _ := flags.(*Flags) f, _ := flags.(*Flags)
scanner.config = f scanner.config = f
err := scanner.initCommands()
if err != nil {
log.Fatal(err)
}
return nil return nil
} }
@ -152,6 +162,71 @@ func (scan *scan) Close() {
defer scan.close() defer scan.close()
} }
func getUnmarshaler(file string) (func([]byte, interface{}) error, error) {
var unmarshaler func([]byte, interface{}) error
switch ext := filepath.Ext(file); ext {
case ".json":
unmarshaler = json.Unmarshal
case ".yaml", ".yml":
unmarshaler = yaml.Unmarshal
default:
err := fmt.Errorf("File type %s not valid.", ext)
return nil, err
}
return unmarshaler, nil
}
func getFileContents(file string, output interface{}) error {
unmarshaler, err := getUnmarshaler(file)
if err != nil {
return err
}
fileContent, err := ioutil.ReadFile(file)
if err != nil {
return err
}
err = unmarshaler([]byte(fileContent), output)
if err != nil {
return err
}
return nil
}
// Initializes the command mappings
func (scanner *Scanner) initCommands() error {
scanner.commandMappings = map[string]interface{}{
"PING": "PING",
"AUTH": "AUTH",
"INFO": "INFO",
"NONEXISTENT": "NONEXISTENT",
"QUIT": "QUIT",
}
if scanner.config.CustomCommands != "" {
var customCommands []string
err := getFileContents(scanner.config.CustomCommands, &customCommands)
if err != nil {
return err
}
scanner.customCommands = customCommands
}
// User supplied a file for updated command mappings
if scanner.config.Mappings != "" {
var mappings map[string]string
err := getFileContents(scanner.config.Mappings, &mappings)
if err != nil {
return err
}
for origCommand, newCommand := range mappings {
scanner.commandMappings[strings.ToUpper(origCommand)] = strings.ToUpper(newCommand)
}
}
return nil
}
// SendCommand sends the given command/args to the server, using the scanner's // SendCommand sends the given command/args to the server, using the scanner's
// configuration, and drop the command/output into the result. // configuration, and drop the command/output into the result.
func (scan *scan) SendCommand(cmd string, args ...string) (RedisValue, error) { func (scan *scan) SendCommand(cmd string, args ...string) (RedisValue, error) {
@ -228,9 +303,9 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
} }
defer scan.Close() defer scan.Close()
result := scan.result result := scan.result
pingResponse, err := scan.SendCommand("PING") pingResponse, err := scan.SendCommand(scanner.commandMappings["PING"].(string))
if err != nil { if err != nil {
// if the first command fails (as opposed to succeeding but returning an // If the first command fails (as opposed to succeeding but returning an
// ErrorMessage response), then flag the probe as having failed. // ErrorMessage response), then flag the probe as having failed.
return zgrab2.TryGetScanStatus(err), nil, err return zgrab2.TryGetScanStatus(err), nil, err
} }
@ -238,19 +313,18 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
// we have positively identified that a redis service is present. // we have positively identified that a redis service is present.
result.PingResponse = forceToString(pingResponse) result.PingResponse = forceToString(pingResponse)
if scanner.config.Password != "" { if scanner.config.Password != "" {
authResponse, err := scan.SendCommand(scanner.config.AuthCommand, scanner.config.Password) authResponse, err := scan.SendCommand(scanner.commandMappings["AUTH"].(string), scanner.config.Password)
if err != nil { if err != nil {
return zgrab2.TryGetScanStatus(err), result, err return zgrab2.TryGetScanStatus(err), result, err
} }
result.AuthResponse = forceToString(authResponse) result.AuthResponse = forceToString(authResponse)
} }
infoResponse, err := scan.SendCommand("INFO") infoResponse, err := scan.SendCommand(scanner.commandMappings["INFO"].(string))
if err != nil { if err != nil {
return zgrab2.TryGetScanStatus(err), result, err return zgrab2.TryGetScanStatus(err), result, err
} }
result.InfoResponse = forceToString(infoResponse) result.InfoResponse = forceToString(infoResponse)
infoResponseBulk, ok := infoResponse.(BulkString) if infoResponseBulk, ok := infoResponse.(BulkString); ok {
if ok {
for _, line := range strings.Split(string(infoResponseBulk), "\r\n") { for _, line := range strings.Split(string(infoResponseBulk), "\r\n") {
if strings.HasPrefix(line, "redis_version:") { if strings.HasPrefix(line, "redis_version:") {
result.Version = strings.SplitN(line, ":", 2)[1] result.Version = strings.SplitN(line, ":", 2)[1]
@ -258,12 +332,12 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
} }
} }
} }
bogusResponse, err := scan.SendCommand("NONEXISTENT") bogusResponse, err := scan.SendCommand(scanner.commandMappings["NONEXISTENT"].(string))
if err != nil { if err != nil {
return zgrab2.TryGetScanStatus(err), result, err return zgrab2.TryGetScanStatus(err), result, err
} }
result.NonexistentResponse = forceToString(bogusResponse) result.NonexistentResponse = forceToString(bogusResponse)
quitResponse, err := scan.SendCommand("QUIT") quitResponse, err := scan.SendCommand(scanner.commandMappings["QUIT"].(string))
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return zgrab2.TryGetScanStatus(err), result, err return zgrab2.TryGetScanStatus(err), result, err
} }