Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for PPTP #470

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions integration_tests/pptp/chap-secrets
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Secrets for authentication using PAP
# client server secret acceptable local IP addresses
username * password *
9 changes: 9 additions & 0 deletions integration_tests/pptp/cleanup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash

set +e

echo "pptp/cleanup: Tests cleanup for pptp"

CONTAINER_NAME=zgrab_pptp

docker stop $CONTAINER_NAME
26 changes: 26 additions & 0 deletions integration_tests/pptp/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash

echo "pptp/setup: Tests setup for pptp"

CONTAINER_TAG="mobtitude/vpn-pptp"
CONTAINER_NAME="zgrab_pptp"

# If the container is already running, use it.
if docker ps --filter "name=$CONTAINER_NAME" | grep -q $CONTAINER_NAME; then
echo "pptp/setup: Container $CONTAINER_NAME already running -- nothing to setup"
exit 0
fi

DOCKER_RUN_FLAGS="--rm --privileged --name $CONTAINER_NAME -td -v ./chap-secrets:/etc/ppp/chap-secrets"

# If it is not running, try launching it -- on success, use that.
echo "pptp/setup: Trying to launch $CONTAINER_NAME..."
if ! docker run $DOCKER_RUN_FLAGS $CONTAINER_TAG; then
echo "failed"
# echo "pptp/setup: Building docker image $CONTAINER_TAG..."
# # If it fails, build it from ./container/Dockerfile
# docker build -t $CONTAINER_TAG ./container
# # Try again
# echo "pptp/setup: Launching $CONTAINER_NAME..."
# docker run $DOCKER_RUN_FLAGS $CONTAINER_TAG
fi
23 changes: 23 additions & 0 deletions integration_tests/pptp/test.sh
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/pptp

CONTAINER_NAME=zgrab_pptp

OUTPUT_FILE=$ZGRAB_OUTPUT/pptp/pptp.json

echo "pptp/test: Tests runner for pptp"
# TODO FIXME: Add any necessary flags or additional tests
CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh pptp > $OUTPUT_FILE

# Dump the docker logs
echo "pptp/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.
7 changes: 7 additions & 0 deletions modules/pptp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package modules

import "github.com/zmap/zgrab2/modules/pptp"

func init() {
pptp.RegisterModule()
}
179 changes: 179 additions & 0 deletions modules/pptp/scanner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// Package pptp contains the zgrab2 Module implementation for PPTP.
package pptp

import (
"encoding/binary"
"fmt"
"net"
"time"

log "github.com/sirupsen/logrus"
"github.com/zmap/zgrab2"
)

// ScanResults is the output of the scan.
type ScanResults struct {
// Banner is the initial data banner sent by the server.
Banner string `json:"banner,omitempty"`

// ControlMessage is the received PPTP control message.
ControlMessage string `json:"control_message,omitempty"`
}

// Flags are the PPTP-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
}

// RegisterModule registers the pptp zgrab2 module.
func RegisterModule() {
var module Module
_, err := zgrab2.AddCommand("pptp", "PPTP", module.Description(), 1723, &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 "Scan for PPTP"
}

// 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 "pptp"
}

// 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
}

// PPTP Start-Control-Connection-Request message constants
const (
PPTP_MAGIC_COOKIE = 0x1A2B3C4D
PPTP_CONTROL_MESSAGE = 1
PPTP_START_CONN_REQUEST = 1
PPTP_PROTOCOL_VERSION = 0x0100 // Split into two 16-bit values for binary.BigEndian.PutUint16
)

// Connection holds the state for a single connection to the PPTP server.
type Connection struct {
config *Flags
results ScanResults
conn net.Conn
}

// Create the Start-Control-Connection-Request message
func createSCCRMessage() []byte {
message := make([]byte, 156)
binary.BigEndian.PutUint16(message[0:2], 156) // Length
binary.BigEndian.PutUint16(message[2:4], PPTP_CONTROL_MESSAGE) // PPTP Message Type
binary.BigEndian.PutUint32(message[4:8], PPTP_MAGIC_COOKIE) // Magic Cookie
binary.BigEndian.PutUint16(message[8:10], PPTP_START_CONN_REQUEST) // Control Message Type
binary.BigEndian.PutUint16(message[10:12], uint16(PPTP_PROTOCOL_VERSION>>16)) // Protocol Version (high 16 bits)
binary.BigEndian.PutUint16(message[12:14], uint16(PPTP_PROTOCOL_VERSION&0xFFFF)) // Protocol Version (low 16 bits)
binary.BigEndian.PutUint32(message[14:18], 0) // Framing Capabilities
binary.BigEndian.PutUint32(message[18:22], 0) // Bearer Capabilities
binary.BigEndian.PutUint16(message[22:24], 0) // Maximum Channels
binary.BigEndian.PutUint16(message[24:26], 0) // Firmware Revision
copy(message[26:90], "ZGRAB2-SCANNER") // Host Name
copy(message[90:], "ZGRAB2") // Vendor Name
return message
}

// Read response from the PPTP server
func (pptp *Connection) readResponse() (string, error) {
buffer := make([]byte, 1024)
pptp.conn.SetReadDeadline(time.Now().Add(5 * time.Second))
n, err := pptp.conn.Read(buffer)
if err != nil {
return "", err
}
return string(buffer[:n]), nil
}

// Scan performs the configured scan on the PPTP server
func (s *Scanner) Scan(t zgrab2.ScanTarget) (status zgrab2.ScanStatus, result interface{}, thrown error) {
var err error
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{}

pptp := Connection{conn: cn, config: s.config, results: results}

// Send Start-Control-Connection-Request message
request := createSCCRMessage()
_, err = pptp.conn.Write(request)
if err != nil {
return zgrab2.TryGetScanStatus(err), &pptp.results, fmt.Errorf("error sending PPTP SCCR message: %w", err)
}

// Read the response
response, err := pptp.readResponse()
if err != nil {
return zgrab2.TryGetScanStatus(err), &pptp.results, fmt.Errorf("error reading PPTP response: %w", err)
}

// Store the banner and control message
pptp.results.Banner = string(request)
pptp.results.ControlMessage = response

return zgrab2.SCAN_SUCCESS, &pptp.results, nil
}
1 change: 1 addition & 0 deletions zgrab2_schemas/zgrab2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
from . import ipp
from . import banner
from . import amqp091
from . import pptp
20 changes: 20 additions & 0 deletions zgrab2_schemas/zgrab2/pptp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# zschema sub-schema for zgrab2's PPTP module
# Registers zgrab2-pptp globally, and pptp with the main zgrab2 schema.
from zschema.leaves import *
from zschema.compounds import *
import zschema.registry

from . import zgrab2

# Schema for ScanResults struct
pptp_scan_response = SubRecord({
"banner": String(),
"control_message": String(),
})

pptp_scan = SubRecord({
"result": pptp_scan_response,
}, extends=zgrab2.base_scan_response)

zschema.registry.register_schema("zgrab2-pptp", pptp_scan)
zgrab2.register_scan_response_type("pptp", pptp_scan)