
이는 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에서 시작하는 것을 알 수 있습니다. 이 두 값을 사용하여 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/에 파일 시스템이 생겼습니다.
이제 분석을 조금 더 진행할 수 있습니다.
파일 시스템을 살펴보면 웹 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(...)***에 의해 실행되는 실제 코드에 연결되어 있음을 알 수 있습니다.

이 메서드들을 하나씩 살펴보던 중 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&`의 실행을 허용합니다.
mount 명령은 실패하지만 뒤따르는 ping은 실행됩니다.
공개된 패치는 폴더가 존재하는지 간단히 확인하여 특수 문자 검사를 제거함으로써 코드를 단순화합니다.

이 취약점을 악용하여 SSH를 활성화하는 코드는 fastwebEnableSSH.py에 있습니다: https://github.com/garis/Fastgate/blob/master/fastwebEnableSSH.py