
CVE-2019-12489
これは、Fastweb の Fastgate モデム/ルーターと Ghidra を使って遊んでいる際に発見した脆弱性 (CVE-2019-12489) の簡単な報告書です。 この脆弱性により、HTTP リクエストを介してコマンドインジェクションが実行可能となり、SSH シェルを有効化するために使用できます。
すべてのファームウェアファイルは http://59.0.121.191:8080/ACS-server/file/FILENAME にあります。
FILENAME は以下に等しい:
したがって、たとえば最新のファームウェアを入手するには、http://59.0.121.191:8080/ACS-server/file/0.00.89_FW_200_Askey にアクセスしてください。
ファイルを抽出するには、https://github.com/nlitsme/ubidump のユーティリティが必要です (これは私にとって最初に機能したものです)。
ファイルに対して binwalk を実行すると、次の結果が得られます:
$ binwalk 0.00.89_FW_200_Askey
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
94 0x5E JFFS2 filesystem, little endian
3276894 0x32005E UBI erase count header, version: 1, EC: 0x0, VID header offset: 0x800, data offset: 0x1000
JFFS2 ファイルシステムは不要で、UBI 部分だけが必要です。これを行うには、dd を使用してファイルをカットできます。そのためには、全体のサイズと UBI の開始位置が必要です。
ファイルサイズは ls で取得できます:
$ ls -la
total 55836
drwxr-xr-x 3 riccardo users 4096 set 24 10:07 .
drwx------ 83 riccardo users 12288 set 24 10:08 ..
-rw-r--r-- 1 riccardo users 57147506 set 24 10:05 0.00.89_FW_200_Askey
値 57147506 が必要です。
binwalk の出力を見ると、UBI 部分は 3276894 から始まっています。これら 2 つの値を使って、次のように dd を使用できます:
$ dd if=0.00.89_FW_200_Askey of=0.00.89_FW_200_Askey.UBI bs=1 skip=3276894 count=57147506
これで、ubidump を使用できます (システム構成によっては初期設定が必要です):
$ git clone https://github.com/nlitsme/ubidump.git
$ sudo pip install crcmod
$ yay -S python-lzo
$ python ubidump/ubidump.py -s bump 0.00.89_FW_200_Askey.UBI
これで、ファイルシステムが bump/rootfs_ubifs/ に展開されました。
これで分析をさらに進めることができます。
ファイルシステム内を調べてみると、Web GUI は mini_httpd によって管理されていることがわかります:
$ grep -i -r "mini_httpd"
etc/sniproxy/urlfilter3.sh:ln -sf /usr/sbin/mini_httpd $URLFILTER_WEBD_PATH
etc/sniproxy/urlfilter3.sh:$URLFILTER_WEBD_PATH -d $URLFILTER_WEB_PATH -c "**.cgi" -u admin -S -E /web/mini_httpd.pem -p $webd_https_port
bin/web.sh:#mv /tmp/mini_httpd /usr/sbin
bin/web.sh:killall -9 mini_httpd
bin/web.sh:mini_httpd -d /web -c "STATUSAPI/**.cgi|STATUSAPI/*" -u admin -T "UTF-8" -p 8080
また、mini_httpd は .cgi ファイルに依存していることがわかります:
$ grep -i -r "status.cgi"
web/factory/mtcmd/mt_info.htm: "url":"/status.cgi",
web/fw-router/app/app.js:.value('apiServiceUrl', '/status.cgi')
**web/js/status.js:var CGI_NAME='status.cgi';**
web/js/status.js: "url":"http://192.168.101.93/STATUSAPI/status.cgi",
web/js/status.js: "url":"http://192.168.101.93/STATUSAPI/status.cgi",
web/js/status.js: "url":http://192.168.101.93/STATUSAPI/status.cgi,
そこで、Ghidra を使用して status.cgi を調べ始めます。
多くの詳細は省略しますが、脆弱性は以下に説明されています。
最初に確認すべきことは、この画像のような main メソッドの存在です:

setup が呼び出されていることがわかります。少し調べてみると、run_service(...) がすべての機能の核心であるように思われます。

&service_tab を探すと、いくつかの特定の文字列が run_service(...) によって実行される実際のコードにリンクされていることがわかります。

これらのメソッドを 1 つずつ調べていくと、usb_remove にたどり着きました。

詳細:
mountVal = (char *)find_val(httpparams,"mount");
URL リクエスト内の文字列を直接変数に取り込みます。
sprintf((char *)&mountLocation,"/mnt/%s",mountVal);
URL リクエストのパラメータを元に完全なパスを作成します。
mountLocation
mountLocation はコマンド内で直接使用されます。
ここで、コマンドインジェクションを防ぐためのチェックがあります。具体的には:
invalidChars = 0x60273b7c;
\ ' ; | のような無効な文字をチェックするために使用されますが、& はリストに含まれていないため、パラメータ mount が &ping -c 10 192.168.1.172& に等しい場合、チェックは /statusapi/bin/umount -lf &ping -c 10 192.168.1.172& の実行を許可します。
マウントコマンドは失敗しますが、その後 ping が実行されます。
公開されたパッチは、単にフォルダが存在するかどうかをチェックすることで、特殊文字のチェックを排除するようにコードを簡素化しています。

この脆弱性を悪用して SSH を有効にするコードは、fastwebEnableSSH.py にあります: https://github.com/garis/Fastgate/blob/master/fastwebEnableSSH.py