├── 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 |
--------------------------------------------------------------------------------