From ffaeeab0f124b2b8a3bda5c44f6a301beeafa185 Mon Sep 17 00:00:00 2001 From: Clayton Zimmerman Date: Tue, 12 Jun 2018 09:27:45 -0400 Subject: [PATCH] Cleans up TODOs and includes more results to collect from scans. --- modules/ipp/ipp.go | 1 + modules/ipp/scanner.go | 84 +++++++++++++++++++++--------------- zgrab2_schemas/zgrab2/ipp.py | 6 ++- 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/modules/ipp/ipp.go b/modules/ipp/ipp.go index 7d43015..2be4b84 100644 --- a/modules/ipp/ipp.go +++ b/modules/ipp/ipp.go @@ -42,6 +42,7 @@ func attributeByteString(syntaxTag byte, name string, value string) []byte { return b } +//TODO: Dynamically create nothing except uri? //Construct a minimal request that an IPP server will respond to func getPrinterAttributesRequest(uri string) bytes.Buffer { var b bytes.Buffer diff --git a/modules/ipp/scanner.go b/modules/ipp/scanner.go index 9dc3ef6..9c4525f 100644 --- a/modules/ipp/scanner.go +++ b/modules/ipp/scanner.go @@ -7,9 +7,11 @@ import ( //"bytes" "encoding/binary" //"errors" - "io" + //"fmt" + //"io" "net/http" "strconv" + "strings" //"net" //"net/url" //"time" @@ -18,15 +20,20 @@ import ( "github.com/zmap/zgrab2" ) +const ( + CONTENT_TYPE string = "application/ipp" +) + //TODO: Tag relevant results and exlain in comments // ScanResults instances are returned by the module's Scan function. type ScanResults struct { - Response *http.Response `json:"response,omitempty"` + //TODO: Include a full response or at least a blob in the data + //Response *http.Response `json:"response,omitempty"` - MajorVersion int8 `json:"major_version"` - MinorVersion int8 `json:"minor_version"` + MajorVersion int8 `json:"version_major"` + MinorVersion int8 `json:"version_minor"` - Version string `json:"version_string,omitempty"` + VersionString string `json:"version_string,omitempty"` CUPSVersion string `json:"cups_version,omitempty"` //TODO: Uncomment this when implementing the TLS version of things @@ -34,35 +41,30 @@ type ScanResults struct { // TLSLog *zgrab2.TLSLog `json:"tls,omitempty"` } -//FIXME: We don't need this. -func readResultsFromResponseBody(body *io.ReadCloser) *ScanResults { - return &ScanResults{} -} - -// TODO: Add more protocol-specific flags +// TODO: Annotate every flag thoroughly +// TODO: Add more protocol-specific flags as needed // Flags holds the command-line configuration for the ipp scan module. // Populated by the framework. type Flags struct { zgrab2.BaseFlags //FIXME: Borrowed from http module - MaxSize int `long:"max-size" default:"256" description:"Max kilobytes to read in response to an HTTP request"` - //TODO: Include once TLS is implemented - // Protocols that support TLS should include zgrab2.TLSFlags + MaxRead int `long:"max-size" default:"256" description:"Max kilobytes to read in response to an HTTP request"` + // TODO: Protocols that support TLS should include zgrab2.TLSFlags (do once implemented) + // TODO: Maybe implement both an ipps connection and upgrade to https + IPPSecure bool `long:"ipps" description:"Perform a TLS handshake immediately upon connecting."` Verbose bool `long:"verbose" description:"More verbose logging, include debug fields in the scan results"` } -//TODO: Figure out what moduel-global state may be necessary // Module implements the zgrab2.Module interface. type Module struct { - // TODO: Add any module-global state + // TODO: Add any module-global state if necessary } -//TODO: Figure out what scan state may be necessary // Scanner implements the zgrab2.Scanner interface. type Scanner struct { config *Flags - // TODO: Add scan state + // TODO: Add scan state if any is necessary } // RegisterModule registers the zgrab2 module. @@ -97,7 +99,6 @@ func (flags *Flags) Help() string { return "" } -//TODO: Implement // Init initializes the Scanner. func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error { f, _ := flags.(*Flags) @@ -127,7 +128,6 @@ func (scanner *Scanner) GetPort() uint { } //FIXME: Maybe switch to ipp/ipps schemes, at least optionally -//FIXME: Stolen from http module, which isn't a good practice func getIPPURL(https bool, host string, port uint16, endpoint string) string { var proto string if https { @@ -138,6 +138,15 @@ func getIPPURL(https bool, host string, port uint16, endpoint string) string { return proto + "://" + host + ":" + strconv.FormatUint(uint64(port), 10) + endpoint } +func ippInContentType(resp http.Response) bool { + for _, t := range resp.Header["Content-Type"] { + if strings.Contains(t, CONTENT_TYPE) { + return true + } + } + return false +} + //TODO: Doesn't support TLS at all right now func (scanner *Scanner) grab(target zgrab2.ScanTarget) (int8, int8, *zgrab2.ScanError) { //FIXME: This is not where this hostname assignment logic should live @@ -145,25 +154,31 @@ func (scanner *Scanner) grab(target zgrab2.ScanTarget) (int8, int8, *zgrab2.Scan if host == "" { host = target.IP.String() } - //TODO: Make https bool depend on scanner's config - //TODO: ?Shouldn't put any endpoint, since we get the same response w/o on CUPS?? - uri := getIPPURL(false, host, uint16(scanner.config.BaseFlags.Port), "/ipp") + //FIXME: ?Should just use endpoint "/", since we get the same response as "/ipp" on CUPS?? + uri := getIPPURL(scanner.config.IPPSecure, host, uint16(scanner.config.BaseFlags.Port), "/ipp") b := getPrinterAttributesRequest(uri) - resp, err := http.Post(uri, "application/ipp", &b) + resp, err := http.Post(uri, CONTENT_TYPE, &b) if err != nil { - //FIXME: Create a descriptive error + //TODO: Create a descriptive error return 0, 0, zgrab2.NewScanError(zgrab2.SCAN_UNKNOWN_ERROR, err) } if resp != nil && resp.Body != nil { defer resp.Body.Close() } else { - //FIXME: Determine whether we need this error to avoid reading from Body + //TODO: Determine whether we need this error to avoid reading from Body return 0, 0, zgrab2.NewScanError(zgrab2.SCAN_UNKNOWN_ERROR, err) } //FIXME: Maybe add something to handle redirects - //FIXME: Probably return the whole response for further inspection, rather - // than grabbing first 2 bytes. In that case, probs instate maxRead like http - //FIXME: Check to make sure that the response is actually IPP + //FIXME: Probably return the whole response for further inspection by ztag, rather + // than grabbing first 2 bytes. In that case, implement maxRead like http module + + //Check to make sure that the repsonse received is actually IPP + //Content-Type header matches is sufficient + //HTTP on port 631 is sufficient + //Still record data in the case of protocol error to see what that data looks like + + //TODO: Record server-header version numbers + //protocols := resp.Header["Server"]) var version int16 if err := binary.Read(resp.Body, binary.BigEndian, &version); err != nil { return 0, 0, zgrab2.NewScanError(zgrab2.SCAN_UNKNOWN_ERROR, err) @@ -172,17 +187,16 @@ func (scanner *Scanner) grab(target zgrab2.ScanTarget) (int8, int8, *zgrab2.Scan } // Scan TODO: describe how scan operates -//1. FIXME: Don't open connection, because we don't need it? -//2. Send something (currently get-printer-attributes) -//3. Take in that response & read out version numbers +//1. Send a request (currently get-printer-attributes) +//2. Take in that response & read out version numbers func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) { - // TODO: implement + // TODO: use Connection again, at least when implementing TLS major, minor, err := scanner.grab(target) //FIXME: Triggering even though error IS nil - //FIXME: This is a sloppy bodge to handle the issue, since you must know implementation details below you + //FIXME: This is a sloppy bodge to handle the issue if major == 0 && minor == 0 && err != nil { //TODO: Consider mimicking HTTP Scan's retryHTTPS functionality - //TODO: Create relevant error, or send something more descriptive? + //TODO: Create more detailed error message? return zgrab2.TryGetScanStatus(err), nil, err } results := &ScanResults{} diff --git a/zgrab2_schemas/zgrab2/ipp.py b/zgrab2_schemas/zgrab2/ipp.py index 347ce8e..d7b2718 100644 --- a/zgrab2_schemas/zgrab2/ipp.py +++ b/zgrab2_schemas/zgrab2/ipp.py @@ -9,7 +9,11 @@ import zgrab2 ipp_scan_response = SubRecord({ "result": SubRecord({ - "test_key": String(doc="FIXME: Remove this") + "version_major": Signed8BitInteger(), + "version_minor": Signed8BitInteger(), + "version_string": String(), + "cups_version": String(), + #"tls": zgrab2.tls_log, }) }, extends=zgrab2.base_scan_response)