├── zgrab2_schemas ├── __init__.py ├── zgrab2 │ ├── testdata │ │ ├── pop3-banner.json │ │ ├── ftp-default.json │ │ ├── pop3-banner.quit.json │ │ ├── pop3-help.banner.quit.json │ │ ├── pop3-noop.help.banner.quit.json │ │ ├── smtp-00.json │ │ ├── smtp-ehlo.03.json │ │ ├── smtp-helo.01.json │ │ ├── smtp-helo.02.json │ │ ├── smtp-ehlo.04.json │ │ ├── redis-password-inline.json │ │ ├── redis-password-normal.json │ │ ├── smtp-ehlo.quit.05.json │ │ ├── redis-renamed-inline.json │ │ ├── redis-renamed-normal.json │ │ ├── redis-default-inline-mappings.json │ │ ├── redis-default-normal-mappings.json │ │ ├── redis-password-inline-mappings.json │ │ ├── redis-password-normal-mappings.json │ │ ├── smtp-help.quit.06.json │ │ ├── ntp-4.2.6_REQ_MON_GETLIST_solo.json │ │ ├── ntp-4.2.6_REQ_MON_GETLIST_1_solo.json │ │ ├── ntp-openntp.json │ │ ├── ntp-4.2.6_normal.json │ │ ├── telnet-telnet.json │ │ ├── postgres-9.3-nossl.json │ │ ├── postgres-9.4-nossl.json │ │ ├── postgres-9.5-nossl.json │ │ ├── redis-password-inline-extra.json │ │ ├── redis-password-normal-extra.json │ │ ├── postgres-10.1-nossl.json │ │ ├── postgres-9.6-nossl.json │ │ ├── redis-renamed-inline-extra.json │ │ ├── redis-renamed-normal-extra.json │ │ ├── http-http.json │ │ ├── ntp-4.2.6_monlist.json │ │ ├── ntp-4.2.6_REQ_MON_GETLIST.json │ │ ├── ntp-4.2.6_REQ_MON_GETLIST_1.json │ │ ├── mysql-5.5.json │ │ ├── mysql-5.6.json │ │ ├── memcached-1.6.0.json │ │ └── memcached-1.6.38.json │ ├── pptp.py │ ├── dnp3.py │ ├── __init__.py │ ├── mqtt.py │ ├── banner.py │ ├── ftp.py │ ├── smtp.py │ ├── imap.py │ ├── telnet.py │ ├── bacnet.py │ ├── socks5.py │ ├── pop3.py │ ├── siemens.py │ ├── fox.py │ ├── amqp091.py │ ├── managesieve.py │ ├── tests.py │ ├── modbus.py │ └── mssql.py └── README.md ├── lib ├── http │ ├── testdata │ │ ├── file │ │ ├── style.css │ │ └── index.html │ ├── transport_default_other.go │ ├── transport_default_wasm.go │ ├── cookiejar │ │ ├── dummy_publicsuffix_test.go │ │ └── example_test.go │ ├── method.go │ ├── example_handle_test.go │ ├── httptrace │ │ ├── example_test.go │ │ └── nettrace.go │ ├── h2_error.go │ ├── jar.go │ ├── roundtrip.go │ ├── h2_error_test.go │ ├── proxy_test.go │ ├── internal │ │ └── ascii │ │ │ └── print.go │ ├── httputil │ │ └── httputil.go │ └── mapping.go ├── smb │ ├── ntlmssp │ │ ├── crypto_test.go │ │ ├── crypto.go │ │ └── ntlmssp_test.go │ ├── smb │ │ └── encoder │ │ │ ├── encoder_test.go │ │ │ └── unicode.go │ ├── about.txt │ ├── .gitignore │ ├── gss │ │ └── oid.go │ ├── README.md │ └── LICENSE ├── ssh │ ├── test │ │ ├── doc.go │ │ └── banner_test.go │ ├── internal │ │ └── poly1305 │ │ │ ├── mac_noasm.go │ │ │ ├── bits_go1.13.go │ │ │ ├── bits_compat.go │ │ │ ├── sum_amd64.go │ │ │ └── sum_ppc64le.go │ ├── testdata │ │ └── doc.go │ ├── tcpip_test.go │ ├── doc.go │ ├── mac.go │ ├── log.go │ └── kex_test.go └── http2 │ ├── config_pre_go124.go │ ├── timer.go │ ├── errors_test.go │ ├── gotrack_test.go │ ├── internal │ └── httpcommon │ │ ├── httpcommon_test.go │ │ └── ascii.go │ ├── unencrypted.go │ ├── ascii.go │ ├── writesched_roundrobin_test.go │ └── writesched_random_test.go ├── modules ├── ipp │ ├── scanner_test.go │ └── ipp_test.go ├── fox.go ├── ftp.go ├── ipp.go ├── ntp.go ├── smb.go ├── dnp3.go ├── http.go ├── imap.go ├── jarm.go ├── mqtt.go ├── mssql.go ├── mysql.go ├── pop3.go ├── pptp.go ├── redis.go ├── smtp.go ├── amqp.go ├── bacnet.go ├── modbus.go ├── oracle.go ├── socks5.go ├── telnet.go ├── mongodb.go ├── postgres.go ├── siemens.go ├── managesieve.go ├── dnp3 │ └── log.go ├── banner.go ├── memcached.go ├── bacnet │ ├── objects_test.go │ ├── query.go │ └── common.go ├── memcached │ └── scanner_test.go ├── smtp │ ├── metadata.go │ ├── smtp.go │ └── scanner_test.go ├── siemens │ ├── common.go │ └── log.go ├── imap │ └── imap.go ├── pop3 │ └── pop3.go └── telnet │ └── log.go ├── integration_tests ├── redis │ └── container │ │ ├── default.conf │ │ ├── password.conf │ │ ├── Dockerfile │ │ ├── extra-commands.yaml │ │ ├── extra-commands.json │ │ ├── mappings.yaml │ │ ├── mappings.json │ │ └── renamed.conf ├── multiple │ ├── input.csv │ ├── multiple.ini │ ├── bad-input-file.ini │ └── docker-run.sh ├── http │ ├── http_smoke_test │ │ └── container │ │ │ ├── index-http.html │ │ │ ├── index-https.html │ │ │ ├── index-redirect.html │ │ │ ├── index-redirect-2.html │ │ │ ├── favicon.ico │ │ │ ├── Dockerfile │ │ │ └── lighttpd.conf │ ├── test.sh │ └── http_version_tests │ │ ├── http1 │ │ ├── index.html │ │ └── nginx.conf │ │ ├── http2-only-https │ │ ├── index.html │ │ ├── Dockerfile │ │ └── Caddyfile │ │ ├── http-both-versions-https │ │ ├── index.html │ │ ├── Dockerfile │ │ └── Caddyfile │ │ ├── h2c │ │ ├── go.mod │ │ ├── Dockerfile │ │ ├── go.sum │ │ └── main.go │ │ ├── docker-compose.yml │ │ └── README.md ├── telnet │ ├── container │ │ ├── inetd.conf │ │ ├── entrypoint.sh │ │ └── Dockerfile │ └── test.sh ├── socks5 │ ├── 3proxy.cfg │ └── test.sh ├── mqtt │ ├── mosquitto.conf │ ├── multiple.ini │ └── test.sh ├── pptp │ ├── chap-secrets │ └── test.sh ├── .template │ ├── module.go.tmpl │ ├── container │ │ ├── Dockerfile │ │ └── entrypoint.sh │ ├── schema.py │ ├── test.sh │ └── README.md ├── smtp │ ├── Dockerfile │ ├── ehlo-starttls │ │ └── Dockerfile │ ├── ehlo-no-starttls │ │ └── Dockerfile │ ├── helo │ │ └── Dockerfile │ └── smtps │ │ └── Dockerfile ├── pop3 │ ├── container │ │ ├── entrypoint.sh │ │ └── Dockerfile │ └── test.sh ├── ipp │ ├── Dockerfile │ ├── container-cups │ │ ├── entrypoint.sh │ │ └── Dockerfile │ └── container-cups-tls │ │ ├── entrypoint.sh │ │ └── Dockerfile ├── ftp │ ├── container │ │ ├── entrypoint.sh │ │ └── Dockerfile │ └── test.sh ├── postgres │ ├── container │ │ ├── postgresql.conf.nossl.partial │ │ ├── postgresql.conf.9.3.nossl.partial │ │ ├── setup_nossl.sh │ │ ├── postgresql.conf.9.3.ssl.partial │ │ ├── postgresql.conf.ssl.partial │ │ ├── Dockerfile.9.3 │ │ ├── setup_ssl.sh │ │ ├── Dockerfile │ │ └── README.md │ └── test.sh ├── ntp │ ├── container-openntp │ │ ├── Dockerfile │ │ └── ntpd.conf │ └── container-4.2.6 │ │ └── Dockerfile ├── mssql │ └── test.sh ├── ssh │ ├── container │ │ └── Dockerfile │ └── test.sh ├── mongodb │ └── test.sh ├── managesieve │ └── test.sh ├── mysql │ └── test.sh └── amqp091 │ └── test.sh ├── .gitattributes ├── .dockerignore ├── requirements.txt ├── multiple.ini ├── docker-runner ├── service-base.Dockerfile ├── docker-run.sh └── entrypoint.sh ├── bin ├── doc.go └── summary.go ├── cmd └── zgrab2 │ ├── mult.ini │ └── main.go ├── tools └── keys │ ├── testdata │ └── test1024dh.pem │ └── rsa.go ├── .gitignore ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ └── integration-test.yml ├── setup.py ├── Dockerfile ├── errors.go ├── multiple.go ├── module_set.go ├── .golangci.yml ├── go.mod ├── blocklist.go ├── ratelimit └── ratelimit.go └── output_test.go /zgrab2_schemas/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/http/testdata/file: -------------------------------------------------------------------------------- 1 | 0123456789 2 | -------------------------------------------------------------------------------- /lib/http/testdata/style.css: -------------------------------------------------------------------------------- 1 | body {} 2 | -------------------------------------------------------------------------------- /modules/ipp/scanner_test.go: -------------------------------------------------------------------------------- 1 | package ipp 2 | -------------------------------------------------------------------------------- /integration_tests/redis/container/default.conf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh eol=LF 2 | Makefile eol=LF 3 | -------------------------------------------------------------------------------- /lib/smb/ntlmssp/crypto_test.go: -------------------------------------------------------------------------------- 1 | package ntlmssp 2 | -------------------------------------------------------------------------------- /lib/http/testdata/index.html: -------------------------------------------------------------------------------- 1 | index.html says hello 2 | -------------------------------------------------------------------------------- /lib/smb/smb/encoder/encoder_test.go: -------------------------------------------------------------------------------- 1 | package encoder 2 | -------------------------------------------------------------------------------- /integration_tests/redis/container/password.conf: -------------------------------------------------------------------------------- 1 | requirepass password! 2 | -------------------------------------------------------------------------------- /integration_tests/multiple/input.csv: -------------------------------------------------------------------------------- 1 | , github.com, http80 2 | , time.nist.gov , ntp -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .github 3 | integration_tests 4 | zgrab2_schemas 5 | *.md 6 | zgrab2 -------------------------------------------------------------------------------- /integration_tests/http/http_smoke_test/container/index-http.html: -------------------------------------------------------------------------------- 1 | HTTP INDEX -------------------------------------------------------------------------------- /integration_tests/http/http_smoke_test/container/index-https.html: -------------------------------------------------------------------------------- 1 | HTTPS INDEX -------------------------------------------------------------------------------- /integration_tests/redis/container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM redis:7 2 | COPY *.conf /usr/local/etc/redis/ 3 | -------------------------------------------------------------------------------- /integration_tests/redis/container/extra-commands.yaml: -------------------------------------------------------------------------------- 1 | - PING 2 | - AUTH password! 3 | - YOUR-COMMAND args -------------------------------------------------------------------------------- /integration_tests/http/http_smoke_test/container/index-redirect.html: -------------------------------------------------------------------------------- 1 | HTTP REDIRECT INDEX -------------------------------------------------------------------------------- /integration_tests/http/http_smoke_test/container/index-redirect-2.html: -------------------------------------------------------------------------------- 1 | HTTP REDIRECT 2 INDEX -------------------------------------------------------------------------------- /integration_tests/telnet/container/inetd.conf: -------------------------------------------------------------------------------- 1 | telnet stream tcp nowait root /usr/sbin/tcpd /usr/sbin/telnetd 2 | -------------------------------------------------------------------------------- /integration_tests/redis/container/extra-commands.json: -------------------------------------------------------------------------------- 1 | [ 2 | "PING", 3 | "AUTH password!", 4 | "YOUR-COMMAND args" 5 | ] -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | zschema 2 | git+https://github.com/zmap/zcrypto@4f0ea0eaccacc4e153ddbb2016afe9d7bb961efd#egg=zcrypto_schemas 3 | requests -------------------------------------------------------------------------------- /integration_tests/redis/container/mappings.yaml: -------------------------------------------------------------------------------- 1 | PING: RENAMED-PING 2 | ECHO: RENAMED-ECHO 3 | AUTH: RENAMED-AUTH 4 | INFO: RENAMED-INFO 5 | -------------------------------------------------------------------------------- /modules/fox.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/fox" 4 | 5 | func init() { 6 | fox.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/ftp.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/ftp" 4 | 5 | func init() { 6 | ftp.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/ipp.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/ipp" 4 | 5 | func init() { 6 | ipp.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/ntp.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/ntp" 4 | 5 | func init() { 6 | ntp.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/smb.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/smb" 4 | 5 | func init() { 6 | smb.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/dnp3.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/dnp3" 4 | 5 | func init() { 6 | dnp3.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/http.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/http" 4 | 5 | func init() { 6 | http.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/imap.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/imap" 4 | 5 | func init() { 6 | imap.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/jarm.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/jarm" 4 | 5 | func init() { 6 | jarm.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/mqtt.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/mqtt" 4 | 5 | func init() { 6 | mqtt.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/mssql.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/mssql" 4 | 5 | func init() { 6 | mssql.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/mysql.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/mysql" 4 | 5 | func init() { 6 | mysql.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/pop3.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/pop3" 4 | 5 | func init() { 6 | pop3.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/pptp.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/pptp" 4 | 5 | func init() { 6 | pptp.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/redis.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/redis" 4 | 5 | func init() { 6 | redis.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/smtp.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/smtp" 4 | 5 | func init() { 6 | smtp.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /multiple.ini: -------------------------------------------------------------------------------- 1 | [ntp] 2 | trigger="ntp" 3 | name="ntp" 4 | port=123 5 | 6 | [http] 7 | trigger="http80" 8 | name="http80" 9 | port=80 10 | -------------------------------------------------------------------------------- /integration_tests/http/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | python3 ./http_version_tests/test.py 5 | python3 ./http_smoke_test/test.py 6 | -------------------------------------------------------------------------------- /modules/amqp.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/amqp091" 4 | 5 | func init() { 6 | amqp091.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/bacnet.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/bacnet" 4 | 5 | func init() { 6 | bacnet.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/modbus.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/modbus" 4 | 5 | func init() { 6 | modbus.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/oracle.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/oracle" 4 | 5 | func init() { 6 | oracle.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/socks5.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/socks5" 4 | 5 | func init() { 6 | socks5.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/telnet.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/telnet" 4 | 5 | func init() { 6 | telnet.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/mongodb.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/mongodb" 4 | 5 | func init() { 6 | mongodb.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/postgres.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/postgres" 4 | 5 | func init() { 6 | postgres.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/siemens.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/siemens" 4 | 5 | func init() { 6 | siemens.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /integration_tests/http/http_smoke_test/container/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zmap/zgrab2/HEAD/integration_tests/http/http_smoke_test/container/favicon.ico -------------------------------------------------------------------------------- /modules/managesieve.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/managesieve" 4 | 5 | func init() { 6 | managesieve.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /modules/dnp3/log.go: -------------------------------------------------------------------------------- 1 | package dnp3 2 | 3 | type DNP3Log struct { 4 | IsDNP3 bool `json:"is_dnp3"` 5 | RawResponse []byte `json:"raw_response,omitempty"` 6 | } 7 | -------------------------------------------------------------------------------- /integration_tests/socks5/3proxy.cfg: -------------------------------------------------------------------------------- 1 | internal 0.0.0.0 2 | external 0.0.0.0 3 | 4 | maxconn 10 5 | 6 | auth none 7 | 8 | socks -p1080 9 | 10 | allow * 11 | 12 | flush -------------------------------------------------------------------------------- /modules/banner.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "github.com/zmap/zgrab2/modules/banner" 5 | ) 6 | 7 | func init() { 8 | banner.RegisterModule() 9 | } 10 | -------------------------------------------------------------------------------- /integration_tests/redis/container/mappings.json: -------------------------------------------------------------------------------- 1 | { 2 | "PING": "RENAMED-PING", 3 | "ECHO": "RENAMED-ECHO", 4 | "AUTH": "RENAMED-AUTH", 5 | "INFO": "RENAMED-INFO" 6 | } -------------------------------------------------------------------------------- /modules/memcached.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "github.com/zmap/zgrab2/modules/memcached" 5 | ) 6 | 7 | func init() { 8 | memcached.RegisterModule() 9 | } 10 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/http1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Hello from HTTP/1.1 versioned server over plain-text!

5 | 6 | -------------------------------------------------------------------------------- /integration_tests/mqtt/mosquitto.conf: -------------------------------------------------------------------------------- 1 | listener 1883 0.0.0.0 2 | 3 | listener 8883 0.0.0.0 4 | protocol mqtt 5 | certfile /mosquitto/certs/server.crt 6 | keyfile /mosquitto/certs/server.key -------------------------------------------------------------------------------- /integration_tests/pptp/chap-secrets: -------------------------------------------------------------------------------- 1 | # Secrets for authentication using PAP 2 | # client server secret acceptable local IP addresses 3 | username * password * -------------------------------------------------------------------------------- /integration_tests/.template/module.go.tmpl: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import "github.com/zmap/zgrab2/modules/#{MODULE_NAME}" 4 | 5 | func init() { 6 | #{MODULE_NAME}.RegisterModule() 7 | } 8 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/http2-only-https/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Hello from HTTP/2 versioned server over HTTPS!

5 | 6 | -------------------------------------------------------------------------------- /lib/smb/about.txt: -------------------------------------------------------------------------------- 1 | Pulled from github.com/stacktitan/smb on 2018/03/16. 2 | 3 | Added smb/zgrab.go to achieve similar behavior to original zgrab scan, 4 | which directly modified NegotiateProtocol. 5 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/http-both-versions-https/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Hello from HTTP/1.1 AND HTTP/2 versioned server over HTTPS!

5 | 6 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/pop3-banner.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"pop3":{"status":"success","protocol":"pop3","result":{"banner":"+OK\r\n"},"timestamp":"2018-04-06T19:40:08Z"}}} 2 | -------------------------------------------------------------------------------- /docker-runner/service-base.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | # Base Ubuntu container with the apt cache already updated. 4 | # Many containers will be able to use this as their base. 5 | 6 | RUN apt-get update 7 | -------------------------------------------------------------------------------- /integration_tests/smtp/Dockerfile: -------------------------------------------------------------------------------- 1 | # Base image for smtp integration tests to avoid duplication of the same code in multiple Dockerfiles. 2 | FROM zgrab2_service_base:latest 3 | 4 | RUN apt-get install -y \ 5 | postfix -------------------------------------------------------------------------------- /integration_tests/smtp/ehlo-starttls/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_smtp_service_base:latest 2 | 3 | RUN rm -rf /var/lib/apt/lists/* 4 | 5 | # Command to run postfix (in the foreground) 6 | CMD ["postfix", "start-fg"] 7 | 8 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/ftp-default.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"ftp":{"status":"success","protocol":"ftp","result":{"banner":"220 (vsFTPd 3.0.3)\r\n"},"timestamp":"2018-04-06T19:37:52Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/h2c/go.mod: -------------------------------------------------------------------------------- 1 | module h2cserver 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.7 6 | 7 | require golang.org/x/net v0.38.0 8 | 9 | require golang.org/x/text v0.23.0 // indirect 10 | -------------------------------------------------------------------------------- /integration_tests/multiple/multiple.ini: -------------------------------------------------------------------------------- 1 | [Application Options] 2 | senders=2 3 | input-file="/multiple/input.csv" 4 | 5 | [ntp] 6 | trigger="ntp" 7 | 8 | [http] 9 | trigger="http80" 10 | max-redirects=3 11 | port=80 -------------------------------------------------------------------------------- /integration_tests/pop3/container/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | 5 | while true; do 6 | watch /usr/sbin/popa3d -D 7 | echo "popa3d exited unexpectedly. Restarting..." 8 | sleep 1 9 | done 10 | -------------------------------------------------------------------------------- /integration_tests/ipp/Dockerfile: -------------------------------------------------------------------------------- 1 | # Base image for ipp integration tests to avoid duplication of the same code in multiple Dockerfiles. 2 | FROM zgrab2_service_base:latest 3 | 4 | RUN apt-get install -y \ 5 | cups \ 6 | cups-pdf -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/pop3-banner.quit.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"pop3":{"status":"success","protocol":"pop3","result":{"banner":"+OK\r\n","quit":"+OK\r\n"},"timestamp":"2018-04-06T19:40:10Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/ftp/container/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | 5 | while true; do 6 | if ! /usr/sbin/vsftpd; then 7 | echo "vsftpd exited unexpectedly. Restarting..." 8 | sleep 1 9 | fi 10 | done 11 | -------------------------------------------------------------------------------- /integration_tests/redis/container/renamed.conf: -------------------------------------------------------------------------------- 1 | rename-command PING "" 2 | rename-command DEBUG "RENAMED-DEBUG" 3 | rename-command ECHO "RENAMED-ECHO" 4 | rename-command INFO "RENAMED-INFO" 5 | rename-command CLIENT "RENAMED-CLIENT" 6 | -------------------------------------------------------------------------------- /integration_tests/multiple/bad-input-file.ini: -------------------------------------------------------------------------------- 1 | [Application Options] 2 | senders=2 3 | input-file="/non-existent-folder/non-existent-file.csv" 4 | 5 | [ntp] 6 | trigger="ntp" 7 | 8 | [http] 9 | trigger="http80" 10 | max-redirects=3 -------------------------------------------------------------------------------- /integration_tests/telnet/container/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | 5 | while true; do 6 | if ! inetutils-inetd -d; then 7 | echo "telnetd exited unexpectedly. Restarting..." 8 | sleep 1 9 | fi 10 | done 11 | -------------------------------------------------------------------------------- /integration_tests/pop3/container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_service_base:latest 2 | 3 | RUN apt-get install -y popa3d 4 | 5 | WORKDIR / 6 | COPY entrypoint.sh . 7 | RUN chmod a+x ./entrypoint.sh 8 | 9 | ENTRYPOINT ["/entrypoint.sh"] 10 | -------------------------------------------------------------------------------- /integration_tests/multiple/docker-run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Runs the zgrab2_runner docker image (built with docker-runner/build-runner.sh) 4 | 5 | : "${DIR:?}" 6 | 7 | set -e 8 | docker run --rm -i -v $DIR:/multiple zgrab2_runner $@ 9 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/pop3-help.banner.quit.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"pop3":{"status":"success","protocol":"pop3","result":{"banner":"+OK\r\n","help":"-ERR\r\n","quit":"+OK\r\n"},"timestamp":"2018-04-06T19:40:12Z"}}} 2 | -------------------------------------------------------------------------------- /bin/doc.go: -------------------------------------------------------------------------------- 1 | // Package bin contains functions useful for creating a binary version of 2 | // ZGrab2. 3 | // 4 | // This package can import "github.com/zmap/zgrab2", and should be imported by 5 | // targets within "github.com/zmap/zgrab2/cmd" 6 | package bin 7 | -------------------------------------------------------------------------------- /cmd/zgrab2/mult.ini: -------------------------------------------------------------------------------- 1 | [Application Options] 2 | output-file="output.txt" 3 | input-file="input.txt" 4 | [tls] 5 | name="tls80" 6 | endpoint = "Endpoint" 7 | [tls] 8 | name="tls8080" 9 | port=8080 10 | endpoint = "Endpoint" 11 | [ssh] 12 | port = 22 13 | -------------------------------------------------------------------------------- /integration_tests/postgres/container/postgresql.conf.nossl.partial: -------------------------------------------------------------------------------- 1 | logging_collector = on 2 | log_directory = 'pg_log' 3 | log_filename = 'postgres.log' 4 | log_file_mode = '0666' 5 | log_min_messages = 'DEBUG5' 6 | log_connections = on 7 | log_disconnections = on 8 | -------------------------------------------------------------------------------- /integration_tests/postgres/container/postgresql.conf.9.3.nossl.partial: -------------------------------------------------------------------------------- 1 | logging_collector = on 2 | log_directory = 'pg_log' 3 | log_filename = 'postgres.log' 4 | log_file_mode = '0666' 5 | log_min_messages = 'DEBUG5' 6 | log_connections = on 7 | log_disconnections = on 8 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/pop3-noop.help.banner.quit.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"pop3":{"status":"success","protocol":"pop3","result":{"banner":"+OK\r\n","noop":"-ERR\r\n","help":"-ERR\r\n","quit":"+OK\r\n"},"timestamp":"2018-04-06T19:40:14Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/smtp-00.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"smtp":{"status":"success","protocol":"smtp","result":{"banner":"220 ba06a6536f7e ESMTP qpsmtpd 0.94 ready; send us your mail, but not your spam.\r\n"},"timestamp":"2018-04-06T19:42:03Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/telnet/container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_service_base:latest 2 | 3 | RUN apt-get install -y inetutils-telnetd 4 | COPY inetd.conf /etc/inetd.conf 5 | 6 | WORKDIR / 7 | COPY entrypoint.sh . 8 | RUN chmod a+x ./entrypoint.sh 9 | 10 | ENTRYPOINT ["/entrypoint.sh"] 11 | -------------------------------------------------------------------------------- /cmd/zgrab2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/zmap/zgrab2/bin" 5 | _ "github.com/zmap/zgrab2/modules" 6 | ) 7 | 8 | // main wraps the "true" main, bin.ZGrab2Main(), after importing all scan 9 | // modules in ZGrab2. 10 | func main() { 11 | bin.ZGrab2Main() 12 | } 13 | -------------------------------------------------------------------------------- /tools/keys/testdata/test1024dh.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIGHAoGBAK5q+uEdYHZkVhcBVRTwOHGgnqIMAgMONcfULTJqYSRy3mRTt+q0iVH5 3 | LiRtGxjEqrVcDJDs+aA92AnvhW50w8ITQheqaHn9nLXtbjox54bKCLzm52XLsgjq 4 | jCE85g5m3V59BFfY5LML70BxDKHiEnWAkoUibs83Q0gnTCEi5sfjAgEC 5 | -----END DH PARAMETERS----- 6 | -------------------------------------------------------------------------------- /integration_tests/postgres/container/setup_nossl.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | set -x 4 | WORKDIR=/tmp/postgres_setup 5 | echo "" >> $PGDATA/postgresql.conf 6 | 7 | # Attach the SSL + Logging config to the end of the main postgresql.conf file 8 | cat $WORKDIR/postgresql.conf.partial >> $PGDATA/postgresql.conf 9 | -------------------------------------------------------------------------------- /integration_tests/ipp/container-cups/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | 5 | while true; do 6 | #FIXME: Determine whether -f or -F is ideal, and whether any other options are needed 7 | if ! /usr/sbin/cupsd -f; then 8 | echo "cupsd exited unexpectedly. Restarting..." 9 | sleep 1 10 | fi 11 | done 12 | -------------------------------------------------------------------------------- /integration_tests/ipp/container-cups-tls/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | 5 | while true; do 6 | #FIXME: Determine whether -f or -F is ideal, and whether any other options are needed 7 | if ! /usr/sbin/cupsd -f; then 8 | echo "cupsd exited unexpectedly. Restarting..." 9 | sleep 1 10 | fi 11 | done 12 | -------------------------------------------------------------------------------- /integration_tests/ipp/container-cups/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_ipp_service_base:latest 2 | 3 | WORKDIR /etc/cups 4 | COPY cupsd.conf cupsd.conf 5 | 6 | RUN service cups stop 7 | RUN update-rc.d -f cupsd remove 8 | 9 | WORKDIR / 10 | COPY entrypoint.sh . 11 | RUN chmod a+x ./entrypoint.sh 12 | 13 | ENTRYPOINT ["/entrypoint.sh"] -------------------------------------------------------------------------------- /integration_tests/ipp/container-cups-tls/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_ipp_service_base:latest 2 | 3 | WORKDIR /etc/cups 4 | COPY cupsssl.conf cupsd.conf 5 | 6 | RUN service cups stop 7 | RUN update-rc.d -f cupsd remove 8 | 9 | WORKDIR / 10 | COPY entrypoint.sh . 11 | RUN chmod a+x ./entrypoint.sh 12 | 13 | ENTRYPOINT ["/entrypoint.sh"] -------------------------------------------------------------------------------- /integration_tests/smtp/ehlo-no-starttls/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_smtp_service_base:latest 2 | 3 | RUN rm -rf /var/lib/apt/lists/* 4 | 5 | # Disable STARTTLS support 6 | RUN echo "smtpd_tls_security_level = none" >> /etc/postfix/main.cf 7 | 8 | # Command to run postfix (in the foreground) 9 | CMD ["postfix", "start-fg"] 10 | 11 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/h2c/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.23 AS build 2 | 3 | WORKDIR /app 4 | COPY go.mod go.sum ./ 5 | RUN go mod download 6 | 7 | COPY . . 8 | RUN go build -o server main.go 9 | 10 | FROM debian:stable-slim 11 | WORKDIR /app 12 | COPY --from=build /app/server . 13 | EXPOSE 8083 14 | CMD ["./server"] 15 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/smtp-ehlo.03.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"smtp":{"status":"success","protocol":"smtp","result":{"banner":"220 ba06a6536f7e ESMTP qpsmtpd 0.94 ready; send us your mail, but not your spam.\r\n","ehlo":"501 ehlo requires domain/address - see RFC-2821 4.1.1.1\r\n"},"timestamp":"2018-04-06T19:42:10Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/smtp-helo.01.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"smtp":{"status":"success","protocol":"smtp","result":{"banner":"220 ba06a6536f7e ESMTP qpsmtpd 0.94 ready; send us your mail, but not your spam.\r\n","helo":"501 helo requires domain/address - see RFC-2821 4.1.1.1\r\n"},"timestamp":"2018-04-06T19:42:05Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/smtp/helo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_smtp_service_base:latest 2 | 3 | RUN rm -rf /var/lib/apt/lists/* 4 | 5 | # Disable all ESMTP capabilities by not announcing ESMTP 6 | RUN echo "smtpd_banner = $$myhostname $$mail_name" >> /etc/postfix/main.cf 7 | 8 | # Command to run postfix (in the foreground) 9 | CMD ["postfix", "start-fg"] 10 | -------------------------------------------------------------------------------- /lib/ssh/test/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package test contains integration tests for the 6 | // github.com/zmap/zgrab2/lib/ssh package. 7 | package test // import "github.com/zmap/zgrab2/lib/ssh/test" 8 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/smtp-helo.02.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"smtp":{"status":"success","protocol":"smtp","result":{"banner":"220 ba06a6536f7e ESMTP qpsmtpd 0.94 ready; send us your mail, but not your spam.\r\n","helo":"250 ba06a6536f7e Hi Unknown [172.17.0.4]; I am so happy to meet you.\r\n"},"timestamp":"2018-04-06T19:42:08Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/h2c/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= 2 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 3 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 4 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 5 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/smtp-ehlo.04.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"smtp":{"status":"success","protocol":"smtp","result":{"banner":"220 ba06a6536f7e ESMTP qpsmtpd 0.94 ready; send us your mail, but not your spam.\r\n","ehlo":"250-ba06a6536f7e Hi Unknown [172.17.0.4]\r\n250-PIPELINING\r\n250 8BITMIME\r\n"},"timestamp":"2018-04-06T19:42:12Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/mqtt/multiple.ini: -------------------------------------------------------------------------------- 1 | [mqtt] 2 | name="mqtt-tls" 3 | trigger="mqtt-tls" 4 | port=8883 5 | tls=true 6 | 7 | [mqtt] 8 | name="mqtt-tls-v5" 9 | trigger="mqtt-tls-v5" 10 | port=8883 11 | tls=true 12 | v5=true 13 | 14 | [mqtt] 15 | name="mqtt" 16 | trigger="mqtt" 17 | port=1883 18 | 19 | [mqtt] 20 | name="mqtt-v5" 21 | trigger="mqtt-v5" 22 | port=1883 23 | v5=true -------------------------------------------------------------------------------- /lib/ssh/internal/poly1305/mac_noasm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build (!amd64 && !ppc64le && !s390x) || !gc || purego 6 | // +build !amd64,!ppc64le,!s390x !gc purego 7 | 8 | package poly1305 9 | 10 | type mac struct{ macGeneric } 11 | -------------------------------------------------------------------------------- /lib/smb/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /lib/smb/gss/oid.go: -------------------------------------------------------------------------------- 1 | package gss 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | func ObjectIDStrToInt(oid string) ([]int, error) { 9 | ret := []int{} 10 | tokens := strings.Split(oid, ".") 11 | for _, token := range tokens { 12 | i, err := strconv.Atoi(token) 13 | if err != nil { 14 | return nil, err 15 | } 16 | ret = append(ret, i) 17 | } 18 | return ret, nil 19 | } 20 | -------------------------------------------------------------------------------- /integration_tests/ntp/container-openntp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_service_base:latest 2 | 3 | RUN apt-get install -y openntpd 4 | # Run directly from the entrypoint 5 | RUN service openntpd stop 6 | 7 | WORKDIR /etc/openntpd 8 | COPY ntpd.conf . 9 | RUN mkdir -p /var/run/openntpd 10 | 11 | # Must be run with --privileged 12 | # Don't fork, extra debugging info 13 | ENTRYPOINT ["/usr/sbin/ntpd", "-d", "-v"] 14 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-password-inline.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: NOAUTH Authentication required.)","info_response":"(Error: NOAUTH Authentication required.)","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","quit_response":"OK"},"timestamp":"2019-06-20T21:12:50Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-password-normal.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: NOAUTH Authentication required.)","info_response":"(Error: NOAUTH Authentication required.)","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","quit_response":"OK"},"timestamp":"2019-06-20T21:12:45Z"}}} 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | 4 | # zgrab 5 | /zgrab2 6 | cmd/zgrab2/zgrab2 7 | cmd/zgrab2/zgrab2.exe 8 | zgrab-output/ 9 | 10 | # CI dependencies 11 | jp 12 | jp.exe 13 | 14 | # Compiled python modules 15 | schemas/*.pyc 16 | 17 | # Marker files for make 18 | docker-runner/*.id 19 | 20 | # Python 21 | venv/* 22 | *.pyc 23 | zgrab2_schemas.egg-info/* 24 | build/* 25 | dist/* 26 | 27 | .vscode/* 28 | .idea/* 29 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/smtp-ehlo.quit.05.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"smtp":{"status":"success","protocol":"smtp","result":{"banner":"220 ba06a6536f7e ESMTP qpsmtpd 0.94 ready; send us your mail, but not your spam.\r\n","ehlo":"250-ba06a6536f7e Hi Unknown [172.17.0.4]\r\n250-PIPELINING\r\n250 8BITMIME\r\n","quit":"221 ba06a6536f7e closing connection. Have a wonderful day.\r\n"},"timestamp":"2018-04-06T19:42:14Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/postgres/container/postgresql.conf.9.3.ssl.partial: -------------------------------------------------------------------------------- 1 | ssl = on 2 | ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' 3 | ssl_cert_file = 'server.crt' 4 | ssl_key_file = 'server.key' 5 | ssl_ca_file = '' 6 | ssl_crl_file = '' 7 | 8 | logging_collector = on 9 | log_directory = 'pg_log' 10 | log_filename = 'postgres.log' 11 | log_file_mode = '0666' 12 | log_min_messages = 'DEBUG5' 13 | log_connections = on 14 | log_disconnections = on 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | _Add a description of your changes here._ 2 | 3 | ## How to Test 4 | 5 | _Add brief instructions on how to test your changes._ 6 | 7 | ## Notes & Caveats 8 | 9 | _If necessary, explain the motivation for this PR, and note any caveats that apply to your changes or future work that will be needed._ 10 | 11 | ## Issue Tracking 12 | 13 | _Add a link to the relevant GitHub issue(s) if the pull request resolves it._ 14 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-renamed-inline.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: ERR unknown command `PING`, with args beginning with: )","info_response":"(Error: ERR unknown command `INFO`, with args beginning with: )","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","quit_response":"OK"},"timestamp":"2019-06-20T21:13:01Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-renamed-normal.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: ERR unknown command `PING`, with args beginning with: )","info_response":"(Error: ERR unknown command `INFO`, with args beginning with: )","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","quit_response":"OK"},"timestamp":"2019-06-20T21:12:56Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/.template/container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_service_base:latest 2 | 3 | RUN apt-get install -y FIXME_YOUR_SERVICE_PKG 4 | 5 | # Try to make it act more container-y -- remove it from init.d and just run the daemon as the entrypoint 6 | RUN service FIXME_YOUR_SERVICED stop 7 | RUN update-rc.d -f FIXME_YOUR_SERVICED remove 8 | 9 | WORKDIR / 10 | COPY entrypoint.sh . 11 | RUN chmod a+x ./entrypoint.sh 12 | 13 | ENTRYPOINT ["/entrypoint.sh"] 14 | -------------------------------------------------------------------------------- /lib/http/transport_default_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !wasm 6 | 7 | package http 8 | 9 | import ( 10 | "context" 11 | "net" 12 | ) 13 | 14 | func defaultTransportDialContext(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) { 15 | return dialer.DialContext 16 | } 17 | -------------------------------------------------------------------------------- /lib/http/transport_default_wasm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build (js && wasm) || wasip1 6 | 7 | package http 8 | 9 | import ( 10 | "context" 11 | "net" 12 | ) 13 | 14 | func defaultTransportDialContext(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) { 15 | return nil 16 | } 17 | -------------------------------------------------------------------------------- /lib/ssh/testdata/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This package contains test data shared between the various subpackages of 6 | // the github.com/zmap/zgrab2/lib/ssh package. Under no circumstance should 7 | // this data be used for production code. 8 | package testdata // import "github.com/zmap/zgrab2/lib/ssh/testdata" 9 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-default-inline-mappings.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: ERR unknown command `RENAMED-PING`, with args beginning with: )","info_response":"(Error: ERR unknown command `RENAMED-INFO`, with args beginning with: )","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","quit_response":"OK"},"timestamp":"2019-06-20T21:12:42Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-default-normal-mappings.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: ERR unknown command `RENAMED-PING`, with args beginning with: )","info_response":"(Error: ERR unknown command `RENAMED-INFO`, with args beginning with: )","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","quit_response":"OK"},"timestamp":"2019-06-20T21:12:37Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-password-inline-mappings.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: ERR unknown command `RENAMED-PING`, with args beginning with: )","info_response":"(Error: ERR unknown command `RENAMED-INFO`, with args beginning with: )","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","quit_response":"OK"},"timestamp":"2019-06-20T21:12:52Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-password-normal-mappings.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: ERR unknown command `RENAMED-PING`, with args beginning with: )","info_response":"(Error: ERR unknown command `RENAMED-INFO`, with args beginning with: )","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","quit_response":"OK"},"timestamp":"2019-06-20T21:12:47Z"}}} 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | - package-ecosystem: github-actions 8 | directory: "/" 9 | schedule: 10 | interval: weekly 11 | - package-ecosystem: docker 12 | directory: "/" 13 | schedule: 14 | interval: weekly 15 | - package-ecosystem: docker 16 | directory: "/docker-runner" 17 | schedule: 18 | interval: weekly 19 | -------------------------------------------------------------------------------- /integration_tests/postgres/container/postgresql.conf.ssl.partial: -------------------------------------------------------------------------------- 1 | ssl = on 2 | ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' 3 | ssl_prefer_server_ciphers = on 4 | ssl_ecdh_curve = 'prime256v1' 5 | ssl_cert_file = 'server.crt' 6 | ssl_key_file = 'server.key' 7 | ssl_ca_file = '' 8 | ssl_crl_file = '' 9 | 10 | logging_collector = on 11 | log_directory = 'pg_log' 12 | log_filename = 'postgres.log' 13 | log_file_mode = '0666' 14 | log_min_messages = 'DEBUG5' 15 | log_connections = on 16 | log_disconnections = on 17 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/smtp-help.quit.06.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"smtp":{"status":"success","protocol":"smtp","result":{"banner":"220 ba06a6536f7e ESMTP qpsmtpd 0.94 ready; send us your mail, but not your spam.\r\n","help":"214-This is qpsmtpd 0.94\r\n214-See http://smtpd.develooper.com/\r\n214 To report bugs or send comments, mail to \u003cask@develooper.com\u003e.\r\n","quit":"221 ba06a6536f7e closing connection. Have a wonderful day.\r\n"},"timestamp":"2018-04-06T19:42:16Z"}}} 2 | -------------------------------------------------------------------------------- /docker-runner/docker-run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Runs the zgrab2_runner docker image (built with docker-runner/build-runner.sh) 4 | # Links the runner image to the targeted container with the hostname alias "target" 5 | # (or $target_name if set), then scans it using the arguments to the script. 6 | 7 | : "${CONTAINER_NAME:?}" 8 | 9 | set -e 10 | TARGET_NAME="${TARGET_NAME:-target}" 11 | 12 | echo "$TARGET_NAME" | docker run --rm -i --network container:"$CONTAINER_NAME" zgrab2_runner --blocklist-file="" $@ 13 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/http1/nginx.conf: -------------------------------------------------------------------------------- 1 | events {} 2 | http { 3 | server { 4 | listen 80; 5 | server_name localhost; 6 | location / { 7 | root /usr/share/nginx/html; 8 | index index.html; 9 | } 10 | location /redirect-http-2 { 11 | return 302 https://localhost:8083; 12 | } 13 | location /redirect-back-to-http-1 { 14 | return 302 http://localhost:8081; 15 | } 16 | } 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/ntp-4.2.6_REQ_MON_GETLIST_solo.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.4","domain":"target","data":{"ntp":{"status":"success","protocol":"ntp","result":{"monlist_response":"AAAAAgAAAAIAAAAAAAAABKwRAAWlHQMDAAAAAJTbAwMAAAAAAAAAAAAAAAAAAAAA","monlist_header":{"is_response":true,"has_more":false,"version":3,"mode":7,"is_authenticated":false,"sequence_number":0,"implementation_number":"IMPL_XNTPD","request_code":"REQ_MON_GETLIST","error":"INFO_OKAY","num_items":1,"mbz":0,"item_size":48}},"timestamp":"2018-04-06T19:39:56Z"}}} 2 | -------------------------------------------------------------------------------- /docker-runner/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This is the entrypoint for the zgrab2_runner container. 4 | # Runs the zgrab2 binary, passing along any arguments, with stdin containing the single line: the ZGRAB_TARGET. 5 | 6 | set -e 7 | 8 | cd /go/src/github.com/zmap/zgrab2 9 | 10 | if ! [ -x $ZGRAB_REBUILD ]; then 11 | if ! [ -x $ZGRAB_BRANCH ]; then 12 | git checkout $ZGRAB_BRANCH 13 | fi 14 | git pull 15 | make 16 | fi 17 | 18 | set -x 19 | echo $ZGRAB_TARGET | /go/src/github.com/zmap/zgrab2/cmd/zgrab2/zgrab2 $* 20 | -------------------------------------------------------------------------------- /integration_tests/postgres/container/Dockerfile.9.3: -------------------------------------------------------------------------------- 1 | FROM postgres:9.3 2 | 3 | ARG IMAGE_TYPE=ssl 4 | 5 | WORKDIR /tmp/postgres_setup 6 | COPY postgresql.conf.9.3.$IMAGE_TYPE.partial postgresql.conf.partial 7 | COPY setup_$IMAGE_TYPE.sh /docker-entrypoint-initdb.d/setup_$IMAGE_TYPE.sh 8 | 9 | RUN chown -R postgres:postgres . 10 | RUN chown postgres:postgres /docker-entrypoint-initdb.d/setup_$IMAGE_TYPE.sh 11 | 12 | RUN chmod 0755 . 13 | RUN chmod 0644 postgresql.conf.partial 14 | RUN chmod 0755 /docker-entrypoint-initdb.d/setup_$IMAGE_TYPE.sh 15 | -------------------------------------------------------------------------------- /modules/bacnet/objects_test.go: -------------------------------------------------------------------------------- 1 | package bacnet 2 | 3 | import ( 4 | . "gopkg.in/check.v1" 5 | ) 6 | 7 | type ObjectsSuite struct { 8 | } 9 | 10 | var _ = Suite(&ObjectsSuite{}) 11 | 12 | func (s *ObjectsSuite) TestMarshalUnmarshalReadProperty(c *C) { 13 | rp := ReadProperty{ 14 | Object: OID_ANY, 15 | Property: PID_OID, 16 | } 17 | b, err := rp.Marshal() 18 | c.Assert(err, IsNil) 19 | dec := new(ReadProperty) 20 | b, err = dec.Unmarshal(b) 21 | c.Assert(err, IsNil) 22 | c.Check(len(b), Equals, 0) 23 | c.Check(dec, DeepEquals, &rp) 24 | } 25 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/ntp-4.2.6_REQ_MON_GETLIST_1_solo.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.4","domain":"target","data":{"ntp":{"status":"success","protocol":"ntp","result":{"monlist_response":"AAAAAgAAAAIAAAAAAAAAA6wRAAWsEQAEAAAAAZTbAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","monlist_header":{"is_response":true,"has_more":false,"version":3,"mode":7,"is_authenticated":false,"sequence_number":0,"implementation_number":"IMPL_XNTPD","request_code":"REQ_MON_GETLIST_1","error":"INFO_OKAY","num_items":1,"mbz":0,"item_size":72}},"timestamp":"2018-04-06T19:39:52Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/http2-only-https/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM caddy:alpine 2 | 3 | # Copy Caddyfile 4 | COPY Caddyfile /etc/caddy/Caddyfile 5 | COPY index.html /usr/share/caddy/html/index.html 6 | 7 | ## Copy static files 8 | #COPY index.html /usr/share/caddy/index.html 9 | 10 | # Generate self-signed cert at build time 11 | RUN apk add openssl && \ 12 | mkdir -p /etc/ssl/private && \ 13 | openssl req -x509 -newkey rsa:2048 -keyout /etc/ssl/private/self.key -out /etc/ssl/private/self.crt \ 14 | -days 365 -nodes -subj "/CN=localhost" 15 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/http-both-versions-https/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM caddy:alpine 2 | 3 | # Copy Caddyfile 4 | COPY Caddyfile /etc/caddy/Caddyfile 5 | COPY index.html /usr/share/caddy/html/index.html 6 | 7 | ## Copy static files 8 | #COPY index.html /usr/share/caddy/index.html 9 | 10 | # Generate self-signed cert at build time 11 | RUN apk add openssl && \ 12 | mkdir -p /etc/ssl/private && \ 13 | openssl req -x509 -newkey rsa:2048 -keyout /etc/ssl/private/self.key -out /etc/ssl/private/self.crt \ 14 | -days 365 -nodes -subj "/CN=localhost" 15 | -------------------------------------------------------------------------------- /lib/http2/config_pre_go124.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | ////go:build !go1.24 6 | 7 | package http2 8 | 9 | import "github.com/zmap/zgrab2/lib/http" 10 | 11 | // Pre-Go 1.24 fallback. 12 | // The Server.HTTP2 and Transport.HTTP2 config fields were added in Go 1.24. 13 | 14 | func fillNetHTTPServerConfig(conf *http2Config, srv *http.Server) {} 15 | 16 | func fillNetHTTPTransportConfig(conf *http2Config, tr *http.Transport) {} 17 | -------------------------------------------------------------------------------- /lib/http/cookiejar/dummy_publicsuffix_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cookiejar_test 6 | 7 | import "github.com/zmap/zgrab2/lib/http/cookiejar" 8 | 9 | type dummypsl struct { 10 | List cookiejar.PublicSuffixList 11 | } 12 | 13 | func (dummypsl) PublicSuffix(domain string) string { 14 | return domain 15 | } 16 | 17 | func (dummypsl) String() string { 18 | return "dummy" 19 | } 20 | 21 | var publicsuffix = dummypsl{} 22 | -------------------------------------------------------------------------------- /lib/ssh/tcpip_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestAutoPortListenBroken(t *testing.T) { 12 | broken := "SSH-2.0-OpenSSH_5.9hh11" 13 | works := "SSH-2.0-OpenSSH_6.1" 14 | if !isBrokenOpenSSHVersion(broken) { 15 | t.Errorf("version %q not marked as broken", broken) 16 | } 17 | if isBrokenOpenSSHVersion(works) { 18 | t.Errorf("version %q marked as broken", works) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/http/method.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http 6 | 7 | // Common HTTP methods. 8 | // 9 | // Unless otherwise noted, these are defined in RFC 7231 section 4.3. 10 | const ( 11 | MethodGet = "GET" 12 | MethodHead = "HEAD" 13 | MethodPost = "POST" 14 | MethodPut = "PUT" 15 | MethodPatch = "PATCH" // RFC 5789 16 | MethodDelete = "DELETE" 17 | MethodConnect = "CONNECT" 18 | MethodOptions = "OPTIONS" 19 | MethodTrace = "TRACE" 20 | ) 21 | -------------------------------------------------------------------------------- /lib/http2/timer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | package http2 5 | 6 | import "time" 7 | 8 | // A timer is a time.Timer, as an interface which can be replaced in tests. 9 | type timer = interface { 10 | C() <-chan time.Time 11 | Reset(d time.Duration) bool 12 | Stop() bool 13 | } 14 | 15 | // timeTimer adapts a time.Timer to the timer interface. 16 | type timeTimer struct { 17 | *time.Timer 18 | } 19 | 20 | func (t timeTimer) C() <-chan time.Time { return t.Timer.C } 21 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import setup, find_packages 3 | 4 | here = os.path.abspath(os.path.dirname(__file__)) 5 | 6 | setup( 7 | name="zgrab2_schemas", 8 | version="0.0.1", 9 | description="ZSchema definitions for zgrab2's JSON output.", 10 | classifiers=["Programming Language :: Python", "Natural Language :: English"], 11 | author="ZMap Team", 12 | author_email="team@zmap.io", 13 | url="https://github.com/zmap/zgrab2", 14 | keywords="zmap censys zgrab2 internet-wide scanning", 15 | packages=find_packages(), 16 | include_package_data=True, 17 | zip_safe=False, 18 | ) 19 | -------------------------------------------------------------------------------- /lib/ssh/internal/poly1305/bits_go1.13.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build go1.13 6 | // +build go1.13 7 | 8 | package poly1305 9 | 10 | import "math/bits" 11 | 12 | func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) { 13 | return bits.Add64(x, y, carry) 14 | } 15 | 16 | func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) { 17 | return bits.Sub64(x, y, borrow) 18 | } 19 | 20 | func bitsMul64(x, y uint64) (hi, lo uint64) { 21 | return bits.Mul64(x, y) 22 | } 23 | -------------------------------------------------------------------------------- /bin/summary.go: -------------------------------------------------------------------------------- 1 | package bin 2 | 3 | import "github.com/zmap/zgrab2" 4 | 5 | // Metadata holds the results of a run of a ZGrab2 binary. 6 | type Metadata struct { 7 | PerModuleMetadata map[string]*zgrab2.ModuleMetadata `json:"statuses"` 8 | StartTime string `json:"start"` 9 | EndTime string `json:"end"` 10 | Duration string `json:"duration"` 11 | CLIInvocation string `json:"zgrab_cli_parameters,omitempty"` 12 | NumTargetsScanned uint `json:"num_targets_scanned"` 13 | } 14 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/ntp-openntp.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"ntp":{"status":"success","protocol":"ntp","result":{"version":3,"time":"2018-04-06T19:39:39.477943897Z","time_response":{"leap_indicator":3,"version":3,"mode":4,"stratum":0,"poll":0,"precision":-29,"root_delay":{"seconds":0,"fraction":0},"root_dispersion":{"seconds":0,"fraction":0},"reference_id":"AAAAAA==","reference_timestamp":{"seconds":0,"fraction":0},"origin_timestamp":{"seconds":0,"fraction":0},"receive_timestamp":{"seconds":3732032379,"fraction":2052753407},"transmit_timestamp":{"seconds":3732032379,"fraction":2052788223}}},"timestamp":"2018-04-06T19:39:39Z"}}} 2 | -------------------------------------------------------------------------------- /lib/http2/errors_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import "testing" 8 | 9 | func TestErrCodeString(t *testing.T) { 10 | tests := []struct { 11 | err ErrCode 12 | want string 13 | }{ 14 | {ErrCodeProtocol, "PROTOCOL_ERROR"}, 15 | {0xd, "HTTP_1_1_REQUIRED"}, 16 | {0xf, "unknown error code 0xf"}, 17 | } 18 | for i, tt := range tests { 19 | got := tt.err.String() 20 | if got != tt.want { 21 | t.Errorf("%d. Error = %q; want %q", i, got, tt.want) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /modules/memcached/scanner_test.go: -------------------------------------------------------------------------------- 1 | package memcached 2 | 3 | import "testing" 4 | 5 | func TestSnakeToCamel(t *testing.T) { 6 | tests := []struct { 7 | input string 8 | expected string 9 | }{ 10 | {"simple_test", "SimpleTest"}, 11 | {"another_example_here", "AnotherExampleHere"}, 12 | {"test", "Test"}, 13 | {"multiple_words_in_string", "MultipleWordsInString"}, 14 | {"single", "Single"}, 15 | {"", ""}, 16 | } 17 | for _, test := range tests { 18 | result := SnakeToCamel(test.input) 19 | if result != test.expected { 20 | t.Errorf("snakeToCamel(%q) = %q; expected %q", test.input, result, test.expected) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/ntp-4.2.6_normal.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.4","domain":"target","data":{"ntp":{"status":"success","protocol":"ntp","result":{"version":3,"time":"2018-04-06T19:39:46.56800099Z","time_response":{"leap_indicator":3,"version":3,"mode":4,"stratum":0,"poll":3,"precision":-22,"root_delay":{"seconds":0,"fraction":0},"root_dispersion":{"seconds":0,"fraction":8},"reference_id":"SU5JVA==","reference_timestamp":{"seconds":0,"fraction":0},"origin_timestamp":{"seconds":0,"fraction":0},"receive_timestamp":{"seconds":3732032386,"fraction":2439545680},"transmit_timestamp":{"seconds":3732032386,"fraction":2441496926}}},"timestamp":"2018-04-06T19:39:46Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/telnet-telnet.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.2","domain":"target","data":{"telnet":{"status":"success","protocol":"telnet","result":{"banner":"Ubuntu 16.04.3 LTS\r\n5a3511f30de7 login: ","will":[{"name":"Suppress Go Ahead","value":3},{"name":"Status","value":5},{"name":"Suppress Go Ahead","value":3},{"name":"Echo","value":1}],"do":[{"name":"Terminal Type","value":24},{"name":"Terminal Speed","value":32},{"name":"X Display Location","value":35},{"name":"New Environment Option","value":39},{"name":"Echo","value":1},{"name":"Negotiate About Window Size","value":31},{"name":"Remote Flow Control","value":33}]},"timestamp":"2018-04-06T19:42:46Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/mssql/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | MODULE_DIR=$(dirname $0) 5 | TEST_ROOT=$MODULE_DIR/.. 6 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 7 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 8 | 9 | CONTAINER_NAME="zgrab_mssql-2022-linux" 10 | 11 | mkdir -p $ZGRAB_OUTPUT/mssql 12 | 13 | OUTPUT_FILE="$ZGRAB_OUTPUT/mssql/2022-linux.json" 14 | 15 | echo "mssql/test: Tests runner for mssql" 16 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh mssql > $OUTPUT_FILE 17 | 18 | echo "BEGIN DOCKER LOGS FROM $CONTAINER_NAME [{(" 19 | docker logs --tail all $CONTAINER_NAME 20 | echo ")}] END DOCKER LOGS FROM $CONTAINER_NAME" 21 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/postgres-9.3-nossl.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.4","domain":"target","data":{"postgres":{"status":"success","protocol":"postgres","result":{"supported_versions":"FATAL: unsupported frontend protocol 0.0: server supports 1.0 to 3.0","protocol_error":{"code":"0A000","file":"postmaster.c","line":"1995","message":"unsupported frontend protocol 255.255: server supports 1.0 to 3.0","routine":"ProcessStartupPacket","severity":"FATAL"},"startup_error":{"code":"28000","file":"postmaster.c","line":"2090","message":"no PostgreSQL user name specified in startup packet","routine":"ProcessStartupPacket","severity":"FATAL"},"is_ssl":false},"timestamp":"2018-04-06T19:41:06Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/postgres-9.4-nossl.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.6","domain":"target","data":{"postgres":{"status":"success","protocol":"postgres","result":{"supported_versions":"FATAL: unsupported frontend protocol 0.0: server supports 1.0 to 3.0","protocol_error":{"code":"0A000","file":"postmaster.c","line":"2010","message":"unsupported frontend protocol 255.255: server supports 1.0 to 3.0","routine":"ProcessStartupPacket","severity":"FATAL"},"startup_error":{"code":"28000","file":"postmaster.c","line":"2118","message":"no PostgreSQL user name specified in startup packet","routine":"ProcessStartupPacket","severity":"FATAL"},"is_ssl":false},"timestamp":"2018-04-06T19:41:11Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/postgres-9.5-nossl.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.8","domain":"target","data":{"postgres":{"status":"success","protocol":"postgres","result":{"supported_versions":"FATAL: unsupported frontend protocol 0.0: server supports 1.0 to 3.0","protocol_error":{"code":"0A000","file":"postmaster.c","line":"2015","message":"unsupported frontend protocol 255.255: server supports 1.0 to 3.0","routine":"ProcessStartupPacket","severity":"FATAL"},"startup_error":{"code":"28000","file":"postmaster.c","line":"2125","message":"no PostgreSQL user name specified in startup packet","routine":"ProcessStartupPacket","severity":"FATAL"},"is_ssl":false},"timestamp":"2018-04-06T19:41:17Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/http-both-versions-https/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | # Enable debug logging globally 3 | log { 4 | output stdout 5 | format console 6 | level DEBUG 7 | } 8 | } 9 | 10 | 11 | https://localhost, https://http2.target { 12 | tls internal 13 | root * /usr/share/caddy/html/ 14 | file_server 15 | 16 | tls /etc/ssl/private/self.crt /etc/ssl/private/self.key 17 | 18 | # Enhanced logging with HTTP/2 details 19 | log { 20 | output stdout 21 | format json 22 | level DEBUG 23 | } 24 | 25 | handle_path /redirect-http-2 { 26 | redir http://localhost:8083 27 | } 28 | } -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-password-inline-extra.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: NOAUTH Authentication required.)","info_response":"(Error: NOAUTH Authentication required.)","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","custom_responses":[{"command":"PING","response":"(Error: NOAUTH Authentication required.)"},{"command":"AUTH","arguments":"password!","response":"OK"},{"command":"YOUR-COMMAND","arguments":"args","response":"(Error: ERR unknown command `YOUR-COMMAND`, with args beginning with: `args`, )"}],"quit_response":"OK"},"timestamp":"2019-06-20T21:12:54Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-password-normal-extra.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: NOAUTH Authentication required.)","info_response":"(Error: NOAUTH Authentication required.)","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","custom_responses":[{"command":"PING","response":"(Error: NOAUTH Authentication required.)"},{"command":"AUTH","arguments":"password!","response":"OK"},{"command":"YOUR-COMMAND","arguments":"args","response":"(Error: ERR unknown command `YOUR-COMMAND`, with args beginning with: `args`, )"}],"quit_response":"OK"},"timestamp":"2019-06-20T21:12:49Z"}}} 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ## Build image ## 2 | ARG GO_VERSION=1.24 3 | FROM golang:${GO_VERSION}-alpine3.21 AS build 4 | 5 | # System dependencies 6 | RUN apk add --no-cache make 7 | 8 | WORKDIR /usr/src/zgrab2 9 | 10 | # Copy and cache deps 11 | COPY go.mod go.sum ./ 12 | RUN go mod download && go mod verify 13 | 14 | # Build the actual app 15 | COPY . . 16 | RUN CGO_ENABLED=0 GOOS=linux make all 17 | 18 | ## Runtime image ## 19 | FROM alpine:latest 20 | 21 | COPY --from=build /usr/src/zgrab2/cmd/zgrab2/zgrab2 /usr/bin/zgrab2 22 | RUN mkdir -p /root/.config/zgrab2 23 | COPY --from=build /usr/src/zgrab2/conf/blocklist.conf /root/.config/zgrab2/blocklist.conf 24 | 25 | WORKDIR /usr/bin/ 26 | ENTRYPOINT ["zgrab2"] 27 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/postgres-10.1-nossl.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.12","domain":"target","data":{"postgres":{"status":"success","protocol":"postgres","result":{"supported_versions":"FATAL: unsupported frontend protocol 0.0: server supports 2.0 to 3.0","protocol_error":{"code":"0A000","file":"postmaster.c","line":"2065","message":"unsupported frontend protocol 255.255: server supports 2.0 to 3.0","routine":"ProcessStartupPacket","severity":"FATAL","severity_v":"FATAL"},"startup_error":{"code":"28000","file":"postmaster.c","line":"2175","message":"no PostgreSQL user name specified in startup packet","routine":"ProcessStartupPacket","severity":"FATAL","severity_v":"FATAL"},"is_ssl":false},"timestamp":"2018-04-06T19:41:29Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/postgres-9.6-nossl.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.10","domain":"target","data":{"postgres":{"status":"success","protocol":"postgres","result":{"supported_versions":"FATAL: unsupported frontend protocol 0.0: server supports 1.0 to 3.0","protocol_error":{"code":"0A000","file":"postmaster.c","line":"2031","message":"unsupported frontend protocol 255.255: server supports 1.0 to 3.0","routine":"ProcessStartupPacket","severity":"FATAL","severity_v":"FATAL"},"startup_error":{"code":"28000","file":"postmaster.c","line":"2141","message":"no PostgreSQL user name specified in startup packet","routine":"ProcessStartupPacket","severity":"FATAL","severity_v":"FATAL"},"is_ssl":false},"timestamp":"2018-04-06T19:41:23Z"}}} 2 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package zgrab2 2 | 3 | import "errors" 4 | 5 | // ErrMismatchedFlags is thrown if the flags for one module type are 6 | // passed to an incompatible module type. 7 | var ErrMismatchedFlags = errors.New("mismatched flag/module") 8 | 9 | // ErrInvalidArguments is thrown if the command-line arguments invalid. 10 | var ErrInvalidArguments = errors.New("invalid arguments") 11 | 12 | // ErrInvalidResponse is returned when the server returns a syntactically-invalid response. 13 | var ErrInvalidResponse = errors.New("invalid response") 14 | 15 | // ErrUnexpectedResponse is returned when the server returns a syntactically-valid but unexpected response. 16 | var ErrUnexpectedResponse = errors.New("unexpected response") 17 | -------------------------------------------------------------------------------- /integration_tests/.template/schema.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's #{MODULE_NAME} module 2 | # Registers zgrab2-#{MODULE_NAME} globally, and #{MODULE_NAME} with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | import zgrab2 9 | 10 | # {MODULE_NAME}_scan_response = SubRecord({ 11 | # "result": SubRecord({ 12 | # # TODO FIXME IMPLEMENT SCHEMA 13 | # }) 14 | # }, extends=zgrab2.base_scan_response) 15 | 16 | # zschema.registry.register_schema("zgrab2-#{MODULE_NAME}", #{MODULE_NAME}_scan_response) 17 | 18 | # zgrab2.register_scan_response_type("#{MODULE_NAME}", #{MODULE_NAME}_scan_response) 19 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/pptp.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's PPTP module 2 | # Registers zgrab2-pptp globally, and pptp with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | from . import zgrab2 8 | 9 | # Schema for ScanResults struct 10 | pptp_scan_response = SubRecord( 11 | { 12 | "banner": String(), 13 | "control_message": String(), 14 | } 15 | ) 16 | 17 | pptp_scan = SubRecord( 18 | { 19 | "result": pptp_scan_response, 20 | }, 21 | extends=zgrab2.base_scan_response, 22 | ) 23 | 24 | zschema.registry.register_schema("zgrab2-pptp", pptp_scan) 25 | zgrab2.register_scan_response_type("pptp", pptp_scan) 26 | -------------------------------------------------------------------------------- /integration_tests/ssh/container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_service_base:latest 2 | 3 | # Adapted from https://docs.docker.com/engine/examples/running_ssh_service/#run-a-test_sshd-container 4 | 5 | RUN apt-get install -y openssh-server 6 | RUN mkdir /var/run/sshd 7 | RUN echo 'root:password' | chpasswd 8 | RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config 9 | 10 | # SSH login fix. Otherwise user is kicked off after login 11 | RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd 12 | 13 | ENV NOTVISIBLE "in users profile" 14 | RUN echo "export VISIBLE=now" >> /etc/profile 15 | 16 | CMD while true; do /usr/sbin/sshd -d -D || echo "sshd exited: $?"; done 17 | -------------------------------------------------------------------------------- /lib/http/example_handle_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "sync" 11 | 12 | "github.com/zmap/zgrab2/lib/http" 13 | ) 14 | 15 | type countHandler struct { 16 | mu sync.Mutex // guards n 17 | n int 18 | } 19 | 20 | func (h *countHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 21 | h.mu.Lock() 22 | defer h.mu.Unlock() 23 | h.n++ 24 | fmt.Fprintf(w, "count is %d\n", h.n) 25 | } 26 | 27 | func ExampleHandle() { 28 | http.Handle("/count", new(countHandler)) 29 | log.Fatal(http.ListenAndServe(":8080", nil)) 30 | } 31 | -------------------------------------------------------------------------------- /integration_tests/ntp/container-4.2.6/Dockerfile: -------------------------------------------------------------------------------- 1 | # Must use amd64 image so this builds on Mac. 2 | # Unfortunately, the ./configure script below doesn't have ARM support 3 | FROM --platform=linux/amd64 ubuntu:24.04 4 | RUN apt-get update 5 | 6 | # This image grabs the 4.2.6p5 source release of NTP and builds it. 7 | 8 | RUN apt-get install -y libssl-dev gcc g++ make binutils autoconf tar wget 9 | 10 | WORKDIR /opt 11 | RUN wget https://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-4.2.6p5.tar.gz 12 | RUN tar -xzf ntp-4.2.6p5.tar.gz 13 | 14 | WORKDIR /opt/ntp-4.2.6p5 15 | RUN ./configure 16 | RUN make 17 | 18 | # Don't require authentication, don't fork, debug level 10 19 | ENTRYPOINT [ "/opt/ntp-4.2.6p5/ntpd/ntpd", "-A", "-n", "-D", "10" ] 20 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | http1-plaintext: 3 | image: nginx:alpine 4 | volumes: 5 | - ./http1/nginx.conf:/etc/nginx/nginx.conf:ro 6 | - ./http1/index.html:/usr/share/nginx/html/index.html:ro 7 | ports: 8 | - "8081:80" 9 | 10 | http2-only-https: 11 | build: 12 | context: ./http2-only-https 13 | ports: 14 | - "8082:443" 15 | expose: 16 | - "443" 17 | http-both-versions-https: 18 | build: 19 | context: ./http-both-versions-https 20 | ports: 21 | - "8083:443" 22 | expose: 23 | - "443" 24 | h2c: 25 | build: 26 | context: ./h2c 27 | ports: 28 | - "8084:443/udp" 29 | - "8084:443/tcp" 30 | 31 | -------------------------------------------------------------------------------- /integration_tests/ssh/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | MODULE_DIR=$(dirname $0) 5 | TEST_ROOT=$MODULE_DIR/.. 6 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 7 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 8 | 9 | CONTAINER_NAME="zgrab_ssh" 10 | 11 | # Run the SSH-specific integration tests: 12 | # 1. Run zgrab2 on the container 13 | 14 | mkdir -p $ZGRAB_OUTPUT/ssh 15 | 16 | OUTPUT_FILE="$ZGRAB_OUTPUT/ssh/ssh.json" 17 | 18 | echo "ssh/test: Testing SSH Version on $CONTAINER_NAME..." 19 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh ssh > $OUTPUT_FILE 20 | 21 | echo "ssh/test: BEGIN docker logs from $CONTAINER_NAME [{(" 22 | docker logs --tail all $CONTAINER_NAME 23 | echo ")}] END docker logs from $CONTAINER_NAME" 24 | -------------------------------------------------------------------------------- /integration_tests/telnet/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | MODULE_DIR=$(dirname $0) 5 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 6 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 7 | 8 | mkdir -p $ZGRAB_OUTPUT/telnet 9 | 10 | CONTAINER_NAME=zgrab_telnet 11 | 12 | OUTPUT_FILE=$ZGRAB_OUTPUT/telnet/telnet.json 13 | 14 | echo "telnet/test: Tests runner for telnet" 15 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh telnet > $OUTPUT_FILE 16 | 17 | # Dump the docker logs 18 | echo "telnet/test: BEGIN docker logs from $CONTAINER_NAME [{(" 19 | docker logs --tail all $CONTAINER_NAME 20 | echo ")}] END docker logs from $CONTAINER_NAME" 21 | 22 | # TODO: If there are any other relevant log files, dump those to stdout here. 23 | -------------------------------------------------------------------------------- /integration_tests/.template/container/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "ERROR: ***" 4 | echo "ERROR: This container's entrypoint must be customized." 5 | echo "ERROR: See integration_tests/#{MODULE_NAME}/container/*" 6 | echo "FIXME: ***" 7 | exit 1 8 | 9 | # This should do the same work as the init.d start script, but the 10 | # process should run in the foreground. 11 | # In some cases, it may be appropriate to exit (and hence stop the 12 | # container) if the daemon process ends, but in others that may be 13 | # expected behavior. 14 | 15 | set -x 16 | 17 | while true; do 18 | if ! /usr/sbin/FIXME_YOUR_SERVICED --fixme-service-options; then 19 | echo "FIXME_YOUR_SERVICED exited unexpectedly. Restarting..." 20 | sleep 1 21 | fi 22 | done 23 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/dnp3.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's dnp3 module 2 | # Registers zgrab2-dnp3 globally, and dnp3 with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | dnp3_scan_response = SubRecord( 11 | { 12 | "result": SubRecord( 13 | { 14 | "is_dnp3": Boolean(), 15 | "raw_response": Binary(), 16 | } 17 | ) 18 | }, 19 | extends=zgrab2.base_scan_response, 20 | ) 21 | 22 | zschema.registry.register_schema("zgrab2-dnp3", dnp3_scan_response) 23 | 24 | zgrab2.register_scan_response_type("dnp3", dnp3_scan_response) 25 | -------------------------------------------------------------------------------- /integration_tests/pptp/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | MODULE_DIR=$(dirname $0) 5 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 6 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 7 | 8 | mkdir -p $ZGRAB_OUTPUT/pptp 9 | 10 | CONTAINER_NAME=zgrab_pptp 11 | 12 | OUTPUT_FILE=$ZGRAB_OUTPUT/pptp/pptp.json 13 | 14 | echo "pptp/test: Tests runner for pptp" 15 | # TODO FIXME: Add any necessary flags or additional tests 16 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh pptp > $OUTPUT_FILE 17 | 18 | # Dump the docker logs 19 | echo "pptp/test: BEGIN docker logs from $CONTAINER_NAME [{(" 20 | docker logs --tail all $CONTAINER_NAME 21 | echo ")}] END docker logs from $CONTAINER_NAME" 22 | 23 | # TODO: If there are any other relevant log files, dump those to stdout here. 24 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/__init__.py: -------------------------------------------------------------------------------- 1 | # Ensure that all of the modules get executed so that they are registered 2 | from . import bacnet 3 | from . import dnp3 4 | from . import fox 5 | from . import ftp 6 | from . import http 7 | from . import managesieve 8 | from . import memcached 9 | from . import modbus 10 | from . import mongodb 11 | from . import mssql 12 | from . import mysql 13 | from . import mysql_errors 14 | from . import ntp 15 | from . import oracle 16 | from . import pop3 17 | from . import postgres 18 | from . import redis 19 | from . import siemens 20 | from . import smb 21 | from . import smtp 22 | from . import ssh 23 | from . import telnet 24 | from . import ipp 25 | from . import banner 26 | from . import amqp091 27 | from . import socks5 28 | from . import mqtt 29 | from . import pptp 30 | -------------------------------------------------------------------------------- /integration_tests/socks5/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | MODULE_DIR=$(dirname $0) 5 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 6 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 7 | 8 | mkdir -p $ZGRAB_OUTPUT/socks5 9 | 10 | CONTAINER_NAME=zgrab_socks5 11 | 12 | OUTPUT_FILE=$ZGRAB_OUTPUT/socks5/socks5.json 13 | 14 | echo "socks5/test: Tests runner for socks5" 15 | # TODO FIXME: Add any necessary flags or additional tests 16 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh socks5 > $OUTPUT_FILE 17 | 18 | # Dump the docker logs 19 | echo "socks5/test: BEGIN docker logs from $CONTAINER_NAME [{(" 20 | docker logs --tail all $CONTAINER_NAME 21 | echo ")}] END docker logs from $CONTAINER_NAME" 22 | 23 | # TODO: If there are any other relevant log files, dump those to stdout here. 24 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-renamed-inline-extra.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: ERR unknown command `PING`, with args beginning with: )","info_response":"(Error: ERR unknown command `INFO`, with args beginning with: )","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","custom_responses":[{"command":"PING","response":"(Error: ERR unknown command `PING`, with args beginning with: )"},{"command":"AUTH","arguments":"password!","response":"(Error: ERR Client sent AUTH, but no password is set)"},{"command":"YOUR-COMMAND","arguments":"args","response":"(Error: ERR unknown command `YOUR-COMMAND`, with args beginning with: `args`, )"}],"quit_response":"OK"},"timestamp":"2019-06-20T21:13:04Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/redis-renamed-normal-extra.json: -------------------------------------------------------------------------------- 1 | {"domain":"target","data":{"redis":{"status":"success","protocol":"redis","result":{"ping_response":"(Error: ERR unknown command `PING`, with args beginning with: )","info_response":"(Error: ERR unknown command `INFO`, with args beginning with: )","nonexistent_response":"(Error: ERR unknown command `NONEXISTENT`, with args beginning with: )","custom_responses":[{"command":"PING","response":"(Error: ERR unknown command `PING`, with args beginning with: )"},{"command":"AUTH","arguments":"password!","response":"(Error: ERR Client sent AUTH, but no password is set)"},{"command":"YOUR-COMMAND","arguments":"args","response":"(Error: ERR unknown command `YOUR-COMMAND`, with args beginning with: `args`, )"}],"quit_response":"OK"},"timestamp":"2019-06-20T21:12:59Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/postgres/container/setup_ssl.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | set -x 4 | WORKDIR=/tmp/postgres_setup 5 | 6 | # Generate a self-signed key/certificate 7 | openssl req -new -x509 -nodes -keyout $WORKDIR/server.p8 -out $WORKDIR/server.crt -subj "/CN=localhost" 8 | 9 | # Get the private key in passwordless PEM format for use by postgres 10 | openssl rsa -in $WORKDIR/server.p8 -out $WORKDIR/server.key 11 | 12 | chown postgres:postgres $WORKDIR/server.* 13 | chmod 0600 $WORKDIR/server.key 14 | chmod 0644 $WORKDIR/server.crt 15 | 16 | cp $WORKDIR/server.key $PGDATA 17 | cp $WORKDIR/server.crt $PGDATA 18 | 19 | echo "" >> $PGDATA/postgresql.conf 20 | 21 | # Attach the SSL + Logging config to the end of the main postgresql.conf file 22 | cat $WORKDIR/postgresql.conf.partial >> $PGDATA/postgresql.conf 23 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/mqtt.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's MQTT module 2 | # Registers zgrab2-mqtt globally, and mqtt with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | from . import zgrab2 8 | 9 | # Schema for ScanResults struct 10 | mqtt_scan_response = SubRecord( 11 | { 12 | "session_present": Boolean(), 13 | "connect_return_code": Unsigned32BitInteger(), 14 | "response": String(), 15 | "tls": zgrab2.tls_log, 16 | } 17 | ) 18 | 19 | mqtt_scan = SubRecord( 20 | { 21 | "result": mqtt_scan_response, 22 | }, 23 | extends=zgrab2.base_scan_response, 24 | ) 25 | 26 | zschema.registry.register_schema("zgrab2-mqtt", mqtt_scan) 27 | zgrab2.register_scan_response_type("mqtt", mqtt_scan) 28 | -------------------------------------------------------------------------------- /integration_tests/smtp/smtps/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_smtp_service_base:latest 2 | 3 | RUN apt-get install -y \ 4 | openssl 5 | RUN rm -rf /var/lib/apt/lists/* 6 | 7 | # Add Certs 8 | RUN mkdir -p /etc/ssl/certs; \ 9 | mkdir -p /etc/ssl/private; 10 | RUN openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ 11 | -keyout /etc/ssl/private/postfix.key \ 12 | -out /etc/ssl/certs/postfix.crt \ 13 | -subj "/C=US/ST=State/L=City/O=MyCompany/CN=mail.example.com" 14 | 15 | # Enable TLS 16 | RUN echo "smtpd_tls_cert_file = /etc/ssl/certs/postfix.crt" >> /etc/postfix/main.cf; \ 17 | echo "smtpd_tls_key_file = /etc/ssl/private/postfix.key" >> /etc/postfix/main.cf; \ 18 | echo "smtpd_tls_wrappermode = yes" >> /etc/postfix/main.cf 19 | 20 | # Command to run postfix (in the foreground) 21 | CMD ["postfix", "start-fg"] 22 | -------------------------------------------------------------------------------- /integration_tests/mongodb/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | versions="3.2.20 3.6.6 4.0.1 4.1.2" 6 | 7 | MODULE_DIR=$(dirname $0) 8 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 9 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 10 | 11 | mkdir -p $ZGRAB_OUTPUT/mongodb 12 | 13 | 14 | echo "mongodb/test: Tests runner for mongodb" 15 | 16 | for version in $versions; do 17 | CONTAINER_NAME=zgrab_mongodb-${version} 18 | echo "mongodb/test: Testing $CONTAINER_NAME" 19 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh mongodb > "$ZGRAB_OUTPUT/mongodb/${version}-normal.json" 20 | # Dump the docker logs 21 | echo "#{MODULE_NAME}/test: BEGIN docker logs from $CONTAINER_NAME [{(" 22 | docker logs --tail all $CONTAINER_NAME 23 | echo ")}] END docker logs from $CONTAINER_NAME" 24 | done 25 | 26 | -------------------------------------------------------------------------------- /modules/smtp/metadata.go: -------------------------------------------------------------------------------- 1 | package smtp 2 | 3 | import "sync" 4 | 5 | type Metadata struct { 6 | sync.Mutex 7 | HostsSupportingEHLO uint `json:"hosts_supporting_ehlo"` 8 | HostsSupportingHELO uint `json:"hosts_supporting_helo"` 9 | HostsSupportingSTARTTLS uint `json:"hosts_supporting_starttls"` 10 | } 11 | 12 | var moduleMetadata Metadata 13 | 14 | func init() { 15 | moduleMetadata = Metadata{} 16 | moduleMetadata.Mutex = sync.Mutex{} 17 | } 18 | 19 | func (m *Metadata) incrementHostsSupportingEHLO() { 20 | m.Lock() 21 | defer m.Unlock() 22 | m.HostsSupportingEHLO++ 23 | } 24 | 25 | func (m *Metadata) incrementHostsSupportingHELO() { 26 | m.Lock() 27 | defer m.Unlock() 28 | m.HostsSupportingHELO++ 29 | } 30 | 31 | func (m *Metadata) incrementHostsSupportingSTARTTLS() { 32 | m.Lock() 33 | defer m.Unlock() 34 | m.HostsSupportingSTARTTLS++ 35 | } 36 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/banner.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's banner module 2 | # Registers zgrab2-banner globally, and banner with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | # modules/banner/scanner.go - Results 11 | banner_scan_response = SubRecord( 12 | { 13 | "result": SubRecord( 14 | { 15 | "banner": String(), 16 | "length": Unsigned32BitInteger(), 17 | "tls": zgrab2.tls_log, 18 | } 19 | ) 20 | }, 21 | extends=zgrab2.base_scan_response, 22 | ) 23 | 24 | zschema.registry.register_schema("zgrab2-banner", banner_scan_response) 25 | 26 | zgrab2.register_scan_response_type("banner", banner_scan_response) 27 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/ftp.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's ftp module 2 | # Registers zgrab2-ftp globally, and ftp with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | # modules/ftp.go - FTPScanResults 11 | ftp_scan_response = SubRecord( 12 | { 13 | "result": SubRecord( 14 | { 15 | "tls": zgrab2.tls_log, 16 | "banner": String(), 17 | "auth_tls": String(), 18 | "auth_ssl": String(), 19 | } 20 | ) 21 | }, 22 | extends=zgrab2.base_scan_response, 23 | ) 24 | 25 | zschema.registry.register_schema("zgrab2-ftp", ftp_scan_response) 26 | 27 | zgrab2.register_scan_response_type("ftp", ftp_scan_response) 28 | -------------------------------------------------------------------------------- /integration_tests/.template/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | MODULE_DIR=$(dirname $0) 5 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 6 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 7 | 8 | mkdir -p $ZGRAB_OUTPUT/#{MODULE_NAME} 9 | 10 | CONTAINER_NAME=zgrab_#{MODULE_NAME} 11 | 12 | OUTPUT_FILE=$ZGRAB_OUTPUT/#{MODULE_NAME}/#{MODULE_NAME}.json 13 | 14 | echo "#{MODULE_NAME}/test: Tests runner for #{MODULE_NAME}" 15 | # TODO FIXME: Add any necessary flags or additional tests 16 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh #{MODULE_NAME} > $OUTPUT_FILE 17 | 18 | # Dump the docker logs 19 | echo "#{MODULE_NAME}/test: BEGIN docker logs from $CONTAINER_NAME [{(" 20 | docker logs --tail all $CONTAINER_NAME 21 | echo ")}] END docker logs from $CONTAINER_NAME" 22 | 23 | # TODO: If there are any other relevant log files, dump those to stdout here. 24 | -------------------------------------------------------------------------------- /integration_tests/ftp/container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_service_base:latest 2 | 3 | RUN apt-get install -y vsftpd 4 | # This comes pre-configured to use a pre-generated certificate at /etc/ssl/certs/ssl-cert-snakeoil.pem, 5 | # but by default ssl_enable=NO. So, changing that to YES and (re)starting the service should be all we need to do. 6 | RUN sed -i 's/ssl_enable=NO/ssl_enable=YES/g' /etc/vsftpd.conf 7 | RUN sed -i 's/#xferlog_std_format=YES/xferlog_std_format=NO/g' /etc/vsftpd.conf 8 | RUN echo 'log_ftp_protocol=YES' >> /etc/vsftpd.conf 9 | RUN echo 'ssl_ciphers=HIGH' >> /etc/vsftpd.conf 10 | 11 | # Try to make it act more container-y -- remove it from init.d and just run the daemon as the entrypoint 12 | RUN service vsftpd stop 13 | RUN update-rc.d -f vsftpd remove 14 | 15 | WORKDIR / 16 | COPY entrypoint.sh . 17 | RUN chmod a+x ./entrypoint.sh 18 | 19 | ENTRYPOINT ["/entrypoint.sh"] 20 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/h2c/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | 8 | "golang.org/x/net/http2" 9 | "golang.org/x/net/http2/h2c" 10 | ) 11 | 12 | func main() { 13 | handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 14 | expectedProtocol := "HTTP/2.0" 15 | if r.Proto != expectedProtocol { 16 | http.Error(w, fmt.Sprintf("expected protocol %s, got %s", expectedProtocol, r.Proto), http.StatusHTTPVersionNotSupported) 17 | return 18 | } 19 | _, err := fmt.Fprint(w, "Successfully served over HTTP/2 NOT over TLS!\n") 20 | if err != nil { 21 | http.Error(w, err.Error(), http.StatusInternalServerError) 22 | } 23 | }) 24 | h2s := &http2.Server{} 25 | h1s := &http.Server{ 26 | Addr: ":443", 27 | Handler: h2c.NewHandler(handler, h2s), 28 | } 29 | log.Fatal(h1s.ListenAndServe()) 30 | } 31 | -------------------------------------------------------------------------------- /lib/http/httptrace/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package httptrace_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "github.com/zmap/zgrab2/lib/http" 12 | "github.com/zmap/zgrab2/lib/http/httptrace" 13 | ) 14 | 15 | func Example() { 16 | req, _ := http.NewRequest("GET", "http://example.com", nil) 17 | trace := &httptrace.ClientTrace{ 18 | GotConn: func(connInfo httptrace.GotConnInfo) { 19 | fmt.Printf("Got Conn: %+v\n", connInfo) 20 | }, 21 | DNSDone: func(dnsInfo httptrace.DNSDoneInfo) { 22 | fmt.Printf("DNS Info: %+v\n", dnsInfo) 23 | }, 24 | } 25 | req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) 26 | _, err := http.DefaultTransport.RoundTrip(req) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/http2/gotrack_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func TestGoroutineLock(t *testing.T) { 14 | oldDebug := DebugGoroutines 15 | DebugGoroutines = true 16 | defer func() { DebugGoroutines = oldDebug }() 17 | 18 | g := newGoroutineLock() 19 | g.check() 20 | 21 | sawPanic := make(chan interface{}) 22 | go func() { 23 | defer func() { sawPanic <- recover() }() 24 | g.check() // should panic 25 | }() 26 | e := <-sawPanic 27 | if e == nil { 28 | t.Fatal("did not see panic from check in other goroutine") 29 | } 30 | if !strings.Contains(fmt.Sprint(e), "wrong goroutine") { 31 | t.Errorf("expected on see panic about running on the wrong goroutine; got %v", e) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /integration_tests/ntp/container-openntp/ntpd.conf: -------------------------------------------------------------------------------- 1 | # $OpenBSD: ntpd.conf,v 1.2 2015/02/10 06:40:08 reyk Exp $ 2 | # sample ntpd configuration file, see ntpd.conf(5). Aside from this comment, un-commenting line 5 is the only change from the default. 3 | 4 | # Addresses to listen on (ntpd does not listen by default) 5 | listen on * 6 | # listen on 127.0.0.1 7 | # listen on ::1 8 | 9 | # sync to a single server 10 | #server ntp.example.org 11 | 12 | # use a random selection of NTP Pool Time Servers 13 | # see http://support.ntp.org/bin/view/Servers/NTPPoolServers 14 | #servers pool.ntp.org 15 | 16 | # Choose servers announced from Debian NTP Pool 17 | servers 0.debian.pool.ntp.org 18 | servers 1.debian.pool.ntp.org 19 | servers 2.debian.pool.ntp.org 20 | servers 3.debian.pool.ntp.org 21 | 22 | # use a specific local timedelta sensor (radio clock, etc) 23 | #sensor nmea0 24 | 25 | # use all detected timedelta sensors 26 | #sensor * 27 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/http-http.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"http":{"status":"success","protocol":"http","result":{"response":{"status_line":"200 OK","status_code":200,"protocol":{"name":"HTTP/1.1","major":1,"minor":1},"headers":{"accept_ranges":["bytes"],"content_length":["36"],"content_type":["text/html"],"last_modified":["Fri, 09 Feb 2018 20:18:50 GMT"],"server":["lighttpd/1.4.35"],"unknown":[{"key":"etag","value":["\"2262550883\""]},{"key":"date","value":["Fri, 06 Apr 2018 19:38:09 GMT"]}]},"body":"\u003chtml\u003e\u003cbody\u003eHTTP INDEX\u003c/body\u003e\u003c/html\u003e","body_sha256":"c11fdc020dbb3150da741f51dcf0423c5ce93a798e91bd5c1957ea7299f4e103","content_length":36,"request":{"url":{"scheme":"http","host":"target","path":"/"},"method":"GET","headers":{"unknown":[{"key":"accept","value":["*/*"]},{"key":"user_agent","value":["Mozilla/5.0 zgrab/0.x"]}]},"host":"target"}}},"timestamp":"2018-04-06T19:38:09Z"}}} 2 | -------------------------------------------------------------------------------- /lib/ssh/test/banner_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd 6 | // +build aix darwin dragonfly freebsd linux netbsd openbsd 7 | 8 | package test 9 | 10 | import ( 11 | "testing" 12 | ) 13 | 14 | func TestBannerCallbackAgainstOpenSSH(t *testing.T) { 15 | server := newServer(t) 16 | defer server.Shutdown() 17 | 18 | clientConf := clientConfig() 19 | 20 | var receivedBanner string 21 | clientConf.BannerCallback = func(message string) error { 22 | receivedBanner = message 23 | return nil 24 | } 25 | 26 | conn := server.Dial(clientConf) 27 | defer conn.Close() 28 | 29 | expected := "Server Banner" 30 | if receivedBanner != expected { 31 | t.Fatalf("got %v; want %v", receivedBanner, expected) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /integration_tests/http/http_smoke_test/container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zgrab2_service_base:latest 2 | RUN apt-get install -y openssl lighttpd curl 3 | 4 | WORKDIR /etc/lighttpd 5 | COPY lighttpd.conf . 6 | 7 | WORKDIR /var/lighttpd/certs 8 | # TODO: use container name for host? 9 | RUN openssl req -new -x509 -subj "/CN=target" -nodes -keyout ssl.key -out ssl.cer 10 | RUN cat ssl.key ssl.cer > ssl.pem 11 | 12 | WORKDIR /var/lighttpd/htdocs/http 13 | COPY index-http.html index.html 14 | COPY favicon.ico favicon.ico 15 | RUN curl https://gist.githubusercontent.com/phillip-stephens/3f1a8d2874b4ff33e4fc46035810b7f9/raw/5bd8ed7fb1b923607c26807ea8ea0643825e6e16/index-very-large-http.html > large.html 16 | 17 | COPY index-redirect.html index-redirect.html 18 | COPY index-redirect-2.html index-redirect-2.html 19 | 20 | WORKDIR /var/lighttpd/htdocs/https 21 | COPY index-https.html index.html 22 | 23 | ENTRYPOINT ["lighttpd", "-f", "/etc/lighttpd/lighttpd.conf", "-D"] 24 | -------------------------------------------------------------------------------- /modules/siemens/common.go: -------------------------------------------------------------------------------- 1 | package siemens 2 | 3 | import "errors" 4 | 5 | var ( 6 | errS7PacketTooShort = errors.New("s7 packet too short") 7 | errInvalidPacket = errors.New("invalid S7 packet") 8 | errNotS7 = errors.New("not a S7 packet") 9 | ) 10 | 11 | // S7Error provides an interface to get S7 errors. 12 | type S7Error struct{} 13 | 14 | var ( 15 | // S7_ERROR_CODES maps error codes to the friendly error string 16 | S7_ERROR_CODES = map[uint32]string{ 17 | // s7 data errors 18 | 0x05: "address error", 19 | 0x0a: "item not available", 20 | // s7 header errors 21 | 0x8104: "context not supported", 22 | 0x8500: "wrong PDU size", 23 | } 24 | ) 25 | 26 | // New gets an S7 error instance for the given error code. 27 | // TODO: Shouldn't it be sharing a single error instance, rather than returning a new error instance each time? 28 | func (s7Error *S7Error) New(errorCode uint32) error { 29 | return errors.New(S7_ERROR_CODES[errorCode]) 30 | } 31 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/ntp-4.2.6_monlist.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.4","domain":"target","data":{"ntp":{"status":"success","protocol":"ntp","result":{"version":3,"time":"2018-04-06T19:39:48.515473906Z","time_response":{"leap_indicator":3,"version":3,"mode":4,"stratum":0,"poll":3,"precision":-22,"root_delay":{"seconds":0,"fraction":0},"root_dispersion":{"seconds":0,"fraction":10},"reference_id":"SU5JVA==","reference_timestamp":{"seconds":0,"fraction":0},"origin_timestamp":{"seconds":0,"fraction":0},"receive_timestamp":{"seconds":3732032388,"fraction":2213943570},"transmit_timestamp":{"seconds":3732032388,"fraction":2215576491}},"monlist_response":"AAAAAQAAAAAAAAAAAAAAAqwRAAWpLQMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","monlist_header":{"is_response":true,"has_more":false,"version":3,"mode":7,"is_authenticated":false,"sequence_number":0,"implementation_number":"IMPL_XNTPD","request_code":"REQ_MON_GETLIST","error":"INFO_OKAY","num_items":1,"mbz":0,"item_size":48}},"timestamp":"2018-04-06T19:39:48Z"}}} 2 | -------------------------------------------------------------------------------- /lib/http/h2_error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !nethttpomithttp2 6 | 7 | package http 8 | 9 | import ( 10 | "reflect" 11 | ) 12 | 13 | func (e http2StreamError) As(target any) bool { 14 | dst := reflect.ValueOf(target).Elem() 15 | dstType := dst.Type() 16 | if dstType.Kind() != reflect.Struct { 17 | return false 18 | } 19 | src := reflect.ValueOf(e) 20 | srcType := src.Type() 21 | numField := srcType.NumField() 22 | if dstType.NumField() != numField { 23 | return false 24 | } 25 | for i := 0; i < numField; i++ { 26 | sf := srcType.Field(i) 27 | df := dstType.Field(i) 28 | if sf.Name != df.Name || !sf.Type.ConvertibleTo(df.Type) { 29 | return false 30 | } 31 | } 32 | for i := 0; i < numField; i++ { 33 | df := dst.Field(i) 34 | df.Set(src.Field(i).Convert(df.Type())) 35 | } 36 | return true 37 | } 38 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/ntp-4.2.6_REQ_MON_GETLIST.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.4","domain":"target","data":{"ntp":{"status":"success","protocol":"ntp","result":{"version":3,"time":"2018-04-06T19:39:54.40363944Z","time_response":{"leap_indicator":3,"version":3,"mode":4,"stratum":0,"poll":3,"precision":-22,"root_delay":{"seconds":0,"fraction":0},"root_dispersion":{"seconds":0,"fraction":16},"reference_id":"SU5JVA==","reference_timestamp":{"seconds":0,"fraction":0},"origin_timestamp":{"seconds":0,"fraction":0},"receive_timestamp":{"seconds":3732032394,"fraction":1733618196},"transmit_timestamp":{"seconds":3732032394,"fraction":1738320156}},"monlist_response":"AAAAAgAAAAAAAAAAAAAABKwRAAWlHQMDAAAAAJTbAwMAAAAAAAAAAAAAAAAAAAAA","monlist_header":{"is_response":true,"has_more":false,"version":3,"mode":7,"is_authenticated":false,"sequence_number":0,"implementation_number":"IMPL_XNTPD","request_code":"REQ_MON_GETLIST","error":"INFO_OKAY","num_items":1,"mbz":0,"item_size":48}},"timestamp":"2018-04-06T19:39:54Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/smtp.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's smtp module 2 | # Registers zgrab2-smtp globally, and smtp with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | smtp_scan_response = SubRecord( 11 | { 12 | "result": SubRecord( 13 | { 14 | "banner": String(), 15 | "ehlo": String(), 16 | "helo": String(), 17 | "help": String(), 18 | "starttls": String(), 19 | "quit": String(), 20 | "implicit_tls": Boolean(), 21 | "tls": zgrab2.tls_log, 22 | } 23 | ) 24 | }, 25 | extends=zgrab2.base_scan_response, 26 | ) 27 | 28 | zschema.registry.register_schema("zgrab2-smtp", smtp_scan_response) 29 | 30 | zgrab2.register_scan_response_type("smtp", smtp_scan_response) 31 | -------------------------------------------------------------------------------- /integration_tests/postgres/container/Dockerfile: -------------------------------------------------------------------------------- 1 | # default Dockerfile for >9.3 2 | ARG POSTGRES_VERSION=10.1 3 | FROM postgres:${POSTGRES_VERSION} 4 | 5 | # Our custom postgres image -- based on the standard images (https://hub.docker.com/_/postgres/) 6 | # On top of the base, we enable logging and (if IMAGE_TYPE == ssl) server-side SSL (with a self-signed certificate) 7 | ARG IMAGE_TYPE=ssl 8 | 9 | WORKDIR /tmp/postgres_setup 10 | # This gets catted to the end of $PGDATA/postgresql.conf 11 | COPY postgresql.conf.$IMAGE_TYPE.partial postgresql.conf.partial 12 | # The docker-entrypoint-initdb.d scripts get run after postgres is installed but before it is started. 13 | COPY setup_$IMAGE_TYPE.sh /docker-entrypoint-initdb.d/setup_$IMAGE_TYPE.sh 14 | 15 | RUN chown -R postgres:postgres . 16 | RUN chown postgres:postgres /docker-entrypoint-initdb.d/setup_$IMAGE_TYPE.sh 17 | 18 | RUN chmod 0755 . 19 | RUN chmod 0644 postgresql.conf.partial 20 | RUN chmod 0755 /docker-entrypoint-initdb.d/setup_$IMAGE_TYPE.sh 21 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/imap.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's imap module 2 | # Registers zgrab2-imap globally, and imap with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | imap_scan_response = SubRecord( 11 | { 12 | "result": SubRecord( 13 | { 14 | "banner": String(doc="The IMAP banner."), 15 | "starttls": String( 16 | doc="The server's response to the STARTTLS command." 17 | ), 18 | "close": String(doc="The server's response to the CLOSE command."), 19 | "tls": zgrab2.tls_log, 20 | } 21 | ) 22 | }, 23 | extends=zgrab2.base_scan_response, 24 | ) 25 | 26 | zschema.registry.register_schema("zgrab2-imap", imap_scan_response) 27 | 28 | zgrab2.register_scan_response_type("imap", imap_scan_response) 29 | -------------------------------------------------------------------------------- /integration_tests/.template/README.md: -------------------------------------------------------------------------------- 1 | Template integration test scripts 2 | 3 | Specify your module's service in the `docker-compose.yml` file. 4 | 5 | - All scripts start with the default `CONTAINER_NAME` variable matching 6 | the standard format (`zgrab_`). 7 | 8 | - `test.sh` runs the container using the `docker-runner.sh` script and 9 | dumps the output in `zgrab-output//.json`. 10 | Afterwards it dumps the container logs to stdout. This can be used 11 | as-is, it only provides a single test case with no arguments. 12 | 13 | - `schema.py` registers the `scan_response_type` and provides a 14 | skeleton that can be filled out. **This must be modified for all new 15 | modules**. 16 | 17 | These files have `#{MODULE_NAME}` statically replaced with the module's 18 | name (the argument passed to `new.sh`). 19 | 20 | - `*.sh` get copied to `integration_tests//*.sh` 21 | 22 | - `schema.py` gets copied to `schemas/.py` 23 | -------------------------------------------------------------------------------- /multiple.go: -------------------------------------------------------------------------------- 1 | package zgrab2 2 | 3 | import "errors" 4 | 5 | // MultipleCommand contains the command line options for running 6 | type MultipleCommand struct { 7 | ConfigFileName string `short:"c" long:"config-file" default:"-" description:"Config filename, use - for stdin"` 8 | ContinueOnError bool `long:"continue-on-error" description:"If proceeding protocols error, do not run following protocols (default: true)"` 9 | BreakOnSuccess bool `long:"break-on-success" description:"If proceeding protocols succeed, do not run following protocols (default: false)"` 10 | } 11 | 12 | // Validate the options sent to MultipleCommand 13 | func (x *MultipleCommand) Validate(_ []string) error { 14 | if x.ConfigFileName == config.InputFileName { 15 | return errors.New("cannot receive config file and input file from same source") 16 | } 17 | 18 | return nil 19 | } 20 | 21 | // Help returns a usage string that will be output at the command line 22 | func (x *MultipleCommand) Help() string { 23 | return "" 24 | } 25 | -------------------------------------------------------------------------------- /module_set.go: -------------------------------------------------------------------------------- 1 | package zgrab2 2 | 3 | // ModuleSet is a container holding named scan modules. It is a wrapper around a 4 | // map. 5 | type ModuleSet map[string]ScanModule 6 | 7 | // AddModule adds m to the ModuleSet, accessible via the given name. If the name 8 | // is already in the ModuleSet, it is overwritten. 9 | func (s ModuleSet) AddModule(name string, m ScanModule) { 10 | s[name] = m 11 | } 12 | 13 | // RemoveModule removes the module at the specified name. If the name is not in 14 | // the module set, nothing happens. 15 | func (s ModuleSet) RemoveModule(name string) { 16 | delete(s, name) 17 | } 18 | 19 | // CopyInto copies the modules in s to destination. The sets will be unique, but 20 | // the underlying ScanModule instances will be the same. 21 | func (s ModuleSet) CopyInto(destination ModuleSet) { 22 | for name, m := range s { 23 | destination[name] = m 24 | } 25 | } 26 | 27 | // NewModuleSet returns an empty ModuleSet. 28 | func NewModuleSet() ModuleSet { 29 | return make(ModuleSet) 30 | } 31 | -------------------------------------------------------------------------------- /lib/http/jar.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http 6 | 7 | import ( 8 | "net/url" 9 | ) 10 | 11 | // A CookieJar manages storage and use of cookies in HTTP requests. 12 | // 13 | // Implementations of CookieJar must be safe for concurrent use by multiple 14 | // goroutines. 15 | // 16 | // The net/http/cookiejar package provides a CookieJar implementation. 17 | type CookieJar interface { 18 | // SetCookies handles the receipt of the cookies in a reply for the 19 | // given URL. It may or may not choose to save the cookies, depending 20 | // on the jar's policy and implementation. 21 | SetCookies(u *url.URL, cookies []*Cookie) 22 | 23 | // Cookies returns the cookies to send in a request for the given URL. 24 | // It is up to the implementation to honor the standard cookie use 25 | // restrictions such as in RFC 6265. 26 | Cookies(u *url.URL) []*Cookie 27 | } 28 | -------------------------------------------------------------------------------- /lib/ssh/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package ssh implements an SSH client and server. 7 | 8 | SSH is a transport security protocol, an authentication protocol and a 9 | family of application protocols. The most typical application level 10 | protocol is a remote shell and this is specifically implemented. However, 11 | the multiplexed nature of SSH is exposed to users that wish to support 12 | others. 13 | 14 | References: 15 | 16 | [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD 17 | [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 18 | 19 | This package does not fall under the stability promise of the Go language itself, 20 | so its API may be changed when pressing needs arise. 21 | */ 22 | package ssh // import "github.com/zmap/zgrab2/lib/ssh" 23 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/ntp-4.2.6_REQ_MON_GETLIST_1.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.4","domain":"target","data":{"ntp":{"status":"success","protocol":"ntp","result":{"version":3,"time":"2018-04-06T19:39:50.515312746Z","time_response":{"leap_indicator":3,"version":3,"mode":4,"stratum":0,"poll":3,"precision":-22,"root_delay":{"seconds":0,"fraction":0},"root_dispersion":{"seconds":0,"fraction":12},"reference_id":"SU5JVA==","reference_timestamp":{"seconds":0,"fraction":0},"origin_timestamp":{"seconds":0,"fraction":0},"receive_timestamp":{"seconds":3732032390,"fraction":2213251392},"transmit_timestamp":{"seconds":3732032390,"fraction":2214061226}},"monlist_response":"AAAAAQAAAAAAAAAAAAAAA6wRAAWsEQAEAAAAAZTbAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","monlist_header":{"is_response":true,"has_more":false,"version":3,"mode":7,"is_authenticated":false,"sequence_number":0,"implementation_number":"IMPL_XNTPD","request_code":"REQ_MON_GETLIST_1","error":"INFO_OKAY","num_items":1,"mbz":0,"item_size":72}},"timestamp":"2018-04-06T19:39:50Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/mysql-5.5.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.3","domain":"target","data":{"mysql":{"status":"success","protocol":"mysql","result":{"protocol_version":10,"server_version":"5.5.58","connection_id":1,"auth_plugin_data":"aWZFZlJGRm97PVove0FuJ3Q4VzAA","character_set":8,"status_flags":{"SERVER_STATUS_AUTOCOMMIT":true},"capability_flags":{"CLIENT_COMPRESS":true,"CLIENT_CONNECT_WITH_DB":true,"CLIENT_FOUND_ROWS":true,"CLIENT_IGNORE_SIGPIPE":true,"CLIENT_IGNORE_SPACE":true,"CLIENT_INTERACTIVE":true,"CLIENT_LOCAL_FILES":true,"CLIENT_LONG_FLAG":true,"CLIENT_LONG_PASSWORD":true,"CLIENT_MULTI_RESULTS":true,"CLIENT_MULTI_STATEMENTS":true,"CLIENT_NO_SCHEMA":true,"CLIENT_ODBC":true,"CLIENT_PLUGIN_AUTH":true,"CLIENT_PROTOCOL_41":true,"CLIENT_PS_MULTI_RESULTS":true,"CLIENT_RESERVED":true,"CLIENT_SECURE_CONNECTION":true,"CLIENT_TRANSACTIONS":true},"auth_plugin_name":"mysql_native_password","raw_packets":["CjUuNS41OAABAAAAaWZFZlJGRm8A//cIAgAPgBUAAAAAAAAAAAAAez1aL3tBbid0OFcwAG15c3FsX25hdGl2ZV9wYXNzd29yZAA="]},"timestamp":"2018-04-06T19:39:14Z"}}} 2 | -------------------------------------------------------------------------------- /integration_tests/postgres/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | versions="9.3 9.4 9.5 9.6 10.1" 6 | types="ssl nossl" 7 | 8 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 9 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 10 | 11 | mkdir -p $ZGRAB_OUTPUT/postgres 12 | 13 | function doTest() { 14 | VERSION=$1 15 | TYPE=$2 16 | CONTAINER_NAME=zgrab_postgres_$VERSION-$TYPE 17 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh postgres > $ZGRAB_OUTPUT/postgres/$VERSION-$TYPE.json 18 | echo "BEGIN DOCKER LOGS FROM $CONTAINER_NAME [{(" 19 | docker logs --tail all $CONTAINER_NAME 20 | echo ")}] END DOCKER LOGS FROM $CONTAINER_NAME" 21 | echo "BEGIN POSTGRES LOGS FROM $CONTAINER_NAME [{(" 22 | # TODO: The "//var/lib" is a work-around for MinGW 23 | docker exec $CONTAINER_NAME cat //var/lib/postgresql/data/pg_log/postgres.log 24 | echo ")}] END POSTGRES LOGS FROM $CONTAINER_NAME" 25 | } 26 | 27 | for version in $versions; do 28 | for type in $types; do 29 | doTest $version $type 30 | done 31 | done 32 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/telnet.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's telnet module 2 | # Registers zgrab2-telnet globally, and telnet with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | telnet_option = SubRecord( 11 | { 12 | "name": String(), 13 | "value": Unsigned16BitInteger(), 14 | } 15 | ) 16 | 17 | telnet_scan_response = SubRecord( 18 | { 19 | "result": SubRecord( 20 | { 21 | "banner": String(), 22 | "will": ListOf(telnet_option), 23 | "do": ListOf(telnet_option), 24 | "wont": ListOf(telnet_option), 25 | "dont": ListOf(telnet_option), 26 | } 27 | ) 28 | }, 29 | extends=zgrab2.base_scan_response, 30 | ) 31 | 32 | zschema.registry.register_schema("zgrab2-telnet", telnet_scan_response) 33 | 34 | zgrab2.register_scan_response_type("telnet", telnet_scan_response) 35 | -------------------------------------------------------------------------------- /integration_tests/ftp/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | MODULE_DIR=$(dirname $0) 5 | TEST_ROOT=$MODULE_DIR/.. 6 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 7 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 8 | 9 | CONTAINER_NAME="zgrab_ftp" 10 | 11 | # Run the FTP-specific integration tests: 12 | # 1. Run zgrab2 on the container 13 | 14 | mkdir -p $ZGRAB_OUTPUT/ftp 15 | 16 | OUTPUT_DIR="$ZGRAB_OUTPUT/ftp" 17 | 18 | echo "ftp/test: Testing FTP with --authtls on $CONTAINER_NAME..." 19 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh ftp --authtls > $OUTPUT_DIR/authtls.json 20 | 21 | echo "ftp/test: Testing FTP on $CONTAINER_NAME..." 22 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh ftp > $OUTPUT_DIR/default.json 23 | 24 | echo "ftp/test: BEGIN vsftpd logs from $CONTAINER_NAME [{(" 25 | docker exec $CONTAINER_NAME cat //var/log/vsftpd.log 26 | echo ")}] END vsftpd logs from $CONTAINER_NAME" 27 | 28 | echo "ftp/test: BEGIN docker logs from $CONTAINER_NAME [{(" 29 | docker logs --tail all $CONTAINER_NAME 30 | echo ")}] END docker logs from $CONTAINER_NAME" 31 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/http2-only-https/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | # Enable debug logging globally 3 | log { 4 | output stdout 5 | format console 6 | level DEBUG 7 | } 8 | } 9 | 10 | 11 | https://localhost, https://http2.target { 12 | tls internal 13 | root * /usr/share/caddy/html/ 14 | file_server 15 | 16 | tls /etc/ssl/private/self.crt /etc/ssl/private/self.key 17 | 18 | # Enhanced logging with HTTP/2 details 19 | log { 20 | output stdout 21 | format json 22 | level DEBUG 23 | 24 | # Include additional fields for HTTP/2 debugging 25 | #include http.request.proto 26 | #include http.request.method 27 | #include http.request.uri 28 | #include http.request.headers 29 | #include http.response.headers 30 | #include http.vars.http2_stream_id 31 | } 32 | 33 | @nonh2 { 34 | expression `{http.request.proto} != "HTTP/2.0"` 35 | } 36 | respond @nonh2 "HTTP/2 required" 403 37 | handle_path /redirect-http-1 { 38 | redir http://localhost:8081 39 | } 40 | } -------------------------------------------------------------------------------- /lib/http/roundtrip.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !js 6 | 7 | package http 8 | 9 | import _ "unsafe" // for linkname 10 | 11 | // RoundTrip should be an internal detail, 12 | // but widely used packages access it using linkname. 13 | // Notable members of the hall of shame include: 14 | // - github.com/erda-project/erda-infra 15 | // 16 | // Do not remove or change the type signature. 17 | // See go.dev/issue/67401. 18 | // 19 | //go:linkname badRoundTrip net/http.(*Transport).RoundTrip 20 | func badRoundTrip(*Transport, *Request) (*Response, error) 21 | 22 | // RoundTrip implements the [RoundTripper] interface. 23 | // 24 | // For higher-level HTTP client support (such as handling of cookies 25 | // and redirects), see [Get], [Post], and the [Client] type. 26 | // 27 | // Like the RoundTripper interface, the error types returned 28 | // by RoundTrip are unspecified. 29 | func (t *Transport) RoundTrip(req *Request) (*Response, error) { 30 | return t.roundTrip(req) 31 | } 32 | -------------------------------------------------------------------------------- /lib/http2/internal/httpcommon/httpcommon_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package httpcommon_test 6 | 7 | import ( 8 | "bytes" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | // This package is imported by the net/http package, 16 | // and therefore must not itself import net/http. 17 | func TestNoNetHttp(t *testing.T) { 18 | files, err := filepath.Glob("*.go") 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | for _, file := range files { 23 | if strings.HasSuffix(file, "_test.go") { 24 | continue 25 | } 26 | // Could use something complex like go/build or x/tools/go/packages, 27 | // but there's no reason for "net/http" to appear (in quotes) in the source 28 | // otherwise, so just use a simple substring search. 29 | data, err := os.ReadFile(file) 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | if bytes.Contains(data, []byte(`"net/http"`)) { 34 | t.Errorf(`%s: cannot import "net/http"`, file) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/smb/README.md: -------------------------------------------------------------------------------- 1 | # SMB 2 | A Go package for communicating over SMB. Currently only minimal funcationality exists for client-side functions. 3 | 4 | Here is a sample client that establishes a session with a server: 5 | 6 | ```go 7 | package main 8 | 9 | import ( 10 | "log" 11 | 12 | "github.com/stacktitan/smb/smb" 13 | ) 14 | 15 | func main() { 16 | 17 | host := "172.16.248.192" 18 | options := smb.Options{ 19 | Host: host, 20 | Port: 445, 21 | User: "alice", 22 | Domain: "corp", 23 | Workstation: "", 24 | Password: "Password123!", 25 | } 26 | debug := false 27 | session, err := smb.NewSession(options, debug) 28 | if err != nil { 29 | log.Fatalln("[!]", err) 30 | } 31 | defer session.Close() 32 | 33 | if session.IsSigningRequired { 34 | log.Println("[-] Signing is required") 35 | } else { 36 | log.Println("[+] Signing is NOT required") 37 | } 38 | 39 | if session.IsAuthenticated { 40 | log.Println("[+] Login successful") 41 | } else { 42 | log.Println("[-] Login failed") 43 | } 44 | 45 | if err != nil { 46 | log.Fatalln("[!]", err) 47 | } 48 | } 49 | 50 | ``` 51 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/mysql-5.6.json: -------------------------------------------------------------------------------- 1 | {"ip":"172.17.0.4","domain":"target","data":{"mysql":{"status":"success","protocol":"mysql","result":{"protocol_version":10,"server_version":"5.6.38","connection_id":1,"auth_plugin_data":"MEtmQVJZRzkoWGZ4SkdmSyd6WCYA","character_set":8,"status_flags":{"SERVER_STATUS_AUTOCOMMIT":true},"capability_flags":{"CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS":true,"CLIENT_COMPRESS":true,"CLIENT_CONNECT_ATTRS":true,"CLIENT_CONNECT_WITH_DB":true,"CLIENT_FOUND_ROWS":true,"CLIENT_IGNORE_SIGPIPE":true,"CLIENT_IGNORE_SPACE":true,"CLIENT_INTERACTIVE":true,"CLIENT_LOCAL_FILES":true,"CLIENT_LONG_FLAG":true,"CLIENT_LONG_PASSWORD":true,"CLIENT_MULTI_RESULTS":true,"CLIENT_MULTI_STATEMENTS":true,"CLIENT_NO_SCHEMA":true,"CLIENT_ODBC":true,"CLIENT_PLUGIN_AUTH":true,"CLIENT_PLUGIN_AUTH_LEN_ENC_CLIENT_DATA":true,"CLIENT_PROTOCOL_41":true,"CLIENT_PS_MULTI_RESULTS":true,"CLIENT_RESERVED":true,"CLIENT_SECURE_CONNECTION":true,"CLIENT_TRANSACTIONS":true},"auth_plugin_name":"mysql_native_password","raw_packets":["CjUuNi4zOAABAAAAMEtmQVJZRzkA//cIAgB/gBUAAAAAAAAAAAAAKFhmeEpHZksnelgmAG15c3FsX25hdGl2ZV9wYXNzd29yZAA="]},"timestamp":"2018-04-06T19:39:16Z"}}} 2 | -------------------------------------------------------------------------------- /lib/smb/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 stacktitan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | # This code is licensed under the terms of the MIT license https://opensource.org/license/mit 2 | # Copyright (c) 2021 Marat Reymers 3 | 4 | version: "2" 5 | linters: 6 | default: none 7 | enable: 8 | - errcheck 9 | - fatcontext 10 | - govet 11 | - ineffassign 12 | - perfsprint 13 | - prealloc 14 | - staticcheck 15 | - unused 16 | settings: 17 | errcheck: 18 | check-type-assertions: true 19 | govet: 20 | disable: 21 | - fieldalignment 22 | enable-all: true 23 | settings: 24 | shadow: 25 | strict: false 26 | exclusions: 27 | generated: lax 28 | presets: 29 | - comments 30 | - common-false-positives 31 | - legacy 32 | - std-error-handling 33 | rules: 34 | - linters: 35 | - errcheck 36 | path: _test\.go 37 | paths: 38 | - third_party$ 39 | - builtin$ 40 | - examples$ 41 | - lib/* 42 | issues: 43 | max-issues-per-linter: 50 44 | formatters: 45 | exclusions: 46 | generated: lax 47 | paths: 48 | - third_party$ 49 | - builtin$ 50 | - examples$ 51 | -------------------------------------------------------------------------------- /lib/ssh/internal/poly1305/bits_compat.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !go1.13 6 | // +build !go1.13 7 | 8 | package poly1305 9 | 10 | // Generic fallbacks for the math/bits intrinsics, copied from 11 | // src/math/bits/bits.go. They were added in Go 1.12, but Add64 and Sum64 had 12 | // variable time fallbacks until Go 1.13. 13 | 14 | func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) { 15 | sum = x + y + carry 16 | carryOut = ((x & y) | ((x | y) &^ sum)) >> 63 17 | return 18 | } 19 | 20 | func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) { 21 | diff = x - y - borrow 22 | borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63 23 | return 24 | } 25 | 26 | func bitsMul64(x, y uint64) (hi, lo uint64) { 27 | const mask32 = 1<<32 - 1 28 | x0 := x & mask32 29 | x1 := x >> 32 30 | y0 := y & mask32 31 | y1 := y >> 32 32 | w0 := x0 * y0 33 | t := x1*y0 + w0>>32 34 | w1 := t & mask32 35 | w2 := t >> 32 36 | w1 += x0 * y1 37 | hi = x1*y1 + w2 + w1>>32 38 | lo = x * y 39 | return 40 | } 41 | -------------------------------------------------------------------------------- /integration_tests/managesieve/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | MODULE_DIR=$(dirname $0) 5 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 6 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 7 | 8 | mkdir -p $ZGRAB_OUTPUT/managesieve 9 | 10 | CONTAINER_NAME=zgrab_managesieve 11 | 12 | OUTPUT_FILE=$ZGRAB_OUTPUT/managesieve/managesieve.json 13 | 14 | echo "managesieve/test: Tests runner for managesieve" 15 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh managesieve > $OUTPUT_FILE 16 | SERVER_IMPLEMENTATION=$(jp -u data.managesieve.result.implementation < $OUTPUT_FILE) 17 | if [[ "$SERVER_IMPLEMENTATION" == "Stalwart"* ]]; then 18 | echo "managesieve/test: Server implementation looks good: $SERVER_IMPLEMENTATION" 19 | else 20 | echo "managesieve/test: Server implementation looks wrong: Got $SERVER_IMPLEMENTATION, expected something starting with 'Stalwart'. Full output: [[" 21 | cat $OUTPUT_FILE 22 | echo "]]" 23 | exit 1 24 | fi 25 | 26 | # Dump the docker logs 27 | echo "managesieve/test: BEGIN docker logs from $CONTAINER_NAME [{(" 28 | docker logs --tail all $CONTAINER_NAME 29 | echo ")}] END docker logs from $CONTAINER_NAME" 30 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/bacnet.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's bacnet module 2 | # Registers zgrab2-bacnet globally, and bacnet with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas 8 | from . import zgrab2 9 | 10 | bacnet_scan_response = SubRecord( 11 | { 12 | "result": SubRecord( 13 | { 14 | "is_bacnet": Boolean(), 15 | "instance_number": Unsigned32BitInteger(), 16 | "vendor_id": Unsigned16BitInteger(), 17 | "vendor_name": String(), 18 | "firmware_revision": String(), 19 | "application_software_revision": String(), 20 | "object_name": String(), 21 | "model_name": String(), 22 | "description": String(), 23 | "location": String(), 24 | } 25 | ) 26 | }, 27 | extends=zgrab2.base_scan_response, 28 | ) 29 | 30 | zschema.registry.register_schema("zgrab2-bacnet", bacnet_scan_response) 31 | 32 | zgrab2.register_scan_response_type("bacnet", bacnet_scan_response) 33 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/socks5.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's Socks5 module 2 | # Registers zgrab2-socks5 globally, and socks5 with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | from . import zgrab2 8 | 9 | # Schema for ScanResults struct 10 | socks5_response_explanation = SubRecord( 11 | { 12 | "Version": String(), 13 | "Reply": String(), 14 | "Reserved": String(), 15 | "Address Type": String(), 16 | "Bound Address": String(), 17 | "Bound Port": String(), 18 | } 19 | ) 20 | 21 | socks5_scan_response = SubRecord( 22 | { 23 | "version": String(), 24 | "method_selection": String(), 25 | "connection_response": String(), 26 | "connection_response_explanation": socks5_response_explanation, 27 | } 28 | ) 29 | 30 | socks5_scan = SubRecord( 31 | { 32 | "result": socks5_scan_response, 33 | }, 34 | extends=zgrab2.base_scan_response, 35 | ) 36 | 37 | zschema.registry.register_schema("zgrab2-socks5", socks5_scan) 38 | zgrab2.register_scan_response_type("socks5", socks5_scan) 39 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/pop3.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's pop3 module 2 | # Registers zgrab2-pop3 globally, and pop3 with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | pop3_scan_response = SubRecord( 11 | { 12 | "result": SubRecord( 13 | { 14 | "banner": String(doc="The POP3 banner."), 15 | "noop": String(doc="The server's response to the NOOP command."), 16 | "help": String(doc="The server's response to the HELP command."), 17 | "starttls": String( 18 | doc="The server's response to the STARTTLS command." 19 | ), 20 | "quit": String(doc="The server's response to the QUIT command."), 21 | "tls": zgrab2.tls_log, 22 | } 23 | ) 24 | }, 25 | extends=zgrab2.base_scan_response, 26 | ) 27 | 28 | zschema.registry.register_schema("zgrab2-pop3", pop3_scan_response) 29 | 30 | zgrab2.register_scan_response_type("pop3", pop3_scan_response) 31 | -------------------------------------------------------------------------------- /modules/imap/imap.go: -------------------------------------------------------------------------------- 1 | package imap 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "regexp" 7 | 8 | "github.com/zmap/zgrab2" 9 | ) 10 | 11 | // This is the regex used in zgrab. 12 | var imapStatusEndRegex = regexp.MustCompile(`\r\n$`) 13 | 14 | const readBufferSize int = 0x10000 15 | 16 | // Connection wraps the state and access to the SMTP connection. 17 | type Connection struct { 18 | Conn net.Conn 19 | } 20 | 21 | // ReadResponse reads from the connection until it matches the imapEndRegex. Copied from the original zgrab. 22 | // TODO: Catch corner cases 23 | func (conn *Connection) ReadResponse() (string, error) { 24 | ret := make([]byte, readBufferSize) 25 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, imapStatusEndRegex) 26 | if err != nil && err != io.EOF && !zgrab2.IsTimeoutError(err) { 27 | return "", err 28 | } 29 | return string(ret[:n]), nil 30 | } 31 | 32 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 33 | func (conn *Connection) SendCommand(cmd string) (string, error) { 34 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 35 | return "", err 36 | } 37 | return conn.ReadResponse() 38 | } 39 | -------------------------------------------------------------------------------- /modules/bacnet/query.go: -------------------------------------------------------------------------------- 1 | package bacnet 2 | 3 | import "bytes" 4 | 5 | type ReadPropertyRequest struct { 6 | NPDU NPDU `json:"npdu"` 7 | APDU APDU `json:"apdu"` 8 | Selection ReadProperty `json:"read_property"` 9 | } 10 | 11 | func (rp *ReadPropertyRequest) Marshal() (out []byte, err error) { 12 | buf := new(bytes.Buffer) 13 | var b []byte 14 | if b, err = rp.NPDU.Marshal(); err != nil { 15 | return 16 | } 17 | buf.Write(b) 18 | if b, err = rp.APDU.Marshal(); err != nil { 19 | return 20 | } 21 | buf.Write(b) 22 | if b, err = rp.Selection.Marshal(); err != nil { 23 | return 24 | } 25 | buf.Write(b) 26 | return buf.Bytes(), nil 27 | } 28 | 29 | func NewReadPropertyRequest(oid ObjectID, pid PropertyID) *ReadPropertyRequest { 30 | req := new(ReadPropertyRequest) 31 | req.NPDU.Version = NPDU_VERSION_ASHRAE_135_1995 32 | req.NPDU.Control |= NPDU_FLAG_EXPECTING_RESPONSE 33 | req.APDU.TypeAndFlags = 0 34 | req.APDU.SegmentSizes.set = true 35 | req.APDU.SegmentSizes.raw = 0x05 36 | req.APDU.InvokeID = 1 37 | req.APDU.ServerChoice = SERVER_CHOICE_READ_PROPERTY 38 | req.Selection.Object = oid 39 | req.Selection.Property = pid 40 | return req 41 | } 42 | -------------------------------------------------------------------------------- /lib/smb/ntlmssp/crypto.go: -------------------------------------------------------------------------------- 1 | package ntlmssp 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/md5" 6 | "strings" 7 | 8 | "golang.org/x/crypto/md4" 9 | 10 | "github.com/zmap/zgrab2/lib/smb/smb/encoder" 11 | ) 12 | 13 | func Ntowfv1(pass string) []byte { 14 | hash := md4.New() 15 | hash.Write(encoder.ToUnicode(pass)) 16 | return hash.Sum(nil) 17 | } 18 | 19 | func Ntowfv2(pass, user, domain string) []byte { 20 | h := hmac.New(md5.New, Ntowfv1(pass)) 21 | h.Write(encoder.ToUnicode(strings.ToUpper(user) + domain)) 22 | return h.Sum(nil) 23 | } 24 | 25 | func Lmowfv2(pass, user, domain string) []byte { 26 | return Ntowfv2(pass, user, domain) 27 | } 28 | 29 | func ComputeResponseNTLMv2(nthash, lmhash, clientChallenge, serverChallenge, timestamp, serverName []byte) []byte { 30 | 31 | temp := []byte{1, 1} 32 | temp = append(temp, 0, 0, 0, 0, 0, 0) 33 | temp = append(temp, timestamp...) 34 | temp = append(temp, clientChallenge...) 35 | temp = append(temp, 0, 0, 0, 0) 36 | temp = append(temp, serverName...) 37 | temp = append(temp, 0, 0, 0, 0) 38 | 39 | h := hmac.New(md5.New, nthash) 40 | h.Write(append(serverChallenge, temp...)) 41 | ntproof := h.Sum(nil) 42 | return append(ntproof, temp...) 43 | } 44 | -------------------------------------------------------------------------------- /lib/http2/unencrypted.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "errors" 9 | "net" 10 | 11 | "github.com/zmap/zcrypto/tls" 12 | ) 13 | 14 | const nextProtoUnencryptedHTTP2 = "unencrypted_http2" 15 | 16 | // unencryptedNetConnFromTLSConn retrieves a net.Conn wrapped in a *tls.Conn. 17 | // 18 | // TLSNextProto functions accept a *tls.Conn. 19 | // 20 | // When passing an unencrypted HTTP/2 connection to a TLSNextProto function, 21 | // we pass a *tls.Conn with an underlying net.Conn containing the unencrypted connection. 22 | // To be extra careful about mistakes (accidentally dropping TLS encryption in a place 23 | // where we want it), the tls.Conn contains a net.Conn with an UnencryptedNetConn method 24 | // that returns the actual connection we want to use. 25 | func unencryptedNetConnFromTLSConn(tc *tls.Conn) (net.Conn, error) { 26 | conner, ok := tc.NetConn().(interface { 27 | UnencryptedNetConn() net.Conn 28 | }) 29 | if !ok { 30 | return nil, errors.New("http2: TLS conn unexpectedly found in unencrypted handoff") 31 | } 32 | return conner.UnencryptedNetConn(), nil 33 | } 34 | -------------------------------------------------------------------------------- /lib/http/h2_error_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !nethttpomithttp2 6 | 7 | package http 8 | 9 | import ( 10 | "errors" 11 | "fmt" 12 | "testing" 13 | ) 14 | 15 | type externalStreamErrorCode uint32 16 | 17 | type externalStreamError struct { 18 | StreamID uint32 19 | Code externalStreamErrorCode 20 | Cause error 21 | } 22 | 23 | func (e externalStreamError) Error() string { 24 | return fmt.Sprintf("ID %v, code %v", e.StreamID, e.Code) 25 | } 26 | 27 | func TestStreamError(t *testing.T) { 28 | var target externalStreamError 29 | streamErr := http2streamError(42, http2ErrCodeProtocol) 30 | ok := errors.As(streamErr, &target) 31 | if !ok { 32 | t.Fatalf("errors.As failed") 33 | } 34 | if target.StreamID != streamErr.StreamID { 35 | t.Errorf("got StreamID %v, expected %v", target.StreamID, streamErr.StreamID) 36 | } 37 | if target.Cause != streamErr.Cause { 38 | t.Errorf("got Cause %v, expected %v", target.Cause, streamErr.Cause) 39 | } 40 | if uint32(target.Code) != uint32(streamErr.Code) { 41 | t.Errorf("got Code %v, expected %v", target.Code, streamErr.Code) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/smb/smb/encoder/unicode.go: -------------------------------------------------------------------------------- 1 | package encoder 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "unicode/utf16" 8 | ) 9 | 10 | func FromUnicode(d []byte) (string, error) { 11 | // Credit to https://github.com/Azure/go-ntlmssp/blob/master/unicode.go for logic 12 | if len(d)%2 > 0 { 13 | return "", errors.New("unicode (UTF 16 LE) specified, but uneven data length") 14 | } 15 | s := make([]uint16, len(d)/2) 16 | err := binary.Read(bytes.NewReader(d), binary.LittleEndian, &s) 17 | if err != nil { 18 | return "", err 19 | } 20 | return string(utf16.Decode(s)), nil 21 | } 22 | 23 | func ToUnicode(s string) []byte { 24 | // Credit to https://github.com/Azure/go-ntlmssp/blob/master/unicode.go for logic 25 | uints := utf16.Encode([]rune(s)) 26 | b := bytes.Buffer{} 27 | binary.Write(&b, binary.LittleEndian, &uints) 28 | return b.Bytes() 29 | } 30 | 31 | func ToSmbString(s string) []byte { 32 | res := ToUnicode(s) 33 | res = append(res, 0x0, 0x0) 34 | return res 35 | } 36 | 37 | func FromSmbString(d []byte) (string, error) { 38 | res, err := FromUnicode(d) 39 | if err != nil { 40 | return "", err 41 | } 42 | if len(res) == 0 { 43 | return "", nil 44 | } 45 | // Trim null terminator 46 | return res[:len(res)-1], nil 47 | } 48 | -------------------------------------------------------------------------------- /lib/smb/ntlmssp/ntlmssp_test.go: -------------------------------------------------------------------------------- 1 | package ntlmssp 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/zmap/zgrab2/lib/smb/smb/encoder" 8 | ) 9 | 10 | /* 11 | Malformed NTLMSSP challenge, in that the first AvPair has an invalid 12 | type code, and has an absurd (0x910e) length for the field. 13 | */ 14 | const problematicResponse = "" + 15 | "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x02\x00\x00\x00\x08\x00\x08\x00" + 16 | "\x38\x00\x00\x00\x05\x02\x8a\xa2\x73\xcb\xa1\xb4\x21\x03\xf7\xfb" + 17 | "\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x40\x00\x40\x00\x00\x00" + 18 | "\x0a\x00\x61\x4a\x00\x00\x00\x0f\x41\x00\x53\x00\x55\x00\x53\x00" + 19 | "\x17\xe9\x0e\x91\x31\xe7\xb2\xce\xac\x29\x59\xba\x01\x00\x08\x00" + 20 | "\x41\x00\x53\x00\x55\x00\x53\x00\x04\x00\x08\x00\x41\x00\x53\x00" + 21 | "\x55\x00\x53\x00\x03\x00\x08\x00\x41\x00\x53\x00\x55\x00\x53\x00" + 22 | "\x07\x00\x08\x00\x24\x35\x53\x3a\x25\xff\xd6\x01\x00\x00\x00\x00" 23 | 24 | func TestMalformedChallenge(t *testing.T) { 25 | challenge := NewChallenge() 26 | if err := encoder.Unmarshal([]byte(problematicResponse), &challenge); err != nil { 27 | if !strings.HasPrefix(err.Error(), "field 'Value'") { 28 | t.Errorf("Expected error on field Value but failed elsewhere: %v", err) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /modules/smtp/smtp.go: -------------------------------------------------------------------------------- 1 | package smtp 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "regexp" 7 | 8 | "github.com/zmap/zgrab2" 9 | ) 10 | 11 | // This is the regex used in zgrab. 12 | // Corner cases like "200 OK\r\nthis is not valid at all\x00\x01\x02\x03\r\n" will be matched. 13 | var smtpEndRegex = regexp.MustCompile(`(?:^\d\d\d\s.*\r\n$)|(?:^\d\d\d-[\s\S]*\r\n\d\d\d\s.*\r\n$)`) 14 | 15 | const readBufferSize int = 0x10000 16 | 17 | // Connection wraps the state and access to the SMTP connection. 18 | type Connection struct { 19 | Conn net.Conn 20 | } 21 | 22 | // ReadResponse reads from the connection until it matches the smtpEndRegex. Copied from the original zgrab. 23 | // TODO: Catch corner cases 24 | func (conn *Connection) ReadResponse() (string, error) { 25 | ret := make([]byte, readBufferSize) 26 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, smtpEndRegex) 27 | if err != nil && err != io.EOF && !zgrab2.IsTimeoutError(err) { 28 | return "", err 29 | } 30 | return string(ret[:n]), nil 31 | } 32 | 33 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 34 | func (conn *Connection) SendCommand(cmd string) (string, error) { 35 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 36 | return "", err 37 | } 38 | return conn.ReadResponse() 39 | } 40 | -------------------------------------------------------------------------------- /modules/pop3/pop3.go: -------------------------------------------------------------------------------- 1 | // Scanner for POP3 protocol 2 | // https://www.ietf.org/rfc/rfc1939.txt 3 | 4 | package pop3 5 | 6 | import ( 7 | "io" 8 | "net" 9 | "regexp" 10 | 11 | "github.com/zmap/zgrab2" 12 | ) 13 | 14 | // This is the regex used in zgrab. 15 | var pop3EndRegex = regexp.MustCompile(`(?:\r\n\.\r\n$)|(?:\r\n$)`) 16 | 17 | const readBufferSize int = 0x10000 18 | 19 | // Connection wraps the state and access to the SMTP connection. 20 | type Connection struct { 21 | Conn net.Conn 22 | } 23 | 24 | // ReadResponse reads from the connection until it matches the pop3EndRegex. Copied from the original zgrab. 25 | // TODO: Catch corner cases 26 | func (conn *Connection) ReadResponse() (string, error) { 27 | ret := make([]byte, readBufferSize) 28 | n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, pop3EndRegex) 29 | // Don't quit for timeouts since we might have gotten relevant data still 30 | if err != nil && err != io.EOF && !zgrab2.IsTimeoutError(err) { 31 | return "", err 32 | } 33 | return string(ret[:n]), nil 34 | } 35 | 36 | // SendCommand sends a command, followed by a CRLF, then wait for / read the server's response. 37 | func (conn *Connection) SendCommand(cmd string) (string, error) { 38 | if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil { 39 | return "", err 40 | } 41 | return conn.ReadResponse() 42 | } 43 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/siemens.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's siemens module 2 | # Registers zgrab2-siemens globally, and siemens with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | siemens_scan_response = SubRecord( 11 | { 12 | "result": SubRecord( 13 | { 14 | "is_s7": Boolean(), 15 | "system": String(), 16 | "module": String(), 17 | "plant_id": String(), 18 | "copyright": String(), 19 | "serial_number": String(), 20 | "module_type": String(), 21 | "reserved_for_os": String(), 22 | "memory_serial_number": String(), 23 | "cpu_profile": String(), 24 | "oem_id": String(), 25 | "location": String(), 26 | "module_id": String(), 27 | "hardware": String(), 28 | "firmware": String(), 29 | } 30 | ) 31 | }, 32 | extends=zgrab2.base_scan_response, 33 | ) 34 | 35 | zschema.registry.register_schema("zgrab2-siemens", siemens_scan_response) 36 | 37 | zgrab2.register_scan_response_type("siemens", siemens_scan_response) 38 | -------------------------------------------------------------------------------- /lib/ssh/internal/poly1305/sum_amd64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build gc && !purego 6 | // +build gc,!purego 7 | 8 | package poly1305 9 | 10 | //go:noescape 11 | func update(state *macState, msg []byte) 12 | 13 | // mac is a wrapper for macGeneric that redirects calls that would have gone to 14 | // updateGeneric to update. 15 | // 16 | // Its Write and Sum methods are otherwise identical to the macGeneric ones, but 17 | // using function pointers would carry a major performance cost. 18 | type mac struct{ macGeneric } 19 | 20 | func (h *mac) Write(p []byte) (int, error) { 21 | nn := len(p) 22 | if h.offset > 0 { 23 | n := copy(h.buffer[h.offset:], p) 24 | if h.offset+n < TagSize { 25 | h.offset += n 26 | return nn, nil 27 | } 28 | p = p[n:] 29 | h.offset = 0 30 | update(&h.macState, h.buffer[:]) 31 | } 32 | if n := len(p) - (len(p) % TagSize); n > 0 { 33 | update(&h.macState, p[:n]) 34 | p = p[n:] 35 | } 36 | if len(p) > 0 { 37 | h.offset += copy(h.buffer[h.offset:], p) 38 | } 39 | return nn, nil 40 | } 41 | 42 | func (h *mac) Sum(out *[16]byte) { 43 | state := h.macState 44 | if h.offset > 0 { 45 | update(&state, h.buffer[:h.offset]) 46 | } 47 | finalize(out, &state.h, &state.s) 48 | } 49 | -------------------------------------------------------------------------------- /lib/ssh/internal/poly1305/sum_ppc64le.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build gc && !purego 6 | // +build gc,!purego 7 | 8 | package poly1305 9 | 10 | //go:noescape 11 | func update(state *macState, msg []byte) 12 | 13 | // mac is a wrapper for macGeneric that redirects calls that would have gone to 14 | // updateGeneric to update. 15 | // 16 | // Its Write and Sum methods are otherwise identical to the macGeneric ones, but 17 | // using function pointers would carry a major performance cost. 18 | type mac struct{ macGeneric } 19 | 20 | func (h *mac) Write(p []byte) (int, error) { 21 | nn := len(p) 22 | if h.offset > 0 { 23 | n := copy(h.buffer[h.offset:], p) 24 | if h.offset+n < TagSize { 25 | h.offset += n 26 | return nn, nil 27 | } 28 | p = p[n:] 29 | h.offset = 0 30 | update(&h.macState, h.buffer[:]) 31 | } 32 | if n := len(p) - (len(p) % TagSize); n > 0 { 33 | update(&h.macState, p[:n]) 34 | p = p[n:] 35 | } 36 | if len(p) > 0 { 37 | h.offset += copy(h.buffer[h.offset:], p) 38 | } 39 | return nn, nil 40 | } 41 | 42 | func (h *mac) Sum(out *[16]byte) { 43 | state := h.macState 44 | if h.offset > 0 { 45 | update(&state, h.buffer[:h.offset]) 46 | } 47 | finalize(out, &state.h, &state.s) 48 | } 49 | -------------------------------------------------------------------------------- /integration_tests/http/http_version_tests/README.md: -------------------------------------------------------------------------------- 1 | ## HTTP Docker Tests 2 | 3 | The goal of this effort was to provide test web servers that have strictly HTTP1 and HTTP2 behavior to test ZGrab's HTTP module. 4 | 5 | ## Running the containers 6 | From this directory, run: 7 | ```shell 8 | docker-compose up --build 9 | ``` 10 | 11 | Then from a separate terminal, run: 12 | ```shell 13 | curl http://localhost:8081/index.html 14 | curl -k --http2 https://localhost:8082/index.html 15 | ``` 16 | 17 | ## Tests 18 | I didn't find out how to easily have docker-run.sh mount the ZGrab code Docker into a namespace where it can reach all the HTTP version test containers at once. I'm sure this is possible, but I've settled for manual testing for now. 19 | 20 | ### Test Setup 21 | From integration_tests/http/http_version_tests run: 22 | ```shell 23 | docker-compose up --build 24 | ``` 25 | 26 | In a separate terminal, from the main zgrab2 directory, run: 27 | ```shell 28 | make 29 | ``` 30 | 31 | ### HTTP/1.1 Test 32 | #### Test Command 33 | ```shell 34 | echo "localhost" | ./zgrab2 http --port=8081 35 | ``` 36 | 37 | #### Expected Behavior 38 | - Status Code: 200 39 | - Protocol: HTTP/1.1 40 | 41 | 42 | ### HTTP/2.0 over TLS Test 43 | #### Test Command 44 | ```shell 45 | echo "localhost" | ./zgrab2 http --port=8082 --use-https 46 | ``` 47 | 48 | #### Expected Behavior 49 | - Status Code: 200 50 | - Protocol: HTTP/2.0 51 | -------------------------------------------------------------------------------- /lib/http/proxy_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http 6 | 7 | import ( 8 | "net/url" 9 | "os" 10 | "testing" 11 | ) 12 | 13 | // TODO(mattn): 14 | // test ProxyAuth 15 | 16 | var cacheKeysTests = []struct { 17 | proxy string 18 | scheme string 19 | addr string 20 | key string 21 | }{ 22 | {"", "http", "foo.com", "|http|foo.com"}, 23 | {"", "https", "foo.com", "|https|foo.com"}, 24 | {"http://foo.com", "http", "foo.com", "http://foo.com|http|"}, 25 | {"http://foo.com", "https", "foo.com", "http://foo.com|https|foo.com"}, 26 | } 27 | 28 | func TestCacheKeys(t *testing.T) { 29 | for _, tt := range cacheKeysTests { 30 | var proxy *url.URL 31 | if tt.proxy != "" { 32 | u, err := url.Parse(tt.proxy) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | proxy = u 37 | } 38 | cm := connectMethod{proxyURL: proxy, targetScheme: tt.scheme, targetAddr: tt.addr} 39 | if got := cm.key().String(); got != tt.key { 40 | t.Fatalf("{%q, %q, %q} cache key = %q; want %q", tt.proxy, tt.scheme, tt.addr, got, tt.key) 41 | } 42 | } 43 | } 44 | 45 | func ResetProxyEnv() { 46 | for _, v := range []string{"HTTP_PROXY", "http_proxy", "NO_PROXY", "no_proxy", "REQUEST_METHOD"} { 47 | os.Unsetenv(v) 48 | } 49 | ResetCachedEnvironment() 50 | } 51 | -------------------------------------------------------------------------------- /modules/ipp/ipp_test.go: -------------------------------------------------------------------------------- 1 | package ipp 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | func ExampleAttributeByteString() { 9 | var buf bytes.Buffer 10 | if err := AttributeByteString(0x47, "attributes-charset", "us-ascii", &buf); err == nil { 11 | // We want the bytes to be printed in byte form 12 | // nolint: staticcheck 13 | fmt.Println(buf.Bytes()) 14 | } 15 | // Output: [71 0 18 97 116 116 114 105 98 117 116 101 115 45 99 104 97 114 115 101 116 0 8 117 115 45 97 115 99 105 105] 16 | } 17 | 18 | func ExampleConvertURIToIPP() { 19 | fmt.Println(ConvertURIToIPP("http://www.google.com:631/ipp", false)) 20 | fmt.Println(ConvertURIToIPP("https://www.google.com:631/ipp", true)) 21 | fmt.Println(ConvertURIToIPP("http://www.google.com/ipp", false)) 22 | fmt.Println(ConvertURIToIPP("https://www.google.com/ipp", true)) 23 | fmt.Println(ConvertURIToIPP("http://www.google.com:631", false)) 24 | fmt.Println(ConvertURIToIPP("https://www.google.com:631", true)) 25 | // TODO: Eventually test for scheme-less urls, but getHTTPURL will never construct one 26 | //fmt.Println(ConvertURIToIPP("www.google.com:631/ipp", false)) 27 | //fmt.Println(ConvertURIToIPP("www.google.com:631/ipp", true)) 28 | // Output: 29 | // ipp://www.google.com:631/ipp 30 | // ipps://www.google.com:631/ipp 31 | // ipp://www.google.com:631/ipp 32 | // ipps://www.google.com:631/ipp 33 | // ipp://www.google.com:631 34 | // ipps://www.google.com:631 35 | } 36 | -------------------------------------------------------------------------------- /integration_tests/mqtt/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | MODULE_DIR=$(dirname $0) 5 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 6 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 7 | 8 | mkdir -p $ZGRAB_OUTPUT/mqtt 9 | 10 | CONTAINER_NAME=zgrab_mqtt 11 | 12 | OUTPUT_FILE=$ZGRAB_OUTPUT/mqtt/mqtt.json 13 | 14 | echo "mqtt/test: Tests runner for mqtt" 15 | # TODO FIXME: Add any necessary flags or additional tests 16 | # echo -e ",target,mqtt 17 | # ,target,mqtt-tls 18 | # ,target,mqtt-v5 19 | # ,target,mqtt-tls-v5" | docker run --rm -i -v ./multiple.ini:/multiple.ini --link $CONTAINER_NAME:target zgrab2_runner multiple -c /multiple.ini> $OUTPUT_FILE 20 | #CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh mqtt --v5 >> $OUTPUT_FILE 21 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh mqtt > $ZGRAB_OUTPUT/mqtt/default.json 22 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh mqtt --v5 > $ZGRAB_OUTPUT/mqtt/mqtt_v5.json 23 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh mqtt --tls -p 8883 > $ZGRAB_OUTPUT/mqtt/mqtt_tls.json 24 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh mqtt --tls --v5 -p 8883 > $ZGRAB_OUTPUT/mqtt/mqtt_tls_v5.json 25 | 26 | # Dump the docker logs 27 | echo "mqtt/test: BEGIN docker logs from $CONTAINER_NAME [{(" 28 | docker logs --tail all $CONTAINER_NAME 29 | echo ")}] END docker logs from $CONTAINER_NAME" 30 | 31 | # TODO: If there are any other relevant log files, dump those to stdout here. 32 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/fox.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's fox module 2 | # Registers zgrab2-fox globally, and fox with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | fox_scan_response = SubRecord( 11 | { 12 | "result": SubRecord( 13 | { 14 | "is_fox": Boolean(), 15 | "version": String(), 16 | "id": Unsigned32BitInteger(), 17 | "hostname": String(), 18 | "host_address": String(), 19 | "app_name": String(), 20 | "app_version": String(), 21 | "vm_name": String(), 22 | "vm_version": String(), 23 | "os_name": String(), 24 | "os_version": String(), 25 | "station_name": String(), 26 | "language": String(), 27 | "time_zone": String(), 28 | "host_id": String(), 29 | "vm_uuid": String(), 30 | "brand_id": String(), 31 | "sys_info": String(), 32 | "agent_auth_type": String(), 33 | "tls": zgrab2.tls_log, 34 | } 35 | ) 36 | }, 37 | extends=zgrab2.base_scan_response, 38 | ) 39 | 40 | zschema.registry.register_schema("zgrab2-fox", fox_scan_response) 41 | 42 | zgrab2.register_scan_response_type("fox", fox_scan_response) 43 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zmap/zgrab2 2 | 3 | go 1.24.0 4 | 5 | toolchain go1.24.7 6 | 7 | require ( 8 | github.com/censys/cidranger v1.1.3 9 | github.com/hashicorp/golang-lru/v2 v2.0.7 10 | github.com/hdm/jarm-go v0.0.7 11 | github.com/prometheus/client_golang v1.23.2 12 | github.com/rabbitmq/amqp091-go v1.10.0 13 | github.com/rogpeppe/go-internal v1.14.1 14 | github.com/sirupsen/logrus v1.9.3 15 | github.com/zmap/zcrypto v0.0.0-20250618174828-7ca6a82cf2d4 16 | github.com/zmap/zflags v1.4.0-beta.1.0.20251126025438-ec78c6d2f8e9 17 | golang.org/x/crypto v0.46.0 18 | golang.org/x/net v0.48.0 19 | golang.org/x/sys v0.39.0 20 | golang.org/x/term v0.38.0 21 | golang.org/x/time v0.14.0 22 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c 23 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 24 | gopkg.in/yaml.v2 v2.4.0 25 | ) 26 | 27 | require ( 28 | github.com/beorn7/perks v1.0.1 // indirect 29 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 30 | github.com/kr/pretty v0.3.1 // indirect 31 | github.com/kr/text v0.2.0 // indirect 32 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 33 | github.com/prometheus/client_model v0.6.2 // indirect 34 | github.com/prometheus/common v0.66.1 // indirect 35 | github.com/prometheus/procfs v0.16.1 // indirect 36 | github.com/weppos/publicsuffix-go v0.40.3-0.20250617082559-9b2e24a9e482 // indirect 37 | go.yaml.in/yaml/v2 v2.4.2 // indirect 38 | golang.org/x/text v0.32.0 // indirect 39 | google.golang.org/protobuf v1.36.8 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /lib/http/internal/ascii/print.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ascii 6 | 7 | import ( 8 | "strings" 9 | "unicode" 10 | ) 11 | 12 | // EqualFold is [strings.EqualFold], ASCII only. It reports whether s and t 13 | // are equal, ASCII-case-insensitively. 14 | func EqualFold(s, t string) bool { 15 | if len(s) != len(t) { 16 | return false 17 | } 18 | for i := 0; i < len(s); i++ { 19 | if lower(s[i]) != lower(t[i]) { 20 | return false 21 | } 22 | } 23 | return true 24 | } 25 | 26 | // lower returns the ASCII lowercase version of b. 27 | func lower(b byte) byte { 28 | if 'A' <= b && b <= 'Z' { 29 | return b + ('a' - 'A') 30 | } 31 | return b 32 | } 33 | 34 | // IsPrint returns whether s is ASCII and printable according to 35 | // https://tools.ietf.org/html/rfc20#section-4.2. 36 | func IsPrint(s string) bool { 37 | for i := 0; i < len(s); i++ { 38 | if s[i] < ' ' || s[i] > '~' { 39 | return false 40 | } 41 | } 42 | return true 43 | } 44 | 45 | // Is returns whether s is ASCII. 46 | func Is(s string) bool { 47 | for i := 0; i < len(s); i++ { 48 | if s[i] > unicode.MaxASCII { 49 | return false 50 | } 51 | } 52 | return true 53 | } 54 | 55 | // ToLower returns the lowercase version of s if s is ASCII and printable. 56 | func ToLower(s string) (lower string, ok bool) { 57 | if !IsPrint(s) { 58 | return "", false 59 | } 60 | return strings.ToLower(s), true 61 | } 62 | -------------------------------------------------------------------------------- /integration_tests/mysql/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | versions="5.5 5.6 5.7 8.0" 5 | 6 | # Run the MySQL-specific integration tests: 7 | # 1. Run zgrab2 on the container 8 | # 2. Check that data.mysql.result.handshake.parsed.server_version matches $MYSQL_VERSION 9 | 10 | MODULE_DIR=$(dirname $0) 11 | TEST_ROOT=$MODULE_DIR/.. 12 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 13 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 14 | 15 | status=0 16 | 17 | function doTest() { 18 | MYSQL_VERSION=$1 19 | CONTAINER_NAME="zgrab_mysql-$MYSQL_VERSION" 20 | OUTPUT_FILE="$ZGRAB_OUTPUT/mysql/$MYSQL_VERSION.json" 21 | echo "mysql/test: Testing MySQL Version $MYSQL_VERSION..." 22 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh mysql --target-timeout 10s > $OUTPUT_FILE 23 | SERVER_VERSION=$(jp -u data.mysql.result.server_version < $OUTPUT_FILE) 24 | if [[ "$SERVER_VERSION" == "$MYSQL_VERSION."* ]]; then 25 | echo "mysql/test: Server version matches expected version: $SERVER_VERSION == $MYSQL_VERSION.*" 26 | else 27 | echo "mysql/test: Server version mismatch: Got $SERVER_VERSION, expected $MYSQL_VERSION.*. Full output: [[" 28 | cat $OUTPUT_FILE 29 | echo "]]" 30 | status=1 31 | fi 32 | echo "mysql/test: BEGIN docker+mysql logs from $CONTAINER_NAME [{(" 33 | docker logs --tail all $CONTAINER_NAME 34 | echo ")}] END docker+mysql logs from $CONTAINER_NAME" 35 | } 36 | 37 | mkdir -p $ZGRAB_OUTPUT/mysql 38 | 39 | for version in $versions; do 40 | doTest $version 41 | done 42 | 43 | exit $status 44 | -------------------------------------------------------------------------------- /integration_tests/postgres/container/README.md: -------------------------------------------------------------------------------- 1 | ## About ## 2 | 3 | The integration_tests/postgres/container folder contains the config files for building the custom postgres docker images. 4 | These are based on the standard postgres images (https://hub.docker.com/_/postgres/), but with two changes: 5 | 6 | 1. Enable logging (in $PGDATA/pg_log/postgres.log) 7 | 2. Enable SSL (at least, if type = ssl) 8 | 9 | ## Adding a new postgres version ## 10 | 11 | For most new versions, you can just add the new version tag to the `versions` list in setup.sh / test.sh and the new version will be pulled in. 12 | If on the other hand you need a custom Dockerfile (as we did for 9.3, which doesn't support all of the SSL config options available in later versions), 13 | you will need to add a custom `Dockerfile.[version]`. 14 | The Dockerfile will receive a build-arg named IMAGE_TYPE, which can be `ssl` or `nossl`, which it can use to make the appropriate setup decisions. 15 | See `Dockerfile.9.3` for an example. The only difference there is it uses the 9.3 versions of the conf files. 16 | 17 | ## Details ## 18 | 19 | 1. `docker compose` creates a docker image tagged `zgrab_postgres:[version]-[type]`, where `[type]` is `ssl` or `nossl` 20 | 2. The Dockerfile drops the `setup_[type].sh` and `postgresql.conf.[nossl].partial` into the image 21 | 3. `docker compose` starts the containers, binding them to ports 3543x (ssl) and 4543x (nonssl). 22 | 4. During startup, the `setup_[type].sh` script is run on the image, setting up logging (and, on SSL images, generating self-signed SSL certificates) -------------------------------------------------------------------------------- /lib/http2/ascii.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import "strings" 8 | 9 | // The HTTP protocols are defined in terms of ASCII, not Unicode. This file 10 | // contains helper functions which may use Unicode-aware functions which would 11 | // otherwise be unsafe and could introduce vulnerabilities if used improperly. 12 | 13 | // asciiEqualFold is strings.EqualFold, ASCII only. It reports whether s and t 14 | // are equal, ASCII-case-insensitively. 15 | func asciiEqualFold(s, t string) bool { 16 | if len(s) != len(t) { 17 | return false 18 | } 19 | for i := 0; i < len(s); i++ { 20 | if lower(s[i]) != lower(t[i]) { 21 | return false 22 | } 23 | } 24 | return true 25 | } 26 | 27 | // lower returns the ASCII lowercase version of b. 28 | func lower(b byte) byte { 29 | if 'A' <= b && b <= 'Z' { 30 | return b + ('a' - 'A') 31 | } 32 | return b 33 | } 34 | 35 | // isASCIIPrint returns whether s is ASCII and printable according to 36 | // https://tools.ietf.org/html/rfc20#section-4.2. 37 | func isASCIIPrint(s string) bool { 38 | for i := 0; i < len(s); i++ { 39 | if s[i] < ' ' || s[i] > '~' { 40 | return false 41 | } 42 | } 43 | return true 44 | } 45 | 46 | // asciiToLower returns the lowercase version of s if s is ASCII and printable, 47 | // and whether or not it was. 48 | func asciiToLower(s string) (lower string, ok bool) { 49 | if !isASCIIPrint(s) { 50 | return "", false 51 | } 52 | return strings.ToLower(s), true 53 | } 54 | -------------------------------------------------------------------------------- /blocklist.go: -------------------------------------------------------------------------------- 1 | package zgrab2 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "strings" 8 | 9 | "github.com/censys/cidranger" 10 | ) 11 | 12 | func stripComments(line, commentDelimiter string) string { 13 | // Remove comments from the line 14 | if idx := strings.Index(line, commentDelimiter); idx != -1 { 15 | return line[:idx] 16 | } 17 | return line 18 | } 19 | 20 | // readBlocklist reads a blocklist file that contains CIDR ranges, IPs, or IP ranges 21 | // It returns a path-compressed trie of CIDR ranges that can be used to check if an IP address is in the blocklist 22 | func readBlocklist(fileName string) (cidranger.Ranger, error) { 23 | data, err := os.ReadFile(fileName) 24 | if err != nil { 25 | return nil, fmt.Errorf("failed to read blocklist file: %w", err) 26 | } 27 | strData := string(data) 28 | // Remove comments and empty lines 29 | cidrStrings := make([]string, 0) 30 | for _, line := range strings.Split(strData, "\n") { 31 | line = stripComments(line, "#") 32 | line = strings.TrimSpace(line) // Trim whitespace 33 | if len(line) > 0 { 34 | cidrStrings = append(cidrStrings, line) 35 | } 36 | } 37 | var cidrs []net.IPNet 38 | cidrs, err = extractCIDRRanges(cidrStrings) 39 | if err != nil { 40 | return nil, fmt.Errorf("failed to convert blacklist into CIDRs: %w", err) 41 | } 42 | ranger := cidranger.NewPCTrieRanger() 43 | for _, cidr := range cidrs { 44 | if err = ranger.Insert(cidranger.NewBasicRangerEntry(cidr)); err != nil { 45 | return nil, fmt.Errorf("failed to insert CIDR (%s) into ranger: %w", cidr.String(), err) 46 | } 47 | } 48 | return ranger, nil 49 | } 50 | -------------------------------------------------------------------------------- /lib/ssh/mac.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | // Message authentication support 8 | 9 | import ( 10 | "crypto/hmac" 11 | "crypto/sha1" 12 | "crypto/sha256" 13 | "hash" 14 | ) 15 | 16 | type macMode struct { 17 | keySize int 18 | etm bool 19 | new func(key []byte) hash.Hash 20 | } 21 | 22 | // truncatingMAC wraps around a hash.Hash and truncates the output digest to 23 | // a given size. 24 | type truncatingMAC struct { 25 | length int 26 | hmac hash.Hash 27 | } 28 | 29 | func (t truncatingMAC) Write(data []byte) (int, error) { 30 | return t.hmac.Write(data) 31 | } 32 | 33 | func (t truncatingMAC) Sum(in []byte) []byte { 34 | out := t.hmac.Sum(in) 35 | return out[:len(in)+t.length] 36 | } 37 | 38 | func (t truncatingMAC) Reset() { 39 | t.hmac.Reset() 40 | } 41 | 42 | func (t truncatingMAC) Size() int { 43 | return t.length 44 | } 45 | 46 | func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } 47 | 48 | var macModes = map[string]*macMode{ 49 | "hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash { 50 | return hmac.New(sha256.New, key) 51 | }}, 52 | "hmac-sha2-256": {32, false, func(key []byte) hash.Hash { 53 | return hmac.New(sha256.New, key) 54 | }}, 55 | "hmac-sha1": {20, false, func(key []byte) hash.Hash { 56 | return hmac.New(sha1.New, key) 57 | }}, 58 | "hmac-sha1-96": {20, false, func(key []byte) hash.Hash { 59 | return truncatingMAC{12, hmac.New(sha1.New, key)} 60 | }}, 61 | } 62 | -------------------------------------------------------------------------------- /lib/http2/internal/httpcommon/ascii.go: -------------------------------------------------------------------------------- 1 | package httpcommon 2 | 3 | // Copyright 2025 The Go Authors. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | import "strings" 8 | 9 | // The HTTP protocols are defined in terms of ASCII, not Unicode. This file 10 | // contains helper functions which may use Unicode-aware functions which would 11 | // otherwise be unsafe and could introduce vulnerabilities if used improperly. 12 | 13 | // asciiEqualFold is strings.EqualFold, ASCII only. It reports whether s and t 14 | // are equal, ASCII-case-insensitively. 15 | func asciiEqualFold(s, t string) bool { 16 | if len(s) != len(t) { 17 | return false 18 | } 19 | for i := 0; i < len(s); i++ { 20 | if lower(s[i]) != lower(t[i]) { 21 | return false 22 | } 23 | } 24 | return true 25 | } 26 | 27 | // lower returns the ASCII lowercase version of b. 28 | func lower(b byte) byte { 29 | if 'A' <= b && b <= 'Z' { 30 | return b + ('a' - 'A') 31 | } 32 | return b 33 | } 34 | 35 | // isASCIIPrint returns whether s is ASCII and printable according to 36 | // https://tools.ietf.org/html/rfc20#section-4.2. 37 | func isASCIIPrint(s string) bool { 38 | for i := 0; i < len(s); i++ { 39 | if s[i] < ' ' || s[i] > '~' { 40 | return false 41 | } 42 | } 43 | return true 44 | } 45 | 46 | // asciiToLower returns the lowercase version of s if s is ASCII and printable, 47 | // and whether or not it was. 48 | func asciiToLower(s string) (lower string, ok bool) { 49 | if !isASCIIPrint(s) { 50 | return "", false 51 | } 52 | return strings.ToLower(s), true 53 | } 54 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/amqp091.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's AMQP091 module 2 | # Registers zgrab2-amqp091 globally, and amqp091 with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | from . import zgrab2 8 | 9 | # Schema for connectionTune struct 10 | connection_tune = SubRecord( 11 | { 12 | "channel_max": Unsigned32BitInteger(), 13 | "frame_max": Unsigned32BitInteger(), 14 | "heartbeat": Unsigned32BitInteger(), 15 | } 16 | ) 17 | 18 | # Schema for knownServerProperties struct 19 | known_server_properties = SubRecord( 20 | { 21 | "product": String(), 22 | "version": String(), 23 | "platform": String(), 24 | "copyright": String(), 25 | "information": String(), 26 | "unknown_props": String(), 27 | } 28 | ) 29 | 30 | # Schema for Result struct 31 | result_schema = SubRecord( 32 | { 33 | "result": SubRecord( 34 | { 35 | "failure": String(), 36 | "version_major": Unsigned32BitInteger(), 37 | "version_minor": Unsigned32BitInteger(), 38 | "server_properties": known_server_properties, 39 | "locales": ListOf(String()), 40 | "auth_success": Boolean(), 41 | "tune": connection_tune, 42 | "tls": zgrab2.tls_log, 43 | } 44 | ) 45 | }, 46 | extends=zgrab2.base_scan_response, 47 | ) 48 | 49 | zschema.registry.register_schema("zgrab2-amqp091", result_schema) 50 | zgrab2.register_scan_response_type("amqp091", result_schema) 51 | -------------------------------------------------------------------------------- /integration_tests/pop3/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | MODULE_DIR=$(dirname $0) 5 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 6 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 7 | 8 | mkdir -p $ZGRAB_OUTPUT/pop3 9 | 10 | CONTAINER_NAME=zgrab_pop3 11 | 12 | OUTPUT_ROOT=$ZGRAB_OUTPUT/pop3 13 | 14 | echo "pop3/test: Tests runner for pop3" 15 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh pop3 > $OUTPUT_ROOT/banner.json 16 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh pop3 --send-quit > $OUTPUT_ROOT/banner.quit.json 17 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh pop3 --send-help --send-quit > $OUTPUT_ROOT/help.banner.quit.json 18 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh pop3 --send-noop --send-help --send-quit > $OUTPUT_ROOT/noop.help.banner.quit.json 19 | 20 | # TODO: the pop3 container does not support STARTTLS; they suggest 21 | # wrapping it in stunnel (which would handle the --pop3s case). 22 | 23 | FIELDS="help quit banner noop" 24 | status=0 25 | for field in $FIELDS; do 26 | for file in $(find $OUTPUT_ROOT -iname "*$field*.json"); do 27 | echo "check $file for $field" 28 | RESULT=$(jp data.pop3.result.$field < $file) 29 | if [ "$RESULT" = "null" ]; then 30 | echo "Did not find $field in $file [[" 31 | cat $file 32 | echo "]]" 33 | status=1 34 | fi 35 | done 36 | done 37 | 38 | # Dump the docker logs 39 | echo "pop3/test: BEGIN docker logs from $CONTAINER_NAME [{(" 40 | docker logs --tail all $CONTAINER_NAME 41 | echo ")}] END docker logs from $CONTAINER_NAME" 42 | -------------------------------------------------------------------------------- /ratelimit/ratelimit.go: -------------------------------------------------------------------------------- 1 | package ratelimit 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | "time" 8 | 9 | lru "github.com/hashicorp/golang-lru/v2/expirable" 10 | "golang.org/x/time/rate" 11 | ) 12 | 13 | // PerObjectRateLimiter manages a per-object rate limit. 14 | // It is thread-safe 15 | type PerObjectRateLimiter[K comparable] struct { 16 | *sync.Mutex 17 | limitLRU *lru.LRU[K, *rate.Limiter] 18 | } 19 | 20 | // NewPerObjectRateLimiter creates a new PerObjectRateLimiter. 21 | func NewPerObjectRateLimiter[K comparable](maxCacheSize int, cacheEntryTTL time.Duration) *PerObjectRateLimiter[K] { 22 | limiter := &PerObjectRateLimiter[K]{ 23 | Mutex: &sync.Mutex{}, 24 | } 25 | limiter.limitLRU = lru.NewLRU[K, *rate.Limiter](maxCacheSize, nil, cacheEntryTTL) // Initialize LRU cache with a maximum size 26 | return limiter 27 | } 28 | 29 | // WaitOrCreate waits for the rate limiter for the given key to allow access. 30 | // If the rate limiter does not exist, one is created using rateLimit and burstRate 31 | func (l *PerObjectRateLimiter[K]) WaitOrCreate(ctx context.Context, key K, rateLimit rate.Limit, burstRate int) error { 32 | l.Lock() 33 | limiter, ok := l.limitLRU.Get(key) 34 | if !ok { 35 | l.limitLRU.Add(key, rate.NewLimiter(rateLimit, burstRate)) // ensure limiter exists for the key 36 | limiter, ok = l.limitLRU.Get(key) 37 | if !ok { 38 | panic("unexpected error: rate limiter for key not found after adding it") 39 | } 40 | } 41 | l.Unlock() // Unlock before waiting to avoid deadlocks 42 | if err := limiter.Wait(ctx); err != nil { 43 | return fmt.Errorf("could not wait for rate limiter for key %v: %w", key, err) 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /modules/smtp/scanner_test.go: -------------------------------------------------------------------------------- 1 | package smtp 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/zmap/zgrab2" 7 | ) 8 | 9 | func TestVerifySMTPContents(t *testing.T) { 10 | type Test struct { 11 | Banner string 12 | ExpectedStatus zgrab2.ScanStatus 13 | ExpectedCode int 14 | } 15 | testTable := map[string]Test{ 16 | "success with code": { 17 | Banner: `220-some.host.com ESMTP Exim 4.93 #2 Thu, 04 Feb 2021 13:34:12 -0500 18 | 220-We do not authorize the use of this system to transport unsolicited, 19 | 220 and/or bulk e-mail.`, 20 | ExpectedStatus: zgrab2.SCAN_SUCCESS, 21 | ExpectedCode: 0, 22 | }, 23 | "success without code": { 24 | Banner: `ESMTP Exim 4.93 #2 Thu, 04 Feb 2021 13:34:12 -0500 25 | 220-We do not authorize the use of this system to transport unsolicited, 26 | 220 and/or bulk e-mail.`, 27 | ExpectedStatus: zgrab2.SCAN_SUCCESS, 28 | ExpectedCode: 0, 29 | }, 30 | "invalid protocol": { 31 | Banner: "gibberish that doesnt match expected response", 32 | ExpectedStatus: zgrab2.SCAN_PROTOCOL_ERROR, 33 | ExpectedCode: 0, 34 | }, 35 | "error response": { 36 | Banner: "500-some.host.com ESMTP something went horribly wrong.", 37 | ExpectedStatus: zgrab2.SCAN_APPLICATION_ERROR, 38 | ExpectedCode: 500, 39 | }, 40 | } 41 | 42 | for name, test := range testTable { 43 | t.Run(name, func(t *testing.T) { 44 | status, code := VerifySMTPContents(test.Banner) 45 | if status != test.ExpectedStatus { 46 | t.Errorf("recieved unexpected status: %s, wanted: %s", status, test.ExpectedStatus) 47 | } 48 | if code != test.ExpectedCode { 49 | t.Errorf("recieved unexpected code: %d, wanted: %d", code, test.ExpectedCode) 50 | } 51 | }) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/managesieve.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's managesieve module 2 | # Registers zgrab2-managesieve globally, and managesieve with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | managesieve_scan_response = SubRecord( 11 | { 12 | "result": SubRecord( 13 | { 14 | "banner": String(doc="The ManageSieve banner."), 15 | "capabilities": ListOf( 16 | String(doc="A capability advertised by the server.") 17 | ), 18 | "sieve_version": String( 19 | doc="The Sieve version advertised by the server." 20 | ), 21 | "implementation": String(doc="The server implementation string."), 22 | "starttls_supported": Boolean(), 23 | "auth_mechanisms": ListOf( 24 | String(doc="Supported SASL authentication mechanism.") 25 | ), 26 | "tls": zgrab2.tls_log, 27 | "starttls_response": String( 28 | doc="The server's response to the STARTTLS command." 29 | ), 30 | "post_tls_capabilities": ListOf( 31 | String( 32 | doc="A capability advertised by the server after TLS connection establishment." 33 | ) 34 | ), 35 | } 36 | ) 37 | }, 38 | extends=zgrab2.base_scan_response, 39 | ) 40 | 41 | zschema.registry.register_schema("zgrab2-managesieve", managesieve_scan_response) 42 | 43 | zgrab2.register_scan_response_type("managesieve", managesieve_scan_response) 44 | -------------------------------------------------------------------------------- /modules/bacnet/common.go: -------------------------------------------------------------------------------- 1 | package bacnet 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | ) 7 | 8 | const ( 9 | MAX_BACNET_FRAME_LEN = 1476 10 | ) 11 | 12 | // VLC Header constants 13 | const ( 14 | VLC_TYPE_IP byte = 0x81 15 | VLC_FUNCTION_UNICAST_NPDU byte = 0x0a 16 | ) 17 | 18 | // NPDU header constant 19 | const ( 20 | NPDU_VERSION_ASHRAE_135_1995 byte = 0x01 21 | NPDU_FLAG_EXPECTING_RESPONSE byte = 0x04 22 | ) 23 | 24 | // APDU Server Choice constants 25 | const ( 26 | SERVER_CHOICE_READ_PROPERTY byte = 0x0c 27 | ) 28 | 29 | var ( 30 | errBACNetPacketTooShort = errors.New("BACNet packet too short") 31 | errInvalidPacket = errors.New("invalid BACNet packet") 32 | errNotBACNet = errors.New("not a BACNet packet") 33 | ) 34 | 35 | func SendVLC(c net.Conn, payload []byte) error { 36 | if len(payload) > 1472 { 37 | return errors.New("payload too long") 38 | } 39 | vlc := VLC{ 40 | Type: VLC_TYPE_IP, 41 | Function: VLC_FUNCTION_UNICAST_NPDU, 42 | Length: 4 + uint16(len(payload)), 43 | } 44 | b, _ := vlc.Marshal() 45 | b = append(b, payload...) 46 | if _, err := c.Write(b); err != nil { 47 | return err 48 | } 49 | return nil 50 | } 51 | 52 | func ReadVLC(c net.Conn) (vlc *VLC, npdu *NPDU, apdu *APDU, leftovers []byte, err error, isBACNet bool) { 53 | b := make([]byte, MAX_BACNET_FRAME_LEN) 54 | n, err := c.Read(b) 55 | if err != nil { 56 | return 57 | } 58 | b = b[0:n] 59 | leftovers = b 60 | vlc = new(VLC) 61 | if leftovers, err = vlc.Unmarshal(leftovers); err != nil { 62 | return 63 | } 64 | isBACNet = true 65 | npdu = new(NPDU) 66 | if leftovers, err = npdu.Unmarshal(leftovers); err != nil { 67 | return 68 | } 69 | apdu = new(APDU) 70 | if leftovers, err = apdu.Unmarshal(leftovers); err != nil { 71 | return 72 | } 73 | return 74 | } 75 | -------------------------------------------------------------------------------- /lib/ssh/log.go: -------------------------------------------------------------------------------- 1 | /* 2 | * ZGrab Copyright 2015 Regents of the University of Michigan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy 6 | * of the License at http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 11 | * implied. See the License for the specific language governing 12 | * permissions and limitations under the License. 13 | */ 14 | 15 | package ssh 16 | 17 | // HandshakeLog contains detailed information about each step of the 18 | // SSH handshake, and can be encoded to JSON. 19 | type HandshakeLog struct { 20 | Banner string `json:"banner,omitempty"` 21 | ServerID *EndpointId `json:"server_id,omitempty"` 22 | ClientID *EndpointId `json:"client_id,omitempty"` 23 | ServerKex *kexInitMsg `json:"server_key_exchange,omitempty"` 24 | ClientKex *kexInitMsg `json:"client_key_exchange,omitempty"` 25 | AlgorithmSelection *algorithms `json:"algorithm_selection,omitempty"` 26 | KeyExchange kexAlgorithm `json:"key_exchange,omitempty"` 27 | Extensions map[string][]byte `json:"extensions,omitempty"` 28 | UserAuth []string `json:"userauth,omitempty"` 29 | Crypto *kexResult `json:"crypto,omitempty"` 30 | } 31 | 32 | type EndpointId struct { 33 | Raw string `json:"raw,omitempty"` 34 | ProtoVersion string `json:"version,omitempty"` 35 | SoftwareVersion string `json:"software,omitempty"` 36 | Comment string `json:"comment,omitempty"` 37 | } 38 | -------------------------------------------------------------------------------- /integration_tests/http/http_smoke_test/container/lighttpd.conf: -------------------------------------------------------------------------------- 1 | server.modules = ( 2 | "mod_access", 3 | "mod_alias", 4 | "mod_compress", 5 | "mod_redirect", 6 | "mod_openssl", 7 | # "mod_rewrite", 8 | ) 9 | 10 | server.document-root = "/var/lighttpd/htdocs/http" 11 | server.username = "www-data" 12 | server.groupname = "www-data" 13 | server.port = 80 14 | server.errorlog = "/var/log/lighttpd/error.log" 15 | 16 | index-file.names = ( "index.php", "index.html", "index.lighttpd.html" ) 17 | url.access-deny = ( "~", ".inc" ) 18 | static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) 19 | 20 | deflate.cache-dir = "/var/cache/lighttpd/compress/" 21 | deflate.mimetypes = ( "application/javascript", "text/css", "text/html", "text/plain" ) 22 | 23 | # default listening port for IPv6 falls back to the IPv4 port 24 | ## Use ipv6 if available 25 | #include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port 26 | include_shell "/usr/share/lighttpd/create-mime.conf.pl" 27 | include "/etc/lighttpd/conf-enabled/*.conf" 28 | 29 | debug.log-request-handling = "enable" 30 | 31 | debug.log-request-header = "enable" 32 | debug.log-response-header = "enable" 33 | debug.log-file-not-found = "enable" 34 | debug.log-condition-handling = "enable" 35 | debug.log-ssl-noise = "enable" 36 | debug.log-timeouts = "enable" 37 | 38 | var.confdir = "/var/lighttpd" 39 | 40 | $SERVER["socket"] == "0.0.0.0:443" { 41 | ssl.engine = "enable" 42 | ssl.pemfile = var.confdir + "/certs/ssl.pem" 43 | # TODO: use container name here? 44 | # server.name = "your.domain.com" 45 | server.document-root = var.confdir + "/htdocs/https" 46 | } 47 | 48 | $HTTP["url"] == "/index-redirect.html" { 49 | url.redirect = ( "" => "/index-redirect-2.html" ) 50 | } 51 | -------------------------------------------------------------------------------- /output_test.go: -------------------------------------------------------------------------------- 1 | package zgrab2 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func ExampleMapFlagsToSet_success() { 8 | output, unknowns := MapFlagsToSet(0xb, func(bit uint64) (string, error) { 9 | return fmt.Sprintf("bit0x%01x", bit), nil 10 | }) 11 | for k, v := range output { 12 | fmt.Printf("%s: %v\n", k, v) 13 | } 14 | for _, v := range unknowns { 15 | fmt.Printf("Unknown: 0x%01x", v) 16 | } 17 | // Unordered Output: 18 | // bit0x1: true 19 | // bit0x2: true 20 | // bit0x8: true 21 | } 22 | 23 | func ExampleMapFlagsToSet_error() { 24 | output, unknowns := MapFlagsToSet(0x1b, func(bit uint64) (string, error) { 25 | if bit < 0x10 { 26 | return fmt.Sprintf("bit0x%01x", bit), nil 27 | } else { 28 | return "", fmt.Errorf("Unrecognized flag 0x%02x", bit) 29 | } 30 | }) 31 | for k, v := range output { 32 | fmt.Printf("%s: %v\n", k, v) 33 | } 34 | for _, v := range unknowns { 35 | fmt.Printf("Unknown: 0x%02x", v) 36 | } 37 | // Unordered Output: 38 | // bit0x1: true 39 | // bit0x2: true 40 | // bit0x8: true 41 | // Unknown: 0x10 42 | } 43 | 44 | func ExampleFlagsToSet() { 45 | output, unknowns := FlagsToSet(0x5, WidenMapKeys(map[int]string{ 46 | 0x1: "bit0", 47 | 0x2: "bit1", 48 | 0x8: "bit3", 49 | })) 50 | for k, v := range output { 51 | fmt.Printf("%s: %v\n", k, v) 52 | } 53 | for _, v := range unknowns { 54 | fmt.Printf("Unknown: 0x%01x", v) 55 | } 56 | // Unordered Output: 57 | // bit0: true 58 | // Unknown: 0x4 59 | } 60 | 61 | func ExampleListFlagsToSet() { 62 | output, unknowns := ListFlagsToSet(0x5, []string{ 63 | "bit0", 64 | "bit1", 65 | "", 66 | "bit3", 67 | }) 68 | for k, v := range output { 69 | fmt.Printf("%s: %v\n", k, v) 70 | } 71 | for _, v := range unknowns { 72 | fmt.Printf("Unknown: 0x%01x", v) 73 | } 74 | // Unordered Output: 75 | // bit0: true 76 | // Unknown: 0x4 77 | } 78 | -------------------------------------------------------------------------------- /lib/http/cookiejar/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cookiejar_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "net/url" 11 | 12 | "github.com/zmap/zgrab2/lib/http" 13 | "github.com/zmap/zgrab2/lib/http/cookiejar" 14 | "github.com/zmap/zgrab2/lib/http/httptest" 15 | ) 16 | 17 | func ExampleNew() { 18 | // Start a server to give us cookies. 19 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 20 | if cookie, err := r.Cookie("Flavor"); err != nil { 21 | http.SetCookie(w, &http.Cookie{Name: "Flavor", Value: "Chocolate Chip"}) 22 | } else { 23 | cookie.Value = "Oatmeal Raisin" 24 | http.SetCookie(w, cookie) 25 | } 26 | })) 27 | defer ts.Close() 28 | 29 | u, err := url.Parse(ts.URL) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | // All users of cookiejar should import "golang.org/x/net/publicsuffix" 35 | jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | client := &http.Client{ 41 | Jar: jar, 42 | } 43 | 44 | if _, err = client.Get(u.String()); err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | fmt.Println("After 1st request:") 49 | for _, cookie := range jar.Cookies(u) { 50 | fmt.Printf(" %s: %s\n", cookie.Name, cookie.Value) 51 | } 52 | 53 | if _, err = client.Get(u.String()); err != nil { 54 | log.Fatal(err) 55 | } 56 | 57 | fmt.Println("After 2nd request:") 58 | for _, cookie := range jar.Cookies(u) { 59 | fmt.Printf(" %s: %s\n", cookie.Name, cookie.Value) 60 | } 61 | // Output: 62 | // After 1st request: 63 | // Flavor: Chocolate Chip 64 | // After 2nd request: 65 | // Flavor: Oatmeal Raisin 66 | } 67 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/tests.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | import collections 4 | import itertools 5 | import json 6 | import os 7 | import pprint 8 | import os.path 9 | from imp import load_source 10 | import unittest 11 | from . import zgrab2 12 | 13 | logging.basicConfig(stream=sys.stderr) 14 | 15 | logger = logging.getLogger("zgrab2-schema-tests") 16 | 17 | import zschema 18 | import zschema.registry 19 | 20 | 21 | def get_data_dir(): 22 | return os.path.join(os.path.dirname(os.path.abspath(__file__)), "testdata") 23 | 24 | 25 | def get_data_file(file): 26 | return os.path.join(get_data_dir(), file) 27 | 28 | 29 | def get_data_files(): 30 | dir = get_data_dir() 31 | files = os.listdir(dir) 32 | return [file for file in files if file.endswith(".json")] 33 | 34 | 35 | def get_schemas(): 36 | return [item for item in zgrab2.scan_response_types] 37 | 38 | 39 | class SchemaTests(unittest.TestCase): 40 | 41 | def test_schema(self): 42 | for schema in get_schemas(): 43 | logger.error("checking schema %s", schema) 44 | recname = "zgrab2-" + schema 45 | record = zschema.registry.get_schema(recname) 46 | record.to_bigquery(recname) 47 | record.to_es() 48 | record.to_flat("zgrab", schema) 49 | 50 | def test_docs(self): 51 | for schema in get_schemas(): 52 | logger.error("checking docs %s", schema) 53 | recname = "zgrab2-" + schema 54 | record = zschema.registry.get_schema(recname) 55 | record.docs_es(recname) 56 | record.docs_bq(recname) 57 | 58 | def test_validate(self): 59 | record = zschema.registry.get_schema("zgrab2") 60 | for file in get_data_files(): 61 | with open(get_data_file(file)) as fp: 62 | record.validate(json.load(fp)) 63 | -------------------------------------------------------------------------------- /lib/http/httputil/httputil.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package httputil provides HTTP utility functions, complementing the 6 | // more common ones in the net/http package. 7 | package httputil 8 | 9 | import ( 10 | "io" 11 | 12 | "github.com/zmap/zgrab2/lib/http/internal" 13 | ) 14 | 15 | // NewChunkedReader returns a new chunkedReader that translates the data read from r 16 | // out of HTTP "chunked" format before returning it. 17 | // The chunkedReader returns [io.EOF] when the final 0-length chunk is read. 18 | // 19 | // NewChunkedReader is not needed by normal applications. The http package 20 | // automatically decodes chunking when reading response bodies. 21 | func NewChunkedReader(r io.Reader) io.Reader { 22 | return internal.NewChunkedReader(r) 23 | } 24 | 25 | // NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP 26 | // "chunked" format before writing them to w. Closing the returned chunkedWriter 27 | // sends the final 0-length chunk that marks the end of the stream but does 28 | // not send the final CRLF that appears after trailers; trailers and the last 29 | // CRLF must be written separately. 30 | // 31 | // NewChunkedWriter is not needed by normal applications. The http 32 | // package adds chunking automatically if handlers don't set a 33 | // Content-Length header. Using NewChunkedWriter inside a handler 34 | // would result in double chunking or chunking with a Content-Length 35 | // length, both of which are wrong. 36 | func NewChunkedWriter(w io.Writer) io.WriteCloser { 37 | return internal.NewChunkedWriter(w) 38 | } 39 | 40 | // ErrLineTooLong is returned when reading malformed chunked data 41 | // with lines that are too long. 42 | var ErrLineTooLong = internal.ErrLineTooLong 43 | -------------------------------------------------------------------------------- /lib/http2/writesched_roundrobin_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | ) 11 | 12 | func TestRoundRobinScheduler(t *testing.T) { 13 | const maxFrameSize = 16 14 | sc := &serverConn{maxFrameSize: maxFrameSize} 15 | ws := newRoundRobinWriteScheduler() 16 | streams := make([]*stream, 4) 17 | for i := range streams { 18 | streamID := uint32(i) + 1 19 | streams[i] = &stream{ 20 | id: streamID, 21 | sc: sc, 22 | } 23 | streams[i].flow.add(1 << 20) // arbitrary large value 24 | ws.OpenStream(streamID, OpenStreamOptions{}) 25 | wr := FrameWriteRequest{ 26 | write: &writeData{ 27 | streamID: streamID, 28 | p: make([]byte, maxFrameSize*(i+1)), 29 | endStream: false, 30 | }, 31 | stream: streams[i], 32 | } 33 | ws.Push(wr) 34 | } 35 | const controlFrames = 2 36 | for i := 0; i < controlFrames; i++ { 37 | ws.Push(makeWriteNonStreamRequest()) 38 | } 39 | 40 | // We should get the control frames first. 41 | for i := 0; i < controlFrames; i++ { 42 | wr, ok := ws.Pop() 43 | if !ok || wr.StreamID() != 0 { 44 | t.Fatalf("wr.Pop() = stream %v, %v; want 0, true", wr.StreamID(), ok) 45 | } 46 | } 47 | 48 | // Each stream should write maxFrameSize bytes until it runs out of data. 49 | // Stream 1 has one frame of data, 2 has two frames, etc. 50 | want := []uint32{1, 2, 3, 4, 2, 3, 4, 3, 4, 4} 51 | var got []uint32 52 | for { 53 | wr, ok := ws.Pop() 54 | if !ok { 55 | break 56 | } 57 | if wr.DataSize() != maxFrameSize { 58 | t.Fatalf("wr.Pop() = %v data bytes, want %v", wr.DataSize(), maxFrameSize) 59 | } 60 | got = append(got, wr.StreamID()) 61 | } 62 | if !reflect.DeepEqual(got, want) { 63 | t.Fatalf("popped streams %v, want %v", got, want) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /modules/telnet/log.go: -------------------------------------------------------------------------------- 1 | /* 2 | * ZGrab Copyright 2015 Regents of the University of Michigan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy 6 | * of the License at http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 11 | * implied. See the License for the specific language governing 12 | * permissions and limitations under the License. 13 | */ 14 | 15 | package telnet 16 | 17 | // TelnetLog is the output of the telnet grab. 18 | type TelnetLog struct { 19 | // Banner is the telnet banner returned by the server. 20 | Banner string `json:"banner,omitempty"` 21 | 22 | // Will is the list of options that the server says that it will use. 23 | Will []TelnetOption `json:"will,omitempty"` 24 | 25 | // Do is the list of options that the server requests that the client use. 26 | Do []TelnetOption `json:"do,omitempty"` 27 | 28 | // Wont is the list of options that the server says it will *not* use. 29 | Wont []TelnetOption `json:"wont,omitempty"` 30 | 31 | // Dont is the list of options that the server requests the client *not* use. 32 | Dont []TelnetOption `json:"dont,omitempty"` 33 | } 34 | 35 | // isTelnet checks if this struct represents having actually detected a Telnet service. 36 | func (log *TelnetLog) isTelnet() bool { 37 | return len(log.Will) > 0 || len(log.Do) > 0 || len(log.Wont) > 0 || len(log.Dont) > 0 38 | } 39 | 40 | // getResult returns the log itself if it represents a Telnet service, otherwise, it returns nil. 41 | func (log *TelnetLog) getResult() *TelnetLog { 42 | if log == nil { 43 | return nil 44 | } 45 | if log.isTelnet() { 46 | return log 47 | } 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/modbus.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's modbus module 2 | # Registers zgrab2-modbus globally, and modbus with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | mei_object_names = [ 11 | "vendor", 12 | "product_code", 13 | "revision", 14 | "vendor_url", 15 | "product_name", 16 | "model_name", 17 | "user_application_name", 18 | ] 19 | 20 | # IDs without an explicit name are encoded as oid_(decimal id). 21 | mei_object_set = SubRecord( 22 | { 23 | i < len(mei_object_names) and mei_object_names[i] or "oid_" + str(i): String() 24 | for i in range(0, 256) 25 | } 26 | ) 27 | 28 | mei_response = SubRecord( 29 | { 30 | "conformity_level": Unsigned8BitInteger(), 31 | "more_follows": Boolean(), 32 | "next_object_id": Unsigned8BitInteger(), 33 | "object_count": Unsigned8BitInteger(), 34 | "objects": mei_object_set, 35 | } 36 | ) 37 | 38 | exception_response = SubRecord( 39 | { 40 | "exception_function": Unsigned8BitInteger(), 41 | "exception_type": Unsigned8BitInteger(), 42 | } 43 | ) 44 | 45 | modbus_scan_response = SubRecord( 46 | { 47 | "result": SubRecord( 48 | { 49 | "length": Unsigned16BitInteger(), 50 | "unit_id": Unsigned8BitInteger(), 51 | "function_code": Unsigned8BitInteger(), 52 | "raw_response": Binary(), 53 | "mei_response": mei_response, 54 | "exception_response": exception_response, 55 | "raw": Binary(), 56 | } 57 | ) 58 | }, 59 | extends=zgrab2.base_scan_response, 60 | ) 61 | 62 | zschema.registry.register_schema("zgrab2-modbus", modbus_scan_response) 63 | 64 | zgrab2.register_scan_response_type("modbus", modbus_scan_response) 65 | -------------------------------------------------------------------------------- /lib/ssh/kex_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | // Key exchange tests. 8 | 9 | import ( 10 | "crypto/rand" 11 | "reflect" 12 | "sync" 13 | "testing" 14 | ) 15 | 16 | // Runs multiple key exchanges concurrent to detect potential data races with 17 | // kex obtained from the global kexAlgoMap. 18 | // This test needs to be executed using the race detector in order to detect 19 | // race conditions. 20 | func TestKexes(t *testing.T) { 21 | type kexResultErr struct { 22 | result *kexResult 23 | err error 24 | } 25 | 26 | for name, kex := range kexAlgoMap { 27 | t.Run(name, func(t *testing.T) { 28 | wg := sync.WaitGroup{} 29 | for i := 0; i < 3; i++ { 30 | wg.Add(1) 31 | go func() { 32 | defer wg.Done() 33 | a, b := memPipe() 34 | 35 | s := make(chan kexResultErr, 1) 36 | c := make(chan kexResultErr, 1) 37 | var magics handshakeMagics 38 | config := Config{GexMinBits: 1024, GexPreferredBits: 2048, GexMaxBits: 8192} 39 | go func() { 40 | r, e := kex.Client(a, rand.Reader, &magics, &config) 41 | a.Close() 42 | c <- kexResultErr{r, e} 43 | }() 44 | go func() { 45 | r, e := kex.Server(b, rand.Reader, &magics, testSigners["ecdsa"].(AlgorithmSigner), testSigners["ecdsa"].PublicKey().Type(), &config) 46 | b.Close() 47 | s <- kexResultErr{r, e} 48 | }() 49 | 50 | clientRes := <-c 51 | serverRes := <-s 52 | if clientRes.err != nil { 53 | t.Errorf("client: %v", clientRes.err) 54 | } 55 | if serverRes.err != nil { 56 | t.Errorf("server: %v", serverRes.err) 57 | } 58 | if !reflect.DeepEqual(clientRes.result, serverRes.result) { 59 | t.Errorf("kex %q: mismatch %#v, %#v", name, clientRes.result, serverRes.result) 60 | } 61 | }() 62 | } 63 | wg.Wait() 64 | }) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /zgrab2_schemas/README.md: -------------------------------------------------------------------------------- 1 | ZGrab 2.0 schemas for zschema 2 | ============================= 3 | 4 | ## Validating 5 | 6 | [integration_tests](../integration_tests) automatically validates 7 | output from the integration tests; to manually validate a zgrab2 result, 8 | you can follow these steps: 9 | 10 | 0. Get [zschema](https://github.com/zmap/zschema) (e.g. `git clone https://github.com/zmap/zschema`) 11 | 1. Run the zschema validator: 12 | 1. Run the zschema module's main function 13 | 2. Pass it the `validate` command 14 | 3. Give the path to the zgrab2 schema [`zgrab2/__init__.py:zgrab2`](zgrab2/__init__.py) 15 | 4. Pass in the zgrab2 JSON file to validate 16 | * ``` 17 | echo 127.0.0.1 | ./cmd/zgrab2/zgrab2 mysql > output.json 18 | PYTHONPATH=/path/to/zschema python3 -m zschema validate zgrab2 output.json --path . --module zgrab2_schemas.zgrab2 19 | ``` 20 | 21 | ## Adding new module schemas 22 | 23 | There are two steps to adding a new zgrab2 module schema: 24 | 25 | 1. Add the module 26 | a. Register the response type with the zgrab2 schema 27 | 2. Register the module in `__init__.py` 28 | 29 | ### Add the module 30 | 31 | Create your python file; if your protocol identifier (the default name 32 | in the result table) is *my_protocol*, name the file `my_protocol.py` 33 | (this allows a static schema validation from `protocol_name` to `protocol_schema`; 34 | unfortunately, this means that multiple scans on a single host, or scans 35 | using custom identifiers, will not validate). 36 | 37 | Your module should include a `SubRecord` that extends from `zgrab2.base_scan_response`, 38 | specifically, overridding the `result` field. See [zgrab2/mysql.py](zgrab2/mysql.py) 39 | for an example. 40 | 41 | ### Register the module 42 | 43 | In [`zgrab2/__init__.py`](zgrab2/__init__.py), add an import for your 44 | module (e.g. `import my_protocol`). This will ensure that the module code 45 | is executed and that the response type is registered with the zgrab2 module. 46 | -------------------------------------------------------------------------------- /integration_tests/amqp091/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | VERSIONS="3.12.14 3.13.2" 5 | 6 | # Run the AMQP-specific integration tests: 7 | # 1. Run zgrab2 on the container 8 | # 2. Check that data.amqp091.result.server_properties.version matches $MQ_VERSION 9 | 10 | ZGRAB_ROOT=$(git rev-parse --show-toplevel) 11 | ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output 12 | 13 | status=0 14 | 15 | function doTest() { 16 | MQ_VERSION=$1 17 | 18 | SUFFIX="" 19 | AUTH_ARGS="" 20 | if [[ -n "$2" ]]; then 21 | AUTH_ARGS=$2 22 | SUFFIX="-auth" 23 | fi 24 | CONTAINER_NAME="zgrab_amqp091-${MQ_VERSION}" 25 | OUTPUT_FILE="$ZGRAB_OUTPUT/amqp091/${MQ_VERSION}${SUFFIX}.json" 26 | echo "amqp091/test: Testing RabbitMQ Version ${MQ_VERSION}${SUFFIX}..." 27 | CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh amqp091 $AUTH_ARGS --target-timeout 10s >$OUTPUT_FILE 28 | SERVER_VERSION=$(jp -u data.amqp091.result.server_properties.version <$OUTPUT_FILE) 29 | if [[ "$SERVER_VERSION" == "$MQ_VERSION" ]]; then 30 | echo "amqp091/test: Server version matches expected version: $SERVER_VERSION == $MQ_VERSION" 31 | else 32 | echo "amqp091/test: Server version mismatch: Got $SERVER_VERSION, expected $MQ_VERSION. Full output: [[" 33 | cat $OUTPUT_FILE 34 | echo "]]" 35 | status=1 36 | fi 37 | 38 | if [[ -n "$AUTH_ARGS" ]]; then 39 | AUTH_SUCCESS=$(jp -u data.amqp091.result.auth_success <$OUTPUT_FILE) 40 | if [[ "$AUTH_SUCCESS" == "true" ]]; then 41 | echo "amqp091/test: Auth test successful" 42 | else 43 | echo "amqp091/test: Auth test failed" 44 | status=1 45 | fi 46 | fi 47 | 48 | echo "amqp091/test: BEGIN docker+amqp091 logs from $CONTAINER_NAME [{(" 49 | docker logs --tail all $CONTAINER_NAME 50 | echo ")}] END docker+amqp091 logs from $CONTAINER_NAME" 51 | } 52 | 53 | mkdir -p $ZGRAB_OUTPUT/amqp091 54 | 55 | for version in $VERSIONS; do 56 | doTest $version 57 | doTest $version "--auth-user guest --auth-pass guest" 58 | done 59 | 60 | exit $status 61 | -------------------------------------------------------------------------------- /lib/http/mapping.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http 6 | 7 | // A mapping is a collection of key-value pairs where the keys are unique. 8 | // A zero mapping is empty and ready to use. 9 | // A mapping tries to pick a representation that makes [mapping.find] most efficient. 10 | type mapping[K comparable, V any] struct { 11 | s []entry[K, V] // for few pairs 12 | m map[K]V // for many pairs 13 | } 14 | 15 | type entry[K comparable, V any] struct { 16 | key K 17 | value V 18 | } 19 | 20 | // maxSlice is the maximum number of pairs for which a slice is used. 21 | // It is a variable for benchmarking. 22 | var maxSlice int = 8 23 | 24 | // add adds a key-value pair to the mapping. 25 | func (h *mapping[K, V]) add(k K, v V) { 26 | if h.m == nil && len(h.s) < maxSlice { 27 | h.s = append(h.s, entry[K, V]{k, v}) 28 | } else { 29 | if h.m == nil { 30 | h.m = map[K]V{} 31 | for _, e := range h.s { 32 | h.m[e.key] = e.value 33 | } 34 | h.s = nil 35 | } 36 | h.m[k] = v 37 | } 38 | } 39 | 40 | // find returns the value corresponding to the given key. 41 | // The second return value is false if there is no value 42 | // with that key. 43 | func (h *mapping[K, V]) find(k K) (v V, found bool) { 44 | if h == nil { 45 | return v, false 46 | } 47 | if h.m != nil { 48 | v, found = h.m[k] 49 | return v, found 50 | } 51 | for _, e := range h.s { 52 | if e.key == k { 53 | return e.value, true 54 | } 55 | } 56 | return v, false 57 | } 58 | 59 | // eachPair calls f for each pair in the mapping. 60 | // If f returns false, pairs returns immediately. 61 | func (h *mapping[K, V]) eachPair(f func(k K, v V) bool) { 62 | if h == nil { 63 | return 64 | } 65 | if h.m != nil { 66 | for k, v := range h.m { 67 | if !f(k, v) { 68 | return 69 | } 70 | } 71 | } else { 72 | for _, e := range h.s { 73 | if !f(e.key, e.value) { 74 | return 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tools/keys/rsa.go: -------------------------------------------------------------------------------- 1 | /* 2 | * ZGrab Copyright 2015 Regents of the University of Michigan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy 6 | * of the License at http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 11 | * implied. See the License for the specific language governing 12 | * permissions and limitations under the License. 13 | */ 14 | 15 | package keys 16 | 17 | import ( 18 | "crypto/rsa" 19 | "encoding/json" 20 | "fmt" 21 | "math/big" 22 | ) 23 | 24 | type RSAPublicKey struct { 25 | *rsa.PublicKey 26 | } 27 | 28 | type auxRSAPublicKey struct { 29 | Exponent int `json:"exponent"` 30 | Modulus []byte `json:"modulus"` 31 | Length int `json:"length"` 32 | } 33 | 34 | type RSAClientParams struct { 35 | Length uint16 `json:"length,omitempty"` 36 | EncryptedPMS []byte `json:"encrypted_pre_master_secret,omitempty"` 37 | } 38 | 39 | // MarshalJSON implements the json.Marshal interface 40 | func (rp *RSAPublicKey) MarshalJSON() ([]byte, error) { 41 | var aux auxRSAPublicKey 42 | if rp.PublicKey != nil { 43 | aux.Exponent = rp.E 44 | aux.Modulus = rp.N.Bytes() 45 | aux.Length = len(aux.Modulus) * 8 46 | } 47 | return json.Marshal(&aux) 48 | } 49 | 50 | // UnmarshalJSON implements the json.Unmarshal interface 51 | func (rp *RSAPublicKey) UnmarshalJSON(b []byte) error { 52 | var aux auxRSAPublicKey 53 | if err := json.Unmarshal(b, &aux); err != nil { 54 | return err 55 | } 56 | if rp.PublicKey == nil { 57 | rp.PublicKey = new(rsa.PublicKey) 58 | } 59 | rp.E = aux.Exponent 60 | rp.N = big.NewInt(0).SetBytes(aux.Modulus) 61 | if len(aux.Modulus)*8 != aux.Length { 62 | return fmt.Errorf("mismatched length (got %d, field specified %d)", len(aux.Modulus), aux.Length) 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/mssql.py: -------------------------------------------------------------------------------- 1 | # zschema sub-schema for zgrab2's mssql module 2 | # Registers zgrab2-mssql globally, and mssql with the main zgrab2 schema. 3 | from zschema.leaves import * 4 | from zschema.compounds import * 5 | import zschema.registry 6 | 7 | import zcrypto_schemas.zcrypto as zcrypto 8 | from . import zgrab2 9 | 10 | ENCRYPT_MODES = [ 11 | "ENCRYPT_OFF", 12 | "ENCRYPT_ON", 13 | "ENCRYPT_NOT_SUP", 14 | "ENCRYPT_REQ", 15 | "UNKNOWN", 16 | ] 17 | 18 | unknown_prelogin_option = SubRecord( 19 | { 20 | "token": Unsigned8BitInteger(), 21 | "value": Binary(), 22 | } 23 | ) 24 | 25 | prelogin_options = SubRecord( 26 | { 27 | "version": SubRecord( 28 | { 29 | "major": Unsigned8BitInteger(), 30 | "minor": Unsigned8BitInteger(), 31 | "build_number": Unsigned16BitInteger(), 32 | } 33 | ), 34 | "encrypt_mode": Enum(values=ENCRYPT_MODES), 35 | "instance": WhitespaceAnalyzedString(), 36 | "thread_id": Unsigned32BitInteger(), 37 | "mars": Unsigned8BitInteger(), 38 | "trace_id": Binary(), 39 | "fed_auth_required": Unsigned8BitInteger(), 40 | "nonce": Binary(), 41 | "unknown": ListOf(unknown_prelogin_option), 42 | } 43 | ) 44 | 45 | mssql_scan_response = SubRecord( 46 | { 47 | "result": SubRecord( 48 | { 49 | "version": WhitespaceAnalyzedString(), 50 | "instance_name": WhitespaceAnalyzedString(), 51 | "prelogin_options": prelogin_options, 52 | "encrypt_mode": Enum( 53 | values=ENCRYPT_MODES, 54 | doc="The negotiated ENCRYPT_MODE with the server.", 55 | ), 56 | "tls": zgrab2.tls_log, 57 | } 58 | ) 59 | }, 60 | extends=zgrab2.base_scan_response, 61 | ) 62 | 63 | zschema.registry.register_schema("zgrab2-mssql", mssql_scan_response) 64 | 65 | zgrab2.register_scan_response_type("mssql", mssql_scan_response) 66 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/memcached-1.6.0.json: -------------------------------------------------------------------------------- 1 | {"ip":"100.65.0.26","domain":"target","data":{"memcached":{"status":"success","protocol":"memcached","result":{"version":"1.6.0\r","libevent_version":"2.1.8-stable\r","supports_ascii":true,"stats":{"pid":1,"uptime":5043,"time":1748388275,"pointer_size":64,"rusage_user":0.879996,"rusage_system":0.356849,"curr_items":0,"total_items":0,"bytes":0,"max_connections":1024,"curr_connections":2,"total_connections":8,"rejected_connections":0,"connected_structures":3,"response_obj_oom":0,"response_obj_count":0,"response_obj_bytes":4672,"read_buf_count":0,"read_buf_bytes":65536,"read_buf_bytes_free":49152,"read_buf_oom":0,"reserved_fds":20,"proxy_conn_requests":0,"proxy_conn_errors":0,"proxy_conn_oom":0,"proxy_req_active":0,"proxy_req_await":0,"cmd_get":0,"cmd_set":0,"cmd_flush":0,"cmd_touch":0,"get_hits":0,"get_misses":0,"get_expired":0,"get_flushed":0,"delete_misses":0,"delete_hits":0,"incr_misses":0,"incr_hits":0,"decr_misses":0,"decr_hits":0,"cas_misses":0,"cas_hits":0,"cas_badval":0,"touch_hits":0,"touch_misses":0,"store_too_large":0,"store_no_memory":0,"auth_cmds":0,"auth_errors":0,"idle_kicks":0,"evictions":0,"reclaimed":0,"bytes_read":42,"bytes_written":10439,"limit_maxbytes":67108864,"accepting_conns":true,"listen_disabled_num":0,"time_in_listen_disabled_us":0,"threads":4,"conn_yields":0,"hash_power_level":16,"hash_bytes":524288,"hash_is_expanding":false,"expired_unfetched":0,"evicted_unfetched":0,"evicted_active":0,"slab_reassign_running":false,"slabs_moved":0,"crawler_reclaimed":0,"crawler_items_checked":0,"lrutail_reflocked":0,"moves_to_cold":0,"moves_to_warm":0,"moves_within_lru":0,"direct_reclaims":0,"lru_crawler_starts":3315,"lru_maintainer_juggles":5075,"slab_global_page_pool":0,"slab_reassign_rescues":0,"slab_reassign_chunk_rescues":0,"slab_reassign_inline_reclaim":0,"slab_reassign_busy_items":0,"slab_reassign_busy_nomem":0,"slab_reassign_busy_deletes":0,"log_worker_dropped":0,"log_worker_written":0,"log_watcher_skipped":0,"log_watcher_sent":0,"log_watchers":0,"unexpected_napi_ids":0,"round_robin_fallback":0}},"timestamp":"2025-05-27T23:24:37Z"}}} 2 | -------------------------------------------------------------------------------- /zgrab2_schemas/zgrab2/testdata/memcached-1.6.38.json: -------------------------------------------------------------------------------- 1 | {"ip":"100.65.0.30","domain":"target","data":{"memcached":{"status":"success","protocol":"memcached","result":{"version":"1.6.38\r","libevent_version":"2.1.12-stable\r","supports_ascii":true,"stats":{"pid":1,"uptime":5044,"time":1748388276,"pointer_size":64,"rusage_user":0.783979,"rusage_system":0.463411,"curr_items":0,"total_items":0,"bytes":0,"max_connections":1024,"curr_connections":2,"total_connections":8,"rejected_connections":0,"connected_structures":3,"response_obj_oom":0,"response_obj_count":1,"response_obj_bytes":65536,"read_buf_count":8,"read_buf_bytes":131072,"read_buf_bytes_free":49152,"read_buf_oom":0,"reserved_fds":20,"proxy_conn_requests":0,"proxy_conn_errors":0,"proxy_conn_oom":0,"proxy_req_active":0,"proxy_req_await":0,"cmd_get":0,"cmd_set":0,"cmd_flush":0,"cmd_touch":0,"get_hits":0,"get_misses":0,"get_expired":0,"get_flushed":0,"delete_misses":0,"delete_hits":0,"incr_misses":0,"incr_hits":0,"decr_misses":0,"decr_hits":0,"cas_misses":0,"cas_hits":0,"cas_badval":0,"touch_hits":0,"touch_misses":0,"store_too_large":0,"store_no_memory":0,"auth_cmds":0,"auth_errors":0,"idle_kicks":0,"evictions":0,"reclaimed":0,"bytes_read":42,"bytes_written":11034,"limit_maxbytes":67108864,"accepting_conns":true,"listen_disabled_num":0,"time_in_listen_disabled_us":0,"threads":4,"conn_yields":0,"hash_power_level":16,"hash_bytes":524288,"hash_is_expanding":false,"expired_unfetched":0,"evicted_unfetched":0,"evicted_active":0,"slab_reassign_running":false,"slabs_moved":0,"crawler_reclaimed":0,"crawler_items_checked":0,"lrutail_reflocked":0,"moves_to_cold":0,"moves_to_warm":0,"moves_within_lru":0,"direct_reclaims":0,"lru_crawler_starts":13,"lru_maintainer_juggles":5075,"slab_global_page_pool":0,"slab_reassign_rescues":0,"slab_reassign_chunk_rescues":0,"slab_reassign_inline_reclaim":0,"slab_reassign_busy_items":0,"slab_reassign_busy_nomem":0,"slab_reassign_busy_deletes":0,"log_worker_dropped":0,"log_worker_written":0,"log_watcher_skipped":0,"log_watcher_sent":0,"log_watchers":0,"unexpected_napi_ids":0,"round_robin_fallback":0}},"timestamp":"2025-05-27T23:24:37Z"}}} 2 | -------------------------------------------------------------------------------- /lib/http2/writesched_random_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package http2 6 | 7 | import "testing" 8 | 9 | func TestRandomScheduler(t *testing.T) { 10 | ws := NewRandomWriteScheduler() 11 | ws.Push(makeWriteHeadersRequest(3)) 12 | ws.Push(makeWriteHeadersRequest(4)) 13 | ws.Push(makeWriteHeadersRequest(1)) 14 | ws.Push(makeWriteHeadersRequest(2)) 15 | ws.Push(makeWriteNonStreamRequest()) 16 | ws.Push(makeWriteNonStreamRequest()) 17 | ws.Push(makeWriteRSTStream(1)) 18 | 19 | // Pop all frames. Should get the non-stream and RST stream requests first, 20 | // followed by the stream requests in any order. 21 | var order []FrameWriteRequest 22 | for { 23 | wr, ok := ws.Pop() 24 | if !ok { 25 | break 26 | } 27 | order = append(order, wr) 28 | } 29 | t.Logf("got frames: %v", order) 30 | if len(order) != 7 { 31 | t.Fatalf("got %d frames, expected 6", len(order)) 32 | } 33 | if order[0].StreamID() != 0 || order[1].StreamID() != 0 { 34 | t.Fatal("expected non-stream frames first", order[0], order[1]) 35 | } 36 | if _, ok := order[2].write.(StreamError); !ok { 37 | t.Fatal("expected RST stream frames first", order[2]) 38 | } 39 | got := make(map[uint32]bool) 40 | for _, wr := range order[2:] { 41 | got[wr.StreamID()] = true 42 | } 43 | for id := uint32(1); id <= 4; id++ { 44 | if !got[id] { 45 | t.Errorf("frame not found for stream %d", id) 46 | } 47 | } 48 | 49 | // Verify that we clean up maps for empty queues in all cases (golang.org/issue/33812) 50 | const arbitraryStreamID = 123 51 | ws.Push(makeHandlerPanicRST(arbitraryStreamID)) 52 | rws := ws.(*randomWriteScheduler) 53 | if got, want := len(rws.sq), 1; got != want { 54 | t.Fatalf("len of 123 stream = %v; want %v", got, want) 55 | } 56 | _, ok := ws.Pop() 57 | if !ok { 58 | t.Fatal("expected to be able to Pop") 59 | } 60 | if got, want := len(rws.sq), 0; got != want { 61 | t.Fatalf("len of 123 stream = %v; want %v", got, want) 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /modules/siemens/log.go: -------------------------------------------------------------------------------- 1 | package siemens 2 | 3 | // S7Log is the output type for the Siemens S7 scan. 4 | type S7Log struct { 5 | // IsS7 indicates that S7 was actually detected, so it should always be true. 6 | IsS7 bool `json:"is_s7"` 7 | 8 | // System is the first field returned in the component ID response. 9 | System string `json:"system,omitempty"` 10 | 11 | // Module is the second field returned in the component ID response. 12 | Module string `json:"module,omitempty"` 13 | 14 | // PlantId is the third field returned in the component ID response. 15 | PlantId string `json:"plant_id,omitempty"` 16 | 17 | // Copyright is the fourth field returned in the component ID response. 18 | Copyright string `json:"copyright,omitempty"` 19 | 20 | // SerialNumber is the fifth field returned in the component ID response. 21 | SerialNumber string `json:"serial_number,omitempty"` 22 | 23 | // ModuleType is the sixth field returned in the component ID response. 24 | ModuleType string `json:"module_type,omitempty"` 25 | 26 | // ReservedForOS is the seventh field returned in the component ID response. 27 | ReservedForOS string `json:"reserved_for_os,omitempty"` 28 | 29 | // MemorySerialNumber is the eighth field returned in the component ID response. 30 | MemorySerialNumber string `json:"memory_serial_number,omitempty"` 31 | 32 | // CpuProfile is the ninth field returned in the component ID response. 33 | CpuProfile string `json:"cpu_profile,omitempty"` 34 | 35 | // OemId is the tenth field returned in the component ID response. 36 | OEMId string `json:"oem_id,omitempty"` 37 | 38 | // Location is the eleventh field returned in the component ID response. 39 | Location string `json:"location,omitempty"` 40 | 41 | // ModuleId is the first field returned in the module identification response. 42 | ModuleId string `json:"module_id,omitempty"` 43 | 44 | // Hardware is the second field returned in the module identification response. 45 | Hardware string `json:"hardware,omitempty"` 46 | 47 | // Fiirmware is the third field returned in the module identification response. 48 | Firmware string `json:"firmware,omitempty"` 49 | } 50 | -------------------------------------------------------------------------------- /lib/http/httptrace/nettrace.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package nettrace contains internal hooks for tracing activity in 6 | // the net package. This package is purely internal for use by the 7 | // net/http/httptrace package and has no stable API exposed to end 8 | // users. 9 | 10 | // Phillip - This was origianally from internal/nettrace, but I moved it here so tests would function 11 | package httptrace 12 | 13 | // TraceKey is a context.Context Value key. Its associated value should 14 | // be a *Trace struct. 15 | type TraceKey struct{} 16 | 17 | // LookupIPAltResolverKey is a context.Context Value key used by tests to 18 | // specify an alternate resolver func. 19 | // It is not exposed to outsider users. (But see issue 12503) 20 | // The value should be the same type as lookupIP: 21 | // 22 | // func lookupIP(ctx context.Context, host string) ([]IPAddr, error) 23 | type LookupIPAltResolverKey struct{} 24 | 25 | // Trace contains a set of hooks for tracing events within 26 | // the net package. Any specific hook may be nil. 27 | type Trace struct { 28 | // DNSStart is called with the hostname of a DNS lookup 29 | // before it begins. 30 | DNSStart func(name string) 31 | 32 | // DNSDone is called after a DNS lookup completes (or fails). 33 | // The coalesced parameter is whether singleflight de-duped 34 | // the call. The addrs are of type net.IPAddr but can't 35 | // actually be for circular dependency reasons. 36 | DNSDone func(netIPs []any, coalesced bool, err error) 37 | 38 | // ConnectStart is called before a Dial, excluding Dials made 39 | // during DNS lookups. In the case of DualStack (Happy Eyeballs) 40 | // dialing, this may be called multiple times, from multiple 41 | // goroutines. 42 | ConnectStart func(network, addr string) 43 | 44 | // ConnectDone is called after a Dial with the results, excluding 45 | // Dials made during DNS lookups. It may also be called multiple 46 | // times, like ConnectStart. 47 | ConnectDone func(network, addr string, err error) 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/integration-test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | go-test: 11 | name: Go Test 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check out source 15 | uses: actions/checkout@v6 16 | 17 | - name: Read go version 18 | id: go_version 19 | run: | 20 | # Read the variable from the file 21 | GO_VERSION=$(grep '^go ' go.mod | awk '{print $2}') 22 | # Set the variable as an output 23 | echo "GO_VERSION=$GO_VERSION" >> $GITHUB_OUTPUT 24 | 25 | - name: Set up Go 26 | uses: actions/setup-go@v6 27 | with: 28 | go-version: ${{ steps.go_version.outputs.GO_VERSION }} 29 | 30 | - name: Build 31 | run: | 32 | go get -t ./... 33 | make 34 | 35 | - name: Test 36 | run: | 37 | make test 38 | 39 | integration-test: 40 | name: Integration Test 41 | runs-on: ubuntu-latest 42 | steps: 43 | - name: Check out source 44 | uses: actions/checkout@v6 45 | 46 | - name: Read go version 47 | id: go_version 48 | run: | 49 | # Read the variable from the file 50 | GO_VERSION=$(grep '^go ' go.mod | awk '{print $2}') 51 | # Set the variable as an output 52 | echo "GO_VERSION=$GO_VERSION" >> $GITHUB_OUTPUT 53 | 54 | - name: Set up Go 55 | uses: actions/setup-go@v6 56 | with: 57 | go-version: ${{ steps.go_version.outputs.GO_VERSION }} 58 | 59 | - name: Install dependencies 60 | run: | 61 | set -e 62 | sudo apt update 63 | # Install latest Python 64 | sudo apt install -y python3 jp python3-pip 65 | python3 -m venv venv 66 | source venv/bin/activate 67 | # Install Python dependencies 68 | pip install zschema 69 | pip install -r requirements.txt 70 | 71 | - name: Run tests 72 | run: | 73 | source venv/bin/activate 74 | make integration-test 75 | --------------------------------------------------------------------------------