题目一
1 |
|
解题思路
preg_match()
函数传入的参数只能是字符串,如果传入数组会报错,返回FALSE
。intval()
函数将变量转换为整数类型。当字符串是纯数字时,能正常转换为整数。
若字符串开头不是数字,转换结果为 0。
字符串开头为数字,会把开头的数字部分转换为整数,其他省略。
若
intval()
函数传入值为数组,当数组为空时返回 0 ,数组不为空时返回 1 。构造 Payload :
1
?num[]=1
tips:在 URL 参数名后面加上
[]
来表明这是一个数组参数,每个数组元素作为一个单独的参数键值对,参数名相同,例如:1
?num[]=1&num[]=2&num[]=3
题目二
1 |
|
解题思路
/^php$/im
:按行匹配,不区分大小写,匹配是否有一行的内容完全对应php
。/^php$/i
:不区分大小写,匹配整段字符串的内容是否为php
。注意:按行匹配是以换行符
%0A
为每行的结尾来区分每行、进行匹配的,也就是说下面这种情况只能算一行:1
?cmd=php%0A
如果是下面这种情况就算两行,也就是本题 Payload :
1
?cmd=%0Aphp
题目三
类似的题,只需要参考下列题目变换 Payload 即可。
1 |
|
解题思路
===
参考 JavaScript ,不仅数值要相等,数据类型也要相等。intval($num,0)
:当参数二为0
时,表示根据字符串格式自动选择合适的进制来完成转换。构造 Payload :
1
2
3
4
5
6
7?num=0x117C
?num=010574 (0开头是八进制)
?num=+4476
?num=4476+1-1
?num=4476.1
?num=4476e1 (科学计数法)
?num=4476' (开头为数字时会把开头的数字部分转换为数字,其他忽略)
题目三变体一
1 |
|
解题思路
==
两边分别为字符串与数字时,会将字符串转换为数字再比较。转换过程没搞懂,暂时问不清楚!
请看 Payload :
1 | ?num=0x117C |
题目三变体二
1 |
|
解题思路
又过滤了字母,还好前面总结的 Payload 够多。
请看 Payload :
1 | ?num=010574 |
题目三变体三
1 |
|
解题思路
放宽了等于的条件,又加了一个条件,strpos()
返回某字符首次出现的位置,所以必须有 0
且 0
不能出现在首位。
请看 Payload :
1 | ?num=%20010574 (加了一个空格的八进制数值,核心是加符号还有0) |
题目四
1 |
|
Payload :
1 | ?u=./flag.php |
题目五
1 |
|
解题思路
md5()
函数传入非字符串类型参数时会返回 false
,此处可传入两个数组使 md5()
函数返回 false
,于是 false === false
绕过 MD5 校验成功。
Payload:
1 | {"a[]":1,"b[]":2} # requests 脚本 data 参数 |
题目六
1 |
|
解题思路
本题有四个三元运算符:
第一个判断是否定义了 $_GET
数组,若定义了,用 $_POST
数组给 $_GET
数组引用赋值( =&
);若未定义,返回字符串 flag
(不会返回到哪里去,后面也是)。
引用赋值 =&
,类似指针,让 $_GET
和 $_POST
指向同一个数据,之后对 $_GET
的操作会直接影响 $_POST
,反之亦然。
第二个判断 $_GET
数组里的 $_GET['flag']
元素的值是否等于 flag
,结合前面的引用赋值,就是判断 $_POST[flag]
元素的值是否等于 flag
。如果等于,则用 $_COOKIE
数组给 $_GET
数组引用赋值。
第三个与第二个同理,不过最后会用 $_SERVER
数组给 $_GET
数组引用赋值。
第四个判断 $_SERVER
数组中的 $_SERVER['HTTP_FLAG']
元素的值是否等于 flag
。如果等于则会将 $flag
变量作为文件名语法高亮(其实就是找不到该文件时会报错,把 $flag
变量的值回显在前端)。
所以构造 URL 、请求体、 Cookie 与请求头字段即可得到 flag 。
Python 脚本解法:
1 | import requests |
headers = { "FLAG": "flag" }
的键名为什么不是 HTTP_FLAG
而是 FLAG
:PHP 会把请求头的所有字段转化为大写并加上前缀 HTTP_
再写入 $_SERVER
数组,每个字段都对应一个元素。$_SERVER
数组的详细介绍可查看我写的关于 $_SERVER
数组的介绍。
题目七
1 |
|
解题思路
随机生成 1 到 0x36d 也就是 877 的数字,并添加到 $allow[]
数组中。随后比对数组元素 $_GET['n']
的值是否在数组 $allow[]
中,并以 $_GET['n']
的值为文件名,将 $_POST['content']
写入文件。
此处的 in_array()
不是严格模式,会转换,根据 PHP 特性,开头为数字的字符串被转化为数字时,后面的字符串会被去掉,所以可以构造 Payload :
1 | params = {"n":"10.php"} # 那么多次随机数总该生成 10 吧 |
通过文件名 10.php
来使该文件被访问时会被当成 PHP 文件处理,从而执行恶意代码。
题目八
1 | # php7.3 |
解题思路
逻辑运算符 and
的优先级没有 &&
高,像 $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
这样写会导致 $v0
先被赋值,再进行后面的与运算(单纯的表达式,没有返回结果给任何变量)。
所以 $v2
和 $v3
不用是数字,可以把 $v2
作为要执行的语句,给 $v3
赋值为 ;
通过第二层条件判断。
Payload:
1 | ?v1=1&v2=var_dump($ctfshow)&v3=; |
$v2
后面的 ('ctfshow')$v3")
会直接报错,但 var_dump()
正常执行。
最后得到的 flag 故意没加 -
,需要自己把 0x2d
转换成 -
。
题目九
1 | # php7.3 |
php7.3
解题思路
与之前相同,and
优先级更低,所以只需满足 $v2
是数字。
此外, $v2
的前两位会被切割掉,所以前两位要补位。
然后用 call_user_func()
函数调用名称为变量 $v1
的值的函数,参数为被切割后的 $v2
。
最后将用户调用函数返回的结果写入文件,文件名为 $v3
的值。
如果是 php5 版本,这道题会容易很多,因为数字后出现的字母会被直接忽略,只要字符串开头是数字,整个字符串就会被识别为数字。但是到了本题的 php7.3 ,哪怕是十六进制数也不会被判定为数字(我尝试用十六进制与 hex2bin()
函数绕过,已失败),只能取巧解题。
1 | <?=`$_POST[0]`; |
1 | <?=`cat *`; |
第一个 Payload 的 Base64 编码的高四位与低四位转化为十六进制在 0 到 9 范围内,不会出现字母,可完美绕过 is_numeric()
函数。
第二个 Payload 删去最后两位后(影响不大),只有数字和字母 e
,被当成科学计数法从而实现绕过。
Python 脚本解法:
1 | import requests |
第一个 Payload 需要用 POST 方法发送要执行的命令,但功能完整;第二个只能查看网站根目录已有的文件的内容。
题目十
1 |
|
解题思路
既然能够用变量变量赋值,那么不妨开阔思路,让 die($error)
返回前端的结果为 flag 。
除开前面两个无法修改的 die()
,最后的两个 die()
都可以通过变量变量赋值为 flag 。不过要注意,应该先在禁止键为 $error
的 $_GET
数组的遍历中用 $flag
给 $suces
赋值,再在禁止值为 $flag
的 $_POST
数组的遍历中用 $suces
给 $error
赋值,方可得到 flag 。
Payload:
1 | data = {"error":"suces"} |