ZGrab 개요

  • ZGrab은 고속의, 모듈러방식의 어플리케이션 레이어 취약점 스캐너이다.
  • ZMap 프로젝트의 일부이다.
  • 아이콘이 카메라이고, 설명이 A Banner Grabber 였던 것을 보아, 원래는 웹 어플리케이션의 톱 페이지 등을 간단하게 확인하는 용도였던 것 같다.
  • ZGrab과 ZGrab2가 있다. ZGrab2가 기존의 ZGrab을 대체하므로 ZGrab2 사용이 추천된다(Zgrab은 DEPRECATED 상태이다).
  • Turbo Intruder 연구중에 하나의 취약점 체크 페이로드를 대량의 서버를 대상으로 체크하고 싶을 때는 ZGrab 사용을 추천한다는 내용을 보았다.
  • (반대로 하나의 서버를 대상으로 대량의 페이로드를 체크하고 싶을 때는 Burp Suite 의 확장 프로그램인 Turbo Intruder를 추천한다고 한다)
  • 실제로 사용해보면 속도가 무지하게 빠르다. 수만개 정도의 요청이라면 수분내에 끝난다.

ZGrab 설치

  • ZGrab은 대부분 Go언어로 개발되었다. (일부분은 Python으로 개발된 것 같다.)
  • 따라서 Go실행 환경을 먼저 설치할 필요가 있다.
  • 설치는 여기 포스트 를 참고로 했다.
  • 리눅스 환경에서 yum을 이용해 설치하였다.
sudo su
yum install epel-release
yum install -y golang
# Go 버전체크 
go version 
# go 버전이 18이상이면 go get 을 못쓴다고 하는 것 같다. 대신에 go install 커맨드를 사용해서 설치했다. 
go install github.com/zmap/ZGrab2@latest
# cd $GOPATH/src/github.com/zmap/ZGrab2
# go install 로 하면 설치 폴더도 달라지는 것 같다. 아래 디렉토리로 이동해서 make 를 실시했다. => 실패했다. 
cd /root/go/pkg/mod/github.com/zmap/zgrab2@v0.1.7
make 
# testify 라이브러리를 설치한 후, 다시 make 를 시도해서 성공하였다. 
go mod download github.com/stretchr/testify
make
# cmd/ZGrab2/ZGrab2에 실행 바이너리가 생성된다. 이 경로를 PATH에 추가해둔다. (.bashrc, .bash_profile)

ZGrab 사용법

기본 커맨드

zgrab2 를 실행하면 다음과 같이 추가 커맨드(모듈)를 함께 지정하라고 나온다.

zgrab2
Please specify one command of: bacnet, banner, dnp3, fox, ftp, http, imap, ipp, modbus, mongodb, mssql, multiple, mysql, ntp, oracle, pop3, postgres, redis, siemens, smb, smtp, ssh, telnet or tls
FATA[0000] could not parse flags: Please specify one command of: bacnet, banner, dnp3, fox, ftp, http, imap, ipp, modbus, mongodb, mssql, multiple, mysql, ntp, oracle, pop3, postgres, redis, siemens, smb, smtp, ssh, telnet or tls
  • 다음 처럼 모듈을 지정하고 -h 옵션을 붙이면 상세한 사용법을 알려준다.
  • 모듈마다 사용가능한 옵션이 상이하다.
  • 예를들면 http 모듈은 heartbleed 취약점을 체크할 수 있는 옵션이 있다. (–heartbleed)
zgrab2 [모듈명] -h

사용가능한 모듈

  • 사용가능한 모듈은 여러가지가 있다. 주로 프로토콜에 따라 구분되는 것 같다.
  • 영문설명은 zgrab2 모듈명 -h 커맨드 실행 결과를 참고하였다.
모듈명 영문설명 한글번역/비고
bacnet Probe for devices that speak Bacnet, commonly used for HVAC control. BACnet은 빌딩 자동제어 및 제어 네트워크의 표준 프로토콜
banner Fetch a raw banner by sending a static probe and checking the result against a regular expression 배너를 읽는다
dnp3 RProbe for DNP3, a SCADA protocol SCADA 프로토콜
fox Probe for Tridium Fox  
ftp Grab an FTP banner 파일공유 프로토콜
http Send an HTTP request and read the response, optionally following redirects.  
imap Fetch an IMAP banner, optionally over TLS  
ipp Probe for printers via IPP  
modbus Probe for Modbus devices, usually PLCs as part of a SCADA system  
mongodb Perform a handshake with a MongoDB server MongoDB서버
mssql Perform a handshake for MSSQL databases MsSQL DB서버
multiple Multiple module actions  
mysql Perform a handshake with a MySQL database MySQL DB서버
ntp Scan for NTP NTP는 시간정보 동기화 프로토콜
oracle Perform a handshake with Oracle database servers 오라클 DB서버
pop3 Fetch POP3 banners, optionally over TLS 메일 서버 pop3관련
postgres Perform a handshake with a PostgreSQL server postgres DB서버
redis Probe for Redis Redis(원격 딕셔너리 자료구조)서버
siemens Probe for Siemens S7 devices  
smb Probe for SMB servers (Windows filesharing / SAMBA) SMB는 네트워크 상 존재하는 노드들 간에 자원을 공유할 수 있도록 설계된 프로토콜
smtp Fetch an SMTP server banner, optionally over TLS 메일서버 SMTP관련
ssh Fetch an SSH server banner and collect key exchange information SSH
telnet Fetch a telnet banner 텔넷
tls Perform a TLS handshake TLS핸드셰이크 수행

HTTP 모듈

주로 HTTP 모듈을 사용하게 될 것 같다.

다음 옵션이 사용가능하다.

  • --method 플래그로 GET이외의 POST와 같은 메소드도 설정가능하다.
# zgrab2 http -h
Usage:
  zgrab2 [OPTIONS] http [http-OPTIONS]

Send an HTTP request and read the response, optionally following redirects.

Application Options:
  -o, --output-file=                    Output filename, use - for stdout (default: -)
  -f, --input-file=                     Input filename, use - for stdin (default: -)
  -m, --metadata-file=                  Metadata filename, use - for stderr (default: -)
  -l, --log-file=                       Log filename, use - for stderr (default: -)
      --source-ip=                      Local source IP address to use for making connections
  -s, --senders=                        Number of send goroutines to use (default: 1000)
      --debug                           Include debug fields in the output.
      --gomaxprocs=                     Set GOMAXPROCS (default: 0)
      --connections-per-host=           Number of times to connect to each host (results in more output) (default: 1)
      --read-limit-per-host=            Maximum total kilobytes to read for a single host (default 96kb) (default: 96)
      --prometheus=                     Address to use for Prometheus server (e.g. localhost:8080). If empty, Prometheus is disabled.

Help Options:
  -h, --help                            Show this help message

[http command options]
      -p, --port=                       Specify port to grab on (default: 80)
      -n, --name=                       Specify name for output json, only necessary if scanning multiple modules (default: http)
      -t, --timeout=                    Set connection timeout (0 = no timeout) (default: 10s)
      -g, --trigger=                    Invoke only on targets with specified tag
      -m, --maxbytes=                   Maximum byte read limit per scan (0 = defaults)
          --heartbleed                  Check if server is vulnerable to Heartbleed
          --session-ticket              Send support for TLS Session Tickets and output ticket if presented
          --extended-master-secret      Offer RFC 7627 Extended Master Secret extension
          --extended-random             Send TLS Extended Random Extension
          --no-sni                      Do not send domain name in TLS Handshake regardless of whether known
          --sct                         Request Signed Certificate Timestamps during TLS Handshake
          --keep-client-logs            Include the client-side logs in the TLS handshake
          --time=                       Explicit request time to use, instead of clock. YYYYMMDDhhmmss format.
          --certificates=               Set of certificates to present to the server
          --certificate-map=            A file mapping server names to certificates
          --root-cas=                   Set of certificates to use when verifying server certificates
          --next-protos=                A list of supported application-level protocols
          --server-name=                Server name used for certificate verification and (optionally) SNI
          --verify-server-certificate   If set, the scan will fail if the server certificate does not match the server-name, or does not
                                        chain to a trusted root.
          --cipher-suite=               A comma-delimited list of hex cipher suites to advertise.
          --min-version=                The minimum SSL/TLS version that is acceptable. 0 means that SSLv3 is the minimum.
          --max-version=                The maximum SSL/TLS version that is acceptable. 0 means use the highest supported value.
          --curve-preferences=          A list of elliptic curves used in an ECDHE handshake, in order of preference.
          --no-ecdhe                    Do not allow ECDHE handshakes
          --signature-algorithms=       Signature and hash algorithms that are acceptable
          --heartbeat-enabled           If set, include the heartbeat extension
          --dsa-enabled                 Accept server DSA keys
          --client-random=              Set an explicit Client Random (base64 encoded)
          --client-hello=               Set an explicit ClientHello (base64 encoded)
          --method=                     Set HTTP request method type (default: GET)
          --endpoint=                   Send an HTTP request to an endpoint (default: /)
          --user-agent=                 Set a custom user agent (default: Mozilla/5.0 zgrab/0.x)
          --retry-https                 If the initial request fails, reconnect and try with HTTPS.
          --max-size=                   Max kilobytes to read in response to an HTTP request (default: 256)
          --max-redirects=              Max number of redirects to follow (default: 0)
          --follow-localhost-redirects  Follow HTTP redirects to localhost
          --use-https                   Perform an HTTPS connection on the initial host
          --redirects-succeed           Redirects are always a success, even if max-redirects is exceeded
          --override-sig-hash           Override the default SignatureAndHashes TLS option with more expansive default

스캔 대상 지정하는 법

  • CSV 파일로 스캔 대상을 설정한다.
  • 콤마로 구분한다.
  • 한줄에 하나의 스캔 대상을 설정한다.
  • 다음 세가지 필드를 입력할 수 있다.
  • IP 나 DOMAIN 둘 중에 하나는 필수이다.
IP, DOMAIN, TAG
  • 다음은 모두 문제없는 입력이다.
10.0.0.1
domain.com
10.0.0.1, domain.com
10.0.0.1, domain.com, tag
10.0.0.1, , tag
, domain.com, tag
192.168.0.0/24, , tag

커맨드 예제

Zgrab2

아마 특정 경로가 존재하는지와 같은 테스트를 가장 많이 할 것 같다.

  • 먼저 스캔 대상을 target.csv로 준비해둔다.
  • 타임아웃을 5초로 하고 대상포트는 443으로 지정한다.
  • --endpoint 에 체크하고 싶은 엔드포인트를 지정한다.
sudo su
$ zgrab2 http -f targets.csv -o result.json -l log.txt -p 443 -t 5 --endpoint=/test

Zgrab1

ZGrab 에서 사용가능한 커맨드이지만 ZGrab2에서는 사용할 수 없을 것 같다.

  • 다음과 같은 식으로 다른 Zmap 프로젝트의 프로그램과 연동해서 사용하는 것 같다.
  • zmap 으로 포트스캔을 수행한 후, 그 결과을 ztee를 이용해서 csv 파일형식으로 변환해서 zgrab으로 어플리케이션 레이어의 스캔을 수행한다.
$ zmap -p 443 --output-fields=* | ztee results.csv | zgrab --port 443 --tls --http="/" --output-file=banners.json

특정 HTTP 페이로드로 테스트하는 방법

  • 특정 페이로드는 설정할 수 있는 옵션이 없다.
  • 그렇게 하고 싶으면 자기가 모듈을 개발해야 할 것 같다.

커스텀 모듈 개발하는 방법

TODO. 언젠가 조사해보자.

Rate Limit 제한

  • 기본 설정이면 상당한 리소스를 사용하는 것 같다.
  • 스캔 결과에 error: socket: too many open files 등의 메세지가 보이면 Rate Limit 제한을 검토해야 한다.
  • https://github.com/zmap/zgrab2/issues/221

ulimit

  • ulimit는 한번에 몇 개의 파일을 열 수 있는가와 관련된 수치
  • too many open files 는 이 수치를 넘었다는 것
  • 이 포스트 의 도움을 받았다.
  • 현재 수치는 ulimit -n 로 확인가능하다. 1024인 경우가 많은 것 같다.
  • 변경하려면 먼저 시스템에서 설정가능한 최대치를 확인한 후
cat /proc/sys/fs/file-max
  • 그 수치를 넘지않게 변경하고자 하는 값을 설정한다.
  • vi /etc/security/limits.conf
root hard nofile 100000
root soft nofile 100000

결과 파싱 및 필터링

  • ZGrab 결과는 기본적으로 Json 형식으로 저장된다.
  • 따라서 jq 명령어와 조합하면 궁합이 좋다.
  • 다음 필터링 형식은 자주 쓰일 것 같다.
  • http 응답 코드와 http 응답 보디에 특정 문자열이 포함되어 있는 것을 찾는다.
cat {결과파일명} | jq '.data .http .result .response | select( .status_code == 응답코드번호) | (select(.body | contains("{찾고싶은문자열}")))' 
cat {결과파일명} | jq '.data .http .result .response | select( .status_code == 200) | (select(.body))' 

참고 URL

  • https://github.com/zmap/zgrab2/wiki/HTTP
  • https://github.com/zmap/zgrab2
  • https://cmpxchg16.medium.com/scan-the-whole-internet-while-drinking-coffee-9c4085539594