mattintosh note

どこかのエンジニアモドキの備忘録

macOS で sslscan を使う…ソースからコンパイルすると inet_ntop のエラーが出るよ

以前 Qiita に掲載していたものですが Qiita を使うのを辞めたのでこちらに転機。

AWS脆弱性診断対応で各種プロトコルのチェックをする機会があったので、一通り curl でテストしようと思ったら最近の OS に載っている OpenSSL ではそもそも SSLv2 が無効になっているためテスト出来なかった。

Terminal

$ /usr/bin/curl --sslv2 https://example.com        # macOS 標準 curl
curl: (35) Your version of the OS does not support SSLv2

$ /opt/local/bin/curl --sslv2 https://example.com  # MacPorts 版 curl
curl: (4) OpenSSL was built without SSLv2 support

となるとシステム標準の OpenSSL は使えないので OpenSSL をソースからビルドする必要がありそう。これはちょっと面倒だなぁ…と思ってたら sslscan が OpenSSL のビルドとスタティックリンクに対応しているようだったので導入してみることにした。sslscan は各種パッケージマネージャからインストール出来るが Ubuntu の APT や MacPorts のバイナリはダイナミックリンクになっていて SSLv2 非対応の OpenSSL を使おうとするのでソースからビルドした。Ubuntu では特に問題なくコンパイル出来たが macOS の場合はエラーが起きたのでそのときのことをメモしておく。

sslscan って何?

GitHub - rbsec/sslscan: sslscan tests SSL/TLS enabled services to discover supported cipher suites

サポートしてる TLS のバージョンを調べたりすることができるツール。今回の要件では CloudFront や ELB/ALB の TLSv1.0 無効化対応をしなくてはならなかったので確認用として導入した。(この話はまた今度書こうかなと思っている)

実行してみると下記のように対応している Cipher や証明書の有効期限の確認などが出来る。

Terminal

$ sslscan example.com
Version: 1.11.11-rbsec-18-gef08e6f-static
OpenSSL 1.0.2-chacha (1.0.2g-dev)

Connected to 93.184.216.34

Testing SSL server example.com on port 443 using SNI name example.com

  TLS Fallback SCSV:
Server supports TLS Fallback SCSV

  TLS renegotiation:
Secure session renegotiation supported

  TLS Compression:
Compression disabled

  Heartbleed:
TLS 1.2 not vulnerable to heartbleed
TLS 1.1 not vulnerable to heartbleed
TLS 1.0 not vulnerable to heartbleed

  Supported Server Cipher(s):
Preferred TLSv1.2  128 bits  ECDHE-RSA-AES128-GCM-SHA256   Curve P-256 DHE 256
Accepted  TLSv1.2  256 bits  ECDHE-RSA-AES256-GCM-SHA384   Curve P-256 DHE 256
Accepted  TLSv1.2  128 bits  ECDHE-RSA-AES128-SHA256       Curve P-256 DHE 256
Accepted  TLSv1.2  128 bits  ECDHE-RSA-AES128-SHA          Curve P-256 DHE 256
Accepted  TLSv1.2  256 bits  ECDHE-RSA-AES256-SHA384       Curve P-256 DHE 256
Accepted  TLSv1.2  256 bits  ECDHE-RSA-AES256-SHA          Curve P-256 DHE 256
Accepted  TLSv1.2  128 bits  AES128-GCM-SHA256            
Accepted  TLSv1.2  256 bits  AES256-SHA                   
Accepted  TLSv1.2  256 bits  CAMELLIA256-SHA              
Accepted  TLSv1.2  128 bits  AES128-SHA                   
Accepted  TLSv1.2  128 bits  CAMELLIA128-SHA              
Accepted  TLSv1.2  128 bits  SEED-SHA                     
Preferred TLSv1.1  128 bits  ECDHE-RSA-AES128-SHA          Curve P-256 DHE 256
Accepted  TLSv1.1  256 bits  ECDHE-RSA-AES256-SHA          Curve P-256 DHE 256
Accepted  TLSv1.1  256 bits  AES256-SHA                   
Accepted  TLSv1.1  256 bits  CAMELLIA256-SHA              
Accepted  TLSv1.1  128 bits  AES128-SHA                   
Accepted  TLSv1.1  128 bits  CAMELLIA128-SHA              
Accepted  TLSv1.1  128 bits  SEED-SHA                     
Preferred TLSv1.0  128 bits  ECDHE-RSA-AES128-SHA          Curve P-256 DHE 256
Accepted  TLSv1.0  256 bits  ECDHE-RSA-AES256-SHA          Curve P-256 DHE 256
Accepted  TLSv1.0  256 bits  AES256-SHA                   
Accepted  TLSv1.0  256 bits  CAMELLIA256-SHA              
Accepted  TLSv1.0  128 bits  AES128-SHA                   
Accepted  TLSv1.0  128 bits  CAMELLIA128-SHA              
Accepted  TLSv1.0  128 bits  SEED-SHA                     

  SSL Certificate:
Signature Algorithm: sha256WithRSAEncryption
RSA Key Strength:    2048

Subject:  www.example.org
Altnames: DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net
Issuer:   DigiCert SHA2 High Assurance Server CA

Not valid before: Nov  3 00:00:00 2015 GMT
Not valid after:  Nov 28 12:00:00 2018 GMT

Homebrew で sslscan をインストールした場合

Homebrew では make static でインストールされるようになっているので、macOS ユーザーで「sslscan を使いたい」かつ「Homebrew を導入している」ならこれでいいと思う。

Terminal

$ /opt/hb/bin/sslscan --version
        1.11.11-static
        OpenSSL 1.0.2f  28 Jan 2016

openssl@1.1 を導入している場合はテストしていないので不明

MacPorts で sslscan をインストールした場合

依存関係にある openssl パッケージで SSLv2 が無効になっているようなのでこちらは SSLv2 のテストが出来ない。特にバリアントの設定なども無いのでこれで固定なようだ。

Terminal

$ /opt/local/bin/sslscan --version
        1.11.11
        OpenSSL 1.0.2p  14 Aug 2018
        OpenSSL version does not support SSLv2
        SSLv2 ciphers will not be detected

ソースコードから sslscan をインストールする

今回は下記の環境で検証しています。

Terminal

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.10.5
BuildVersion:   14F2511
$ git remote -v
origin  https://github.com/rbsec/sslscan (fetch)
origin  https://github.com/rbsec/sslscan (push)
$ git branch
* master
$ git log -1
commit ef08e6ffaab20e9d144436715b2ab8b24ecae54f
Author: rbsec <robin@rbsec.net>
Date:   Mon Aug 20 12:59:42 2018 +0100

    Fix missing newline in SCSV output if server only supports TLSv1.0

ソースを https://github.com/rbsec/sslscan からクローンしてくる。

Terminal

$ git clone https://github.com/rbsec/sslscan.git

README の通りに make static すると OpenSSL のビルドは成功するが、sslscan のビルドで inet_ntop 部分でエラーが出る。

Terminal

$ make static
if [ -d openssl -a -d openssl/.git ]; then \
        cd ./openssl && git checkout OpenSSL_1_0_2-stable && git pull | grep -q "Already up-to-date." && [ -e ../.openssl.is.fresh ] || touch ../.openssl.is.fresh ; \
    else \
        git clone --depth 1 -b OpenSSL_1_0_2-stable https://github.com/PeterMosmans/openssl ./openssl && cd ./openssl && touch ../.openssl.is.fresh ; \
    fi
M   apps/Makefile
M   crypto/asn1/Makefile
M   crypto/chacha/Makefile
M   crypto/evp/Makefile
M   crypto/modes/Makefile
D   krb5.h
M   ssl/Makefile
M   test/Makefile
Already on 'OpenSSL_1_0_2-stable'
Your branch is up-to-date with 'origin/OpenSSL_1_0_2-stable'.
true
/Applications/Xcode.app/Contents/Developer/usr/bin/make sslscan STATIC_BUILD=TRUE
cc -o sslscan -Wall -Wformat=2 -Wformat-security -L/tmp/sslscan/openssl/ -D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIE -I/tmp/sslscan/openssl/include/ -I/tmp/sslscan/openssl/  -DVERSION=\"1.11.11-rbsec-18-gef08e6f-static\" sslscan.c -lssl -lcrypto -lz -ldl
sslscan.c:3113:13: warning: implicit declaration of function 'inet_ntop' is invalid in C99 [-Wimplicit-function-declaration]
            inet_ntop(ai->ai_family, &options->serverAddress6.sin6_addr, options->addrstr, sizeof(options->addrstr));
            ^
1 warning generated.

inet.h/usr/include/arpa/inet.h にあるが、inet.hリポジトリ内のソースを検索してみると sslscan.c 内で __linux__ が定義されていたら読み込むようになっているらしい。

Terminal

$ grep -r /usr/include inet_ntop
/usr/include/apr-1/apr_network_io.h:     *  used in inet_ntop... */
/usr/include/arpa/inet.h:const char *inet_ntop(int, const void *, char *, socklen_t);
/usr/include/net-snmp/net-snmp-config.h:/* Define to 1 if you have the `inet_ntop' function. */
/usr/include/php/ext/standard/basic_functions.h:PHP_NAMED_FUNCTION(php_inet_ntop);
/usr/include/php/main/php_config.h:/* Define to 1 if you have the `inet_ntop' function. */

sslscan/sslscan.c

#ifdef __linux__↩
    #include <arpa/inet.h>#endif

sslscan.c を書き換えるか、CPPFLAGS="-D__linux__" をセットするとビルドが通る。ただし、最初から CPPFLAGS="-D__linux__" make static としてしまうと OpenSSL のビルドでエラーが起きるので make static で OpenSSL のビルドを済ませ、sslscan のビルドでコケた後に CPPFLAGS をセットして再度 make static する。

Terminal

$ CPPFLAGS="-D__linux__" make static
if [ -d openssl -a -d openssl/.git ]; then \
        cd ./openssl && git checkout OpenSSL_1_0_2-stable && git pull | grep -q "Already up-to-date." && [ -e ../.openssl.is.fresh ] || touch ../.openssl.is.fresh ; \
    else \
        git clone --depth 1 -b OpenSSL_1_0_2-stable https://github.com/PeterMosmans/openssl ./openssl && cd ./openssl && touch ../.openssl.is.fresh ; \
    fi
M   apps/Makefile
M   crypto/asn1/Makefile
M   crypto/chacha/Makefile
M   crypto/evp/Makefile
M   crypto/modes/Makefile
D   krb5.h
M   ssl/Makefile
M   test/Makefile
Already on 'OpenSSL_1_0_2-stable'
Your branch is up-to-date with 'origin/OpenSSL_1_0_2-stable'.
true
/Applications/Xcode.app/Contents/Developer/usr/bin/make sslscan STATIC_BUILD=TRUE
cc -o sslscan -Wall -Wformat=2 -Wformat-security -L/tmp/sslscan/openssl/ -D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIE -I/tmp/sslscan/openssl/include/ -I/tmp/sslscan/openssl/ -D__linux__ -DVERSION=\"1.11.11-rbsec-18-gef08e6f-static\" sslscan.c -lssl -lcrypto -lz -ldl

もしくは make static を手動で個別実行するか。make sslscan の場合は STATIC_BUILD=TRUE も必要になります。

Terminal

$ make openssl/libcrypto.a && CPPFALGS="-D__linux__" make static
または
$ make openssl/libcrypto.a && CPPFLAGS="-D__linux__" make sslscan STATIC_BUILD=TRUE

バージョンを確認して SSLv2 が使用できないメッセージが表示されなければ OK。

Terminal

$ ./sslscan --version
        1.11.11-rbsec-18-gef08e6f-static
        OpenSSL 1.0.2-chacha (1.0.2g-dev)

余談: git pull して更新したらビルド出来なくなった

make openssl 側に変更があったりするようなので openssl ディレクトリを一旦削除してから make static すれば良い。

余談: CPPFLAGS ではなく CFLAGS をセットした場合

※これは make に関するメモなので特に読む必要はない

CFLAGS="-D__linux__" とした場合は書き方によって動きが変わる。いずれも OpenSSL のビルドは終わっている状態。

make static の引数に CFLAGS="-D__linux__" を与えた場合

Makefile で設定される CFLAGS が最終的にコマンドラインで指定した -D__linux__ に置き換わるのでヘッダーが読み込まれずエラーになる。

Terminal

$ make static CFLAGS="-D__linux__"
:
中略
:
/Applications/Xcode.app/Contents/Developer/usr/bin/make sslscan STATIC_BUILD=TRUE
cc -o sslscan -Wall -Wformat=2 -Wformat-security -L/tmp/sslscan/openssl/ -D__linux__  -DVERSION=\"1.11.11-rbsec-18-gef08e6f-static\" sslscan.c -lssl -lcrypto -lz -ldl

環境変数CFLAGS をセットした場合

下記の4つの例ではそれぞれフラグや出来上がる実行ファイルが変わる。MacPorts ユーザでなければスタティックリンクになることもある。

Terminal

### command: 1 (MacPorts ユーザはダイナミックリンクになる) ###
$ CFLAGS="-D__linux__" make static
/Applications/Xcode.app/Contents/Developer/usr/bin/make sslscan STATIC_BUILD=TRUE
cc -o sslscan -Wall -Wformat=2 -Wformat-security -L/tmp/sslscan/openssl/ -D__linux__ -D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIE -I/usr/local/include -I/usr/local/ssl/include -I/usr/local/ssl/include/openssl -I/usr/local/opt/openssl/include -I/opt/local/include -I/opt/local/include/openssl -D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIE -I/tmp/sslscan/openssl/include/ -I/tmp/sslscan/openssl/  -DVERSION=\"1.11.11-rbsec-18-gef08e6f-static\" sslscan.c -lssl -lcrypto -lz -ldl

### command: 2 (フラグが余計に追加されるがスタティックリンクになる) ###
$ CFLAGS="-D__linux__" make static STATIC_BUILD=TRUE
/Applications/Xcode.app/Contents/Developer/usr/bin/make sslscan STATIC_BUILD=TRUE
cc -o sslscan -Wall -Wformat=2 -Wformat-security -L/private/tmp/sslscan/openssl/ -D__linux__ -D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIE -I/tmp/sslscan/openssl/include/ -I/tmp/sslscan/openssl/ -D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIE -I/private/tmp/sslscan/openssl/include/ -I/private/tmp/sslscan/openssl/  -DVERSION=\"1.11.11-rbsec-18-gef08e6f-static\" sslscan.c -lssl -lcrypto -lz -ldl
rbsec-18-gef08e6f-static\" sslscan.c -lssl -lcrypto -lz -ldl

### command: 3 (MacPorts ユーザはダイナミックリンクになる) ###
$ CFLAGS="-D__linux__" make sslscan
cc -o sslscan -Wall -Wformat=2 -Wformat-security -L/usr/local/lib -L/usr/local/ssl/lib -L/usr/local/opt/openssl/lib -L/opt/local/lib -D__linux__ -D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIE -I/usr/local/include -I/usr/local/ssl/include -I/usr/local/ssl/include/openssl -I/usr/local/opt/openssl/include -I/opt/local/include -I/opt/local/include/openssl  -DVERSION=\"1.11.11-rbsec-18-gef08e6f\" sslscan.c -lssl -lcrypto -ldl
ld: warning: directory not found for option '-L/usr/local/ssl/lib'
ld: warning: directory not found for option '-L/usr/local/opt/openssl/lib'

### command: 4 (正常。CPPFLAGS と同様) ###
$ CFLAGS="-D__linux__" make sslscan STATIC_BUILD=TRUE
cc -o sslscan -Wall -Wformat=2 -Wformat-security -L/tmp/sslscan/openssl/ -D__linux__ -D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIE -I/tmp/sslscan/openssl/include/ -I/tmp/sslscan/openssl/  -DVERSION=\"1.11.11-rbsec-18-gef08e6f-static\" sslscan.c -lssl -lcrypto -lz -ldl

これについては Makefile を見てみるとわかるが、make static の場合は sslscan の前に openssl/libcrypto.a がある。このときにはまだ STATIC_BUILD はセットされていないため FALSE になり、else 側へ落ちる。そして定義済みである CFLAGS には -I/usr/local/include -I/usr/local/ssl/include -I/usr/local/ssl/include/openssl -I/usr/local/opt/openssl/include -I/opt/local/include -I/opt/local/include/openssl によって各種パラメータが追加される。

openssl/libcrypto.a が終わって、sslscan ターゲットが実行されるときには STATIC_BUILD=TRUE がセットされているので今度は -I${PWD}/include/ -I${PWD}/CFLAGS に追加され、優先するインクルードディレクトリの順番が狂って意図しない結果になる。

MacPorts ユーザであれば /opt/local が優先されているのでそちらのヘッダーが読み込まれ、Homebrew ユーザであれば /usr/local が優先されてそちらのヘッダーを読み込んでしまうかもしれない。(私は Homebrew を /opt/hb にインストールしているので試してはいない)

sslscan/Makefile

# for static linking
ifeq ($(STATIC_BUILD), TRUE)
PWD          = $(shell pwd)/openssl
LDFLAGS      += -L${PWD}/
CFLAGS       += -I${PWD}/include/ -I${PWD}/
LIBS         = -lssl -lcrypto -lz
ifneq ($(OS), FreeBSD)
    LIBS += -ldl
endif
ifeq ($(OS), SunOS)
    LIBS += -lsocket -lnsl
endif
GIT_VERSION  := $(GIT_VERSION)-static
else
# for dynamic linking
LDFLAGS   += -L/usr/local/lib -L/usr/local/ssl/lib -L/usr/local/opt/openssl/lib -L/opt/local/lib
CFLAGS    += -I/usr/local/include -I/usr/local/ssl/include -I/usr/local/ssl/include/openssl -I/usr/local/opt/openssl/include -I/opt/local/include -I/opt/local/include/openssl
endif