-
Notifications
You must be signed in to change notification settings - Fork 316
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #471 from AlexAQ972/socks5
Add support for socks5
- Loading branch information
Showing
8 changed files
with
371 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
internal 0.0.0.0 | ||
external 0.0.0.0 | ||
|
||
maxconn 10 | ||
|
||
auth none | ||
|
||
socks -p1080 | ||
|
||
allow * | ||
|
||
flush |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/usr/bin/env bash | ||
|
||
set +e | ||
|
||
echo "socks5/cleanup: Tests cleanup for socks5" | ||
|
||
CONTAINER_NAME=zgrab_socks5 | ||
|
||
docker stop $CONTAINER_NAME |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#!/usr/bin/env bash | ||
|
||
echo "socks5/setup: Tests setup for socks5" | ||
|
||
CONTAINER_TAG="3proxy/3proxy" | ||
CONTAINER_NAME="zgrab_socks5" | ||
|
||
# If the container is already running, use it. | ||
if docker ps --filter "name=$CONTAINER_NAME" | grep -q $CONTAINER_NAME; then | ||
echo "socks5/setup: Container $CONTAINER_NAME already running -- nothing to setup" | ||
exit 0 | ||
fi | ||
|
||
DOCKER_RUN_FLAGS="--rm --name $CONTAINER_NAME -e "PROXY_USER=user" -e "PROXY_PASS=password" -v ./3proxy.cfg:/etc/3proxy/3proxy.cfg -td" | ||
|
||
# If it is not running, try launching it -- on success, use that. | ||
echo "socks5/setup: Trying to launch $CONTAINER_NAME..." | ||
if ! docker run $DOCKER_RUN_FLAGS $CONTAINER_TAG; then | ||
echo "failed" | ||
# echo "socks5/setup: Building docker image $CONTAINER_TAG..." | ||
# # If it fails, build it from ./container/Dockerfile | ||
# docker build -t $CONTAINER_TAG ./container | ||
# # Try again | ||
# echo "socks5/setup: Launching $CONTAINER_NAME..." | ||
# docker run $DOCKER_RUN_FLAGS $CONTAINER_TAG | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -e | ||
MODULE_DIR=$(dirname $0) | ||
ZGRAB_ROOT=$(git rev-parse --show-toplevel) | ||
ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output | ||
|
||
mkdir -p $ZGRAB_OUTPUT/socks5 | ||
|
||
CONTAINER_NAME=zgrab_socks5 | ||
|
||
OUTPUT_FILE=$ZGRAB_OUTPUT/socks5/socks5.json | ||
|
||
echo "socks5/test: Tests runner for socks5" | ||
# TODO FIXME: Add any necessary flags or additional tests | ||
CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh socks5 > $OUTPUT_FILE | ||
|
||
# Dump the docker logs | ||
echo "socks5/test: BEGIN docker logs from $CONTAINER_NAME [{(" | ||
docker logs --tail all $CONTAINER_NAME | ||
echo ")}] END docker logs from $CONTAINER_NAME" | ||
|
||
# TODO: If there are any other relevant log files, dump those to stdout here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package modules | ||
|
||
import "github.com/zmap/zgrab2/modules/socks5" | ||
|
||
func init() { | ||
socks5.RegisterModule() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
// Package socks5 contains the zgrab2 Module implementation for SOCKS5. | ||
package socks5 | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
|
||
log "github.com/sirupsen/logrus" | ||
"github.com/zmap/zgrab2" | ||
) | ||
|
||
// ScanResults is the output of the scan. | ||
type ScanResults struct { | ||
Version string `json:"version,omitempty"` | ||
MethodSelection string `json:"method_selection,omitempty"` | ||
ConnectionResponse string `json:"connection_response,omitempty"` | ||
ConnectionResponseExplanation map[string]string `json:"connection_response_explanation,omitempty"` | ||
} | ||
|
||
// Flags are the SOCKS5-specific command-line flags. | ||
type Flags struct { | ||
zgrab2.BaseFlags | ||
Verbose bool `long:"verbose" description:"More verbose logging, include debug fields in the scan results"` | ||
} | ||
|
||
// Module implements the zgrab2.Module interface. | ||
type Module struct { | ||
} | ||
|
||
// Scanner implements the zgrab2.Scanner interface, and holds the state | ||
// for a single scan. | ||
type Scanner struct { | ||
config *Flags | ||
} | ||
|
||
// Connection holds the state for a single connection to the SOCKS5 server. | ||
type Connection struct { | ||
buffer [10000]byte | ||
config *Flags | ||
results ScanResults | ||
conn net.Conn | ||
} | ||
|
||
// RegisterModule registers the socks5 zgrab2 module. | ||
func RegisterModule() { | ||
var module Module | ||
_, err := zgrab2.AddCommand("socks5", "SOCKS5", module.Description(), 1080, &module) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
|
||
// NewFlags returns the default flags object to be filled in with the | ||
// command-line arguments. | ||
func (m *Module) NewFlags() interface{} { | ||
return new(Flags) | ||
} | ||
|
||
// NewScanner returns a new Scanner instance. | ||
func (m *Module) NewScanner() zgrab2.Scanner { | ||
return new(Scanner) | ||
} | ||
|
||
// Description returns an overview of this module. | ||
func (m *Module) Description() string { | ||
return "Perform a SOCKS5 scan" | ||
} | ||
|
||
// Validate flags | ||
func (f *Flags) Validate(args []string) (err error) { | ||
return | ||
} | ||
|
||
// Help returns this module's help string. | ||
func (f *Flags) Help() string { | ||
return "" | ||
} | ||
|
||
// Protocol returns the protocol identifier for the scanner. | ||
func (s *Scanner) Protocol() string { | ||
return "socks5" | ||
} | ||
|
||
// Init initializes the Scanner instance with the flags from the command line. | ||
func (s *Scanner) Init(flags zgrab2.ScanFlags) error { | ||
f, _ := flags.(*Flags) | ||
s.config = f | ||
return nil | ||
} | ||
|
||
// InitPerSender does nothing in this module. | ||
func (s *Scanner) InitPerSender(senderID int) error { | ||
return nil | ||
} | ||
|
||
// GetName returns the configured name for the Scanner. | ||
func (s *Scanner) GetName() string { | ||
return s.config.Name | ||
} | ||
|
||
// GetTrigger returns the Trigger defined in the Flags. | ||
func (scanner *Scanner) GetTrigger() string { | ||
return scanner.config.Trigger | ||
} | ||
|
||
// readResponse reads a response from the SOCKS5 server. | ||
func (conn *Connection) readResponse(expectedLength int) ([]byte, error) { | ||
resp := make([]byte, expectedLength) | ||
_, err := conn.conn.Read(resp) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return resp, nil | ||
} | ||
|
||
// sendCommand sends a command to the SOCKS5 server. | ||
func (conn *Connection) sendCommand(cmd []byte) error { | ||
_, err := conn.conn.Write(cmd) | ||
return err | ||
} | ||
|
||
// explainResponse converts the raw response into a human-readable explanation. | ||
func explainResponse(resp []byte) map[string]string { | ||
if len(resp) < 10 { | ||
return map[string]string{"error": "response too short"} | ||
} | ||
|
||
return map[string]string{ | ||
"Version": fmt.Sprintf("0x%02x (SOCKS Version 5)", resp[0]), | ||
"Reply": fmt.Sprintf("0x%02x (%s)", resp[1], getReplyDescription(resp[1])), | ||
"Reserved": fmt.Sprintf("0x%02x", resp[2]), | ||
"Address Type": fmt.Sprintf("0x%02x (%s)", resp[3], getAddressTypeDescription(resp[3])), | ||
"Bound Address": fmt.Sprintf("%d.%d.%d.%d", resp[4], resp[5], resp[6], resp[7]), | ||
"Bound Port": fmt.Sprintf("%d", int(resp[8])<<8|int(resp[9])), | ||
} | ||
} | ||
|
||
func getReplyDescription(code byte) string { | ||
switch code { | ||
case 0x00: | ||
return "succeeded" | ||
case 0x01: | ||
return "general SOCKS server failure" | ||
case 0x02: | ||
return "connection not allowed by ruleset" | ||
case 0x03: | ||
return "network unreachable" | ||
case 0x04: | ||
return "host unreachable" | ||
case 0x05: | ||
return "connection refused" | ||
case 0x06: | ||
return "TTL expired" | ||
case 0x07: | ||
return "command not supported" | ||
case 0x08: | ||
return "address type not supported" | ||
default: | ||
return "unassigned" | ||
} | ||
} | ||
|
||
func getAddressTypeDescription(code byte) string { | ||
switch code { | ||
case 0x01: | ||
return "IPv4 address" | ||
case 0x03: | ||
return "Domain name" | ||
case 0x04: | ||
return "IPv6 address" | ||
default: | ||
return "unknown" | ||
} | ||
} | ||
|
||
// PerformHandshake performs the SOCKS5 handshake. | ||
func (conn *Connection) PerformHandshake() (bool, error) { | ||
// Send version identifier/method selection message | ||
verMethodSel := []byte{0x05, 0x01, 0x00} // VER = 0x05, NMETHODS = 1, METHODS = 0x00 (NO AUTHENTICATION REQUIRED) | ||
err := conn.sendCommand(verMethodSel) | ||
if err != nil { | ||
return false, fmt.Errorf("error sending version identifier/method selection: %w", err) | ||
} | ||
conn.results.Version = "0x05" | ||
|
||
// Read method selection response | ||
methodSelResp, err := conn.readResponse(2) | ||
if err != nil { | ||
return false, fmt.Errorf("error reading method selection response: %w", err) | ||
} | ||
conn.results.MethodSelection = fmt.Sprintf("%x", methodSelResp) | ||
|
||
if methodSelResp[1] == 0xFF { | ||
return true, fmt.Errorf("no acceptable authentication methods") | ||
} | ||
|
||
return false, nil | ||
} | ||
|
||
// PerformConnectionRequest sends a connection request to the SOCKS5 server. | ||
func (conn *Connection) PerformConnectionRequest() error { | ||
// Send a connection request | ||
req := []byte{0x05, 0x01, 0x00, 0x01, 0xA6, 0x6F, 0x04, 0x64, 0x00, 0x50} // VER = 0x05, CMD = CONNECT, RSV = 0x00, ATYP = IPv4, DST.ADDR = 166.111.4.100, DST.PORT = 80 | ||
err := conn.sendCommand(req) | ||
if err != nil { | ||
return fmt.Errorf("error sending connection request: %w", err) | ||
} | ||
|
||
// Read connection response | ||
resp, err := conn.readResponse(10) | ||
if err != nil { | ||
return fmt.Errorf("error reading connection response: %w", err) | ||
} | ||
conn.results.ConnectionResponse = fmt.Sprintf("%x", resp) | ||
conn.results.ConnectionResponseExplanation = explainResponse(resp) | ||
|
||
if resp[1] > 0x80 { | ||
return fmt.Errorf("connection request failed with response: %x", resp) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Scan performs the configured scan on the SOCKS5 server. | ||
func (s *Scanner) Scan(t zgrab2.ScanTarget) (status zgrab2.ScanStatus, result interface{}, thrown error) { | ||
var err error | ||
var have_auth bool | ||
conn, err := t.Open(&s.config.BaseFlags) | ||
if err != nil { | ||
return zgrab2.TryGetScanStatus(err), nil, fmt.Errorf("error opening connection: %w", err) | ||
} | ||
cn := conn | ||
defer func() { | ||
cn.Close() | ||
}() | ||
|
||
results := ScanResults{} | ||
socks5Conn := Connection{conn: cn, config: s.config, results: results} | ||
|
||
have_auth, err = socks5Conn.PerformHandshake() | ||
if err != nil { | ||
if have_auth { | ||
return zgrab2.SCAN_SUCCESS, &socks5Conn.results, nil | ||
} else { | ||
return zgrab2.TryGetScanStatus(err), &socks5Conn.results, fmt.Errorf("error during handshake: %w", err) | ||
} | ||
} | ||
|
||
err = socks5Conn.PerformConnectionRequest() | ||
if err != nil { | ||
return zgrab2.TryGetScanStatus(err), &socks5Conn.results, fmt.Errorf("error during connection request: %w", err) | ||
} | ||
|
||
return zgrab2.SCAN_SUCCESS, &socks5Conn.results, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# zschema sub-schema for zgrab2's Socks5 module | ||
# Registers zgrab2-socks5 globally, and socks5 with the main zgrab2 schema. | ||
from zschema.leaves import * | ||
from zschema.compounds import * | ||
import zschema.registry | ||
|
||
from . import zgrab2 | ||
|
||
# Schema for ScanResults struct | ||
socks5_response_explanation = SubRecord( | ||
{ | ||
"Version": String(), | ||
"Reply": String(), | ||
"Reserved": String(), | ||
"Address Type": String(), | ||
"Bound Address": String(), | ||
"Bound Port": String(), | ||
} | ||
) | ||
|
||
socks5_scan_response = SubRecord( | ||
{ | ||
"version": String(), | ||
"method_selection": String(), | ||
"connection_response": String(), | ||
"connection_response_explanation": socks5_response_explanation, | ||
} | ||
) | ||
|
||
socks5_scan = SubRecord( | ||
{ | ||
"result": socks5_scan_response, | ||
}, | ||
extends=zgrab2.base_scan_response, | ||
) | ||
|
||
zschema.registry.register_schema("zgrab2-socks5", socks5_scan) | ||
zgrab2.register_scan_response_type("socks5", socks5_scan) |