zgrab2/modules/oracle/scanner.go

272 lines
9.8 KiB
Go

// Package oracle provides the zgrab2 scanner module for Oracle's TNS protocol.
// Default Port: 1521 (TCP)
//
// The scan does the first part of a TNS handshake, prior to the point where
// any actual authentication is required; the happy case goes
// 1. client-to-server: Connect(--client-version, --min-server-version, --connect-descriptor)
// 2. server-to-client: Resend
// 3. client-to-server: Connect(exact same data)
// 4. server-to-client: Accept(server_version)
// 5. client-to-server: Data: Native Service Negotiation
// 6. server-to-client: Data: Native Service Negotiation(component release versions)
//
// The default scan uses a generic connect descriptor with no explicit connect
// data / service name, so it relies on the server to choose the destination.
//
// Sending an intentionally invalid --connect-descriptor can force a Refuse
// response, which should include a version number.
//
// The output includes the server's protocol version and any component release
// versions that are returned.
package oracle
import (
"fmt"
"strconv"
log "github.com/sirupsen/logrus"
"github.com/zmap/zgrab2"
)
// ScanResults instances are returned by the module's Scan function.
type ScanResults struct {
// Handshake is the log of the TNS handshake between client and server.
Handshake *HandshakeLog `json:"handshake,omitempty"`
// TLSLog contains the log of the TLS handshake (and any additional
// configured TLS scan operations).
TLSLog *zgrab2.TLSLog `json:"tls,omitempty"`
}
// Flags holds the command-line configuration for the HTTP scan module.
// Populated by the framework.
type Flags struct {
zgrab2.BaseFlags
zgrab2.TLSFlags
// Version is the client version number sent to the server in the Connect
// packet. TODO: Find version number mappings.
Version uint16 `long:"client-version" description:"The client version number to send." default:"312"`
// MinVersion is the minimum protocol version that the client claims support
// for in the Connect packet. Same format as Version above.
MinVersion uint16 `long:"min-server-version" description:"The minimum supported client version to send in the connect packet." default:"300"`
// ReleaseVersion is the five-component dotted-decimal release version
// string the client should send during native Native Security Negotiation.
ReleaseVersion string `long:"release-version" description:"The dotted-decimal release version used during the NSN negoatiation. Must contain five components (e.g. 1.2.3.4.5)." default:"11.2.0.4.0"`
// GlobalServiceOptions sets the ServiceOptions flags the client will send
// to the server in the Connect packet. 16 bits.
GlobalServiceOptions string `long:"global-service-options" description:"The Global Service Options flags to send in the connect packet." default:"0x0C41"`
// SDU sets the requested Session Data Unit size value the client sends in
// the Connect packet. 16 bits.
SDU string `long:"sdu" description:"The SDU value to send in the connect packet." default:"0x2000"`
// TDU sets the request Transport Data Unit size value the client sends in
// the Connect packet. 16 bits.
TDU string `long:"tdu" description:"The TDU value to send in the connect packet." default:"0xFFFF"`
// ProtocolCharacteristics sets the protocol characteristics flags the
// client sends to the server in the Connect packet. 16 bits.
ProtocolCharacterisics string `long:"protocol-characteristics" description:"The Protocol Characteristics flags to send in the connect packet." default:"0x7F08"`
// ConnectFlags sets the connect flags the client sends to the server in the
// Connect packet. The upper 16 bits give the first byte, the lower 16 bits
// the second byte.
ConnectFlags string `long:"connect-flags" description:"The connect flags for the connect packet." default:"0x4141"`
// ConnectDescriptor sets the connect descriptor the client sends in the
// data payload of the Connect packet.
// See https://docs.oracle.com/cd/E11882_01/network.112/e41945/glossary.htm#BGBEAGEA
ConnectDescriptor string `long:"connect-descriptor" description:"The connect descriptor to use in the connect packet."`
// TCPS determines whether the connection starts with a TLS handshake.
TCPS bool `long:"tcps" description:"Wrap the connection with a TLS handshake."`
// NewTNS causes the client to use the newer TNS header format with 32-bit
// lengths.
NewTNS bool `long:"new-tns" description:"If set, use new-style TNS headers"`
// Verbose causes more verbose logging, and includes debug fields inthe scan
// results.
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.
type Scanner struct {
config *Flags
}
// RegisterModule registers the zgrab2 module.
func RegisterModule() {
var module Module
_, err := zgrab2.AddCommand("oracle", "oracle", "Probe for oracle", 1521, &module)
if err != nil {
log.Fatal(err)
}
}
// NewFlags returns a default Flags object.
func (module *Module) NewFlags() interface{} {
return new(Flags)
}
// NewScanner returns a new Scanner instance.
func (module *Module) NewScanner() zgrab2.Scanner {
return new(Scanner)
}
// Validate checks that the flags are valid.
// On success, returns nil.
// On failure, returns an error instance describing the error.
func (flags *Flags) Validate(args []string) error {
u16Strings := map[string]string{
"global-service-options": flags.GlobalServiceOptions,
"protocol-characteristics": flags.ProtocolCharacterisics,
"connect-flags": flags.ConnectFlags,
"sdu": flags.SDU,
"tdu": flags.TDU,
}
for name, value := range u16Strings {
v, err := strconv.ParseUint(value, 0, 32)
if err != nil {
return fmt.Errorf("%s: %s is not a valid 16-bit integer: %v", name, value, err)
}
if v > 0xffff {
return fmt.Errorf("%s: %s is larger than 16 bits", name, value)
}
}
if _, err := EncodeReleaseVersion(flags.ReleaseVersion); err != nil {
return fmt.Errorf("release-version: %s is not a valid five-component dotted-decimal number", flags.ReleaseVersion)
}
return nil
}
// Help returns the module's help string.
func (flags *Flags) Help() string {
return ""
}
// Init initializes the Scanner.
func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error {
f, _ := flags.(*Flags)
scanner.config = f
if f.Verbose {
log.SetLevel(log.DebugLevel)
}
return nil
}
// InitPerSender initializes the scanner for a given sender.
func (scanner *Scanner) InitPerSender(senderID int) error {
return nil
}
// GetName returns the Scanner name defined in the Flags.
func (scanner *Scanner) GetName() string {
return scanner.config.Name
}
// GetTrigger returns the Trigger defined in the Flags.
func (scanner *Scanner) GetTrigger() string {
return scanner.config.Trigger
}
// Protocol returns the protocol identifier of the scan.
func (scanner *Scanner) Protocol() string {
return "oracle"
}
func (scanner *Scanner) getTNSDriver() *TNSDriver {
mode := TNSModeOld
if scanner.config.NewTNS {
mode = TNSMode12c
}
return &TNSDriver{Mode: mode}
}
// Scan does the following:
// 1. Make a TCP connection to the target
// 2. If --tcps is set, do a TLS handshake and use the wrapped socket in future
// calls.
// 3. Instantiate the TNS driver (TNSMode12c if --new-tns is set, otherwise
// TNSModeOld)
// 4. Send the Connect packet to the server with the provided options and
// connect descriptor
// 5. If the server responds with a valid TNS packet, an Oracle server has been
// detected. If not, fail.
// 6. If the response is...
// a. ...a Resend packet, then set result.DidResend and re-send the packet.
// b. ...a Refused packet, then set the result.RefuseReason and RefuseError,
// then exit.
// c. ...a Redirect packet, then set result.RedirectTarget and exit.
// d. ...an Accept packet, go to 7
// e. ...anything else: exit with SCAN_APPLICATION_ERROR
// 7. Pull the server protocol version and other flags from the Accept packet
// into the results, then send a Native Security Negotiation Data packet.
// 8. If the response is not a Data packet, exit with SCAN_APPLICATION_ERROR.
// 9. Pull the versions out of the response and exit with SCAN_SUCCESS.
func (scanner *Scanner) Scan(t zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) {
var results *ScanResults
sock, err := t.Open(&scanner.config.BaseFlags)
if err != nil {
return zgrab2.TryGetScanStatus(err), nil, err
}
if scanner.config.TCPS {
tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(sock)
if err != nil {
// GetTLSConnection can only fail if the input flags are bad
panic(err)
}
results = new(ScanResults)
results.TLSLog = tlsConn.GetLog()
err = tlsConn.Handshake()
if err != nil {
return zgrab2.TryGetScanStatus(err), nil, err
}
sock = tlsConn
}
conn := Connection{
conn: sock,
scanner: scanner,
target: &t,
tnsDriver: scanner.getTNSDriver(),
}
connectDescriptor := scanner.config.ConnectDescriptor
if connectDescriptor == "" {
// In local testing, omitting the SERVICE_NAME allowed the server to
// choose an appropriate default. CID.PROGRAM added strictly for logging
// purposes.
connectDescriptor = "(DESCRIPTION=(CONNECT_DATA=(CID=(PROGRAM=zgrab2))))"
}
handshakeLog, err := conn.Connect(connectDescriptor)
if handshakeLog != nil {
// Ensure that any handshake logs, even if incomplete, get returned.
if results == nil {
// If the results were not created previously to store the TLS log,
// create it now
results = new(ScanResults)
}
results.Handshake = handshakeLog
}
if err != nil {
switch err {
case ErrUnexpectedResponse:
return zgrab2.SCAN_APPLICATION_ERROR, results, err
default:
return zgrab2.TryGetScanStatus(err), results, err
}
}
return zgrab2.SCAN_SUCCESS, results, nil
}