写在开头:
这里包含了 ctfshow-web 入门 - PHP 特性里面所有题目的 wp,是我在刚接触 ctf 那段时间刷的,由于当时没什么基础,wp 也是复现大佬的写法,一边做一边学也是。最近搭博客也是想着整理一下以前写的东西就给搬过来了,写的不好大家多担待些,orz

# web89


得到 flag 的途径就是让 num 中包含数字,但又会被 preg_match 给过滤掉,可以用数组的形式使 preg_match 报错,达到绕过

# web90


采用了强比较,intval 中base的位置是0的话,意味着进制将由数字的开头决定,可以采用4467的八进制或者十六进制来绕过base的位置是0的话,意味着进制将由数字的开头决定,可以采用4467的八进制或者十六进制来绕过`num==="4476"`

# web91


先是将cmdphp进行多行比较,有一次匹配对即可,再将其与php单行比较,通过换行符cmd与php进行多行比较,有一次匹配对即可,再将其与php单行比较,通过换行符%0a,将其分割,使`/^php/im 成功执行,使 /^php$/` 执行失败:

# web92


这是一个 num 和 4476 的弱比较,绕过只需将 4476 转换为 8 进制,由于有 intval 函数的存在,还需在数字前添加 0:

# web93


比上题多过滤了字母,是为了防止 e 的应用,因为 php 中 e 可以不表示科学计数法,从而以带字母数字的形式通过弱比较,而 intval 也会在 e 处停下,这里仍使用八进制解法即可

# web94


strpos 函数查到 num 中的第一个 0 出现的位置,也就是说,直接输入 010574 的话,strpos 会返回 0,而!0 的结果就是 1,所以 0 绝对不能出现在第一个位置,可以采用 4476.0%20010574%0a010574

# web95


过滤了 . 号, %20%0a 还能照常绕过

# web96


在 php 中 ./ 表示当前目录,既可以绕过弱比较,又可以正确输出 flag
也可以使用 php://filter伪协议 ,通过 base64 编码获取 flag

# web97


post 两个变量使其内容不等又是 md5 值相等,很显然是构造数组,md5 面对数组会返回 null 值

# web98


运用了 php 中的三元运算符和 & 地址引用,改换代码如下:

get 随便传参就行,改为 post

# web99


in_array 的第三个参数没有被设置,说明 n 和 allow 数组中的值会进行弱比较,根据 file_put_content 函数,写入 php 文件,由 content 写入 php 代码:

查看目录:

# web100


v0 需要是大于 0 的数数字,由于运算符优先性,= 比 and 优先,所以 v1 大于 0 即可,v2 中不能含有;而 v3 必须含有;
拼接起来的 $v2ctfshow$v3 可以将中间的字符注释掉,这样只需要由 v2 输出 $ctfshow 就行,使用 var_dump 或者 print_r 输出


0x2d- 代替就能得到 flag 字符串了

# web101


题目中给出了提示,说 flag 在 ctfshow 的类中
了解到可以用 reflectionclass 建立反射类来获取数据元的信息

# web102


其中 s 是删除了 v2 的前两个得到的,将作为 v1 的参数来获得 str 的表达式,其中,v2 应为一句话木马,最后使用 v3 将其作为文件传上去
其中,v2 必须是数字或者带 e 的数字,通过将代码 base64 编码,转 ascii 码再 16 进制转换,成功得预期数字字符串:

# web103


比上题多过滤了 str 变量中 php 的部分,不影响 v3,所以照常使用 php 伪协议,文件中的命令 <?php 一直是用 <?= 来绕过,因为带 php 构造不出预期的字符

# web104


与 md5 同理,shal 也无法处理数组

# web105


考察变量覆盖和 die 函数的特性,要绕过前面两个 die 函数,先覆盖 get:
使 suces 的地址指向 flag
再覆盖 post:
由于写的是 error=suces,不会通过强比较,但是 error 的地址又能指向 flag
这样,无论是剩下的哪个 die 都能输出 flag 的内容,由于 post 没有 flag=flag
所以先 die 了 error(die 是停止运行并输出内容):

# web106


和 web104 类似,多了 v1 和 v2 的比较,还是用数组即可:

# web107


parse_str 函数将解析 v1 中的变量,由于下面的 if 判断语句得出,应该使 v1=flag ;而后与 v3 的 md5 值比较,这里我们直接使 v3 的 md5 值为 null,这样 v2['flag'] 也为 null 就能输出 flag

# web108


strrev 函数有着倒序的作用, 0x36d 是 16 进制,转换为 10 进制就是 877,由于倒序,传入为 778 即可,ereg 函数用来匹配字符,所以 c 中需要字母可以通过截断符 %00 来防止因为有数字返回 false,同样,弱比较可以通过数字末尾带字母的方式匹配成功

# web109


看到 new 想到建立反射类

# web110


学习了一手佬的 wp,了解到还有 FilesystemIterator 这种迭代器能够差目录,配合上 getcwd() 函数,找到文件

# web111


看到题目定义了一个 getFlag 的函数,按照 preg_match 的限制 v1 应该为 ctfshow, 但是通过函数的作用,v2 不能直接输入,因为会受到内部函数无法调用外部变量的影响,所以采用了 GLOBAL 常量数组,将其变为全局变量

# web112


题目先是通过 is_file 函数判断是否为一个文件,然后用 filter 函数来进行过滤,发现没有过滤 php,考虑使用 php 伪协议来读取文件

# web113


发现过滤了 filter,那么 php 伪协议就不能用了,了解到,可以重复多次输入 /prof/self/root 这个根目录表达方式来超过 is_file 函数的输入限制,让其返回 false,还可以通过 zlib 来绕过

# web114


把上题使用的 compress 和 root 都过滤了,但发现 filter 反而没有,直接使用 php://filter伪协议

# web115


首先,会过滤掉 0x0. , e+ 这几个通过进制变换得到的东西
随后,通过 is_numeric , trim , filter 函数来判断,其中 trim() 函数会去掉 num 里的 %0a %0b %0d %20 %09 ,只有换页符 %0c 不会,其次,!== 是强比较,会比较数据类型,字符串肯定不会等于 36

# web123


可以看到,想要得到 flag 有两种方式,一种是通过 c,一种是通过 fl0g,但前面的代码已经用!isset 的方式将 fl0g 给否决掉了,只能 post fun,而且需要含有 CTF_SHOWCTF_SHOW.COM ,由于 php 特性,导致 +.[ 都会被转换成 _ ,但 [ 被转化之后,后面的符号会保持不变所以构造 payload:

# web125


过滤了一些用法,要找到不含上述符合的函数来读取,想到 highlight_file 绕过

# web126


这里没什么思路,参考了一下 wp,了解到 assert 这个断言函数,通过断言引用了aa的`_SERVER 方式,将$a变成了数组来传递$fl0g,成功绕过了 $_GET` 对于 fl0g 的限制,从而得到 flag

# web127


waf 函数获得 $url 中的脚本信息,利用 extracr 函数转化为 get 请求,由于过滤了 _ ,所以用 php 特性,将 _ 用空格来代替,构造 payload:

# web128


看到 var_dump 的参数需要经过两次函数回调的变换,而且 f1 还不能含有字母数字,看了 wp,还真是骚操作,用 _() 来代替 gettext() 函数,其作用是原封不动输出参数,符合我们的要求,还加上 get_defined_vars() 函数,用于返回所有已定义变量构成的数组:

# web129


stripos 检索 ctfshow 的位置,需要大于 0,还要考虑 readfile 函数,采用目录穿越的操作构造 /../ 来穿越目录:

# web130


preg_match 针对 ctfshow 字符串的前面做出了过滤,却没有关注后面,直接输入 ctfshow 或者后面加字符都可以绕过

# web131


在 ctfshow 前面多了 36D,运用正则表达溢出绕过 preg_match 函数,根据题目给出的提示,写了 25 万次 very

# web132


打开题目发现是个网页,网页源码没发现什么有价值的东西,通过 dirsearch 去扫描后台目录

抓到了 admin 和 asserts,进入 admin 页面,发现源码

三个变量,其中,由于 && 的优先级高于 || ,所以只要满足 $username==="admin"$code=='admin' 就可以拿到 flag,password 随便填写

# web133


在六个字符的限制下,写 flag.php 都不够用,在 wp 启发下,了解到构造
F=`$F`; + 其他命令的形式,由于字数限制, $F 肯定是会被读入的,但由于之前已经给 F 赋了值,所以被读入部分也可以视为 $F`;+其他命令 ,所有,会有其他命令被输入进去,通过 curl 外带,筛选 flag 带出

# web134


@parse_str($_SERVER['QUERY_STRING']); 查询 url 中 ? 后面的键值对,并且解析字符串,将解析后的结果设为 PHP 的超级全局数组,再通过 extract($_POST); 将数组中的键作为 POST 的变量名,数组中的值作为 POST 的值,通过传入 _POST['key1']=36d 来绕过对于 GET 和 POST 的检查

# web135


先前的 curl 外带失效了,还是自身引用自身,采用 cp 复制 flag.php 内容到 1.txt 文件中

访问 1.txt

# web136


用常规命令执行即可,但发现没有回显,运用 linux 的 tee 命令,将 ls 的内容写在文件 2 中

发现 flag 文件 f149_15_h3r3 ,重复步骤抓取文件,拿到 flag

# web137


涉及类的静态调用,直接用就行

# web138


这题冒号的输入被限制了,使用数组的方式进行绕过

# web139


和 web135 一样的题干,但是禁了 tee 命令,搬运了两个脚本来获取文件名和内容


# web140


看到最后一个 if 处是一个数字和字符串的弱比较,想要得到 True,intval 的返回值应该为 0
其中 f1 和 f2 都得是由字母数字构成的,而我们需要的 $f1($f2()) 的返回值应该为字母开头的字符串,空数组,0 或者是 False,所以可以通过构造上述几种结果,这里我选择了构造 FALSE

通过 system 函数的报错来拿到 flag

# web141


其中,v1 和 v2 随便用数字填,v3 根据 preg_match 来看可以是字母数字和下划线,在 eval 函数中绕过 return 可以添加运算符,而添加的运算符不会影响函数执行。由于 v3 的限制,可以通过取反来绕过

# web142


主要解决 sleep 的问题,直接令 v1=0 即可,这样 d=0,查看源码拿到 flag

# web143


和 web141 类似,v1 和 v2 是数字就行,v3 被过滤了字母数字,取反号和或都被过滤了,考虑用异或绕过, ;?> 代替,运算符中 + 和 - 也被过滤,用 * 即可,构造 payload:

# web144


v3 被限制成了一个字符,v2 也被 preg_match 限定了范围,v1 还是随便取数字就行,这里转换一下,将 v3 变为运算符,v2 来执行命令,此处我采用了或运算

# web145


v1 和 v2 仍旧任意取数字,v3 使用取反来构造命令,了解到,return 还可以通过?构造判断的方式来绕过: eval("return 1?system('tac f*'):1")

# web146


过滤了冒号,上题的绕过不适用了,改为 || 运算符

# web147


这题的解法主要靠 create_fuction 这个函数,其主要思路就是:

# web148


发现没有对 ^ 做过滤,考虑到异或绕过:

# web149


file_put_contents 能把字符串输入到文件中,能创建文件也能覆盖掉文件,不是 index.php 的文件都会被删除,将一句话木马直接写入 index.php 中

# web150


文件包含类型的题目,通过在 UA 处写一句话木马,然后包含 /var/log/nginx/access.log 就可以使用我们写的一句话了

# web150_plus


一样的题干,不过打了补丁,因为上题的解法为非预期解
构造 ?..CTFSHOW..=phpinfo 就可以看到 phpinfo 信息
原因是 ..CTFSHOW.. 解析变量成 __CTFSHOW__ 然后进行了变量覆盖,因为 CTFSHOW 是类就会使用 __autoload() 函数方法,去加载,因为等于 phpinfo 就会去加载 phpinfo