ctfshow Web入门[PHP特性] web89-110 Writeup
PHP 的相关特性, 例如弱类型, 变量覆盖
web89
http://44237afb-ae1e-47ea-8b59-d43af0ddf381.challenge.ctf.show/?num[]=a
preg_match()
匹配数组时会返回 false
而 intval()
接受数组后仍返回 true
web90
这里 ===
不仅会判断内容是否相同, 而且还会判断类型是否相同
PHP 文档
当 base 为 0 时, 会检测 value 的格式来决定使用的进制
我们可以使用八进制或者十六进制绕过
或者使用 1146.0
1146aaa
, 因为 intval()
是一个取整函数, 非整数部分都会被截断, 包括字符串
payload
|
|
web91
/i
表示忽略大小写, /m
表示多行匹配, ^
表示匹配开头, $
表示匹配结尾
即多行/单行匹配以 php
为开头和结尾的字符串
多行匹配 (不匹配换行符)
|
|
单行匹配 (匹配换行符, 相当于一行)
|
|
这里我们用换行符通过第一个 if, 绕过第二个 if, 得到 flag
|
|
看了 hint 才知道这原来是一个解析漏洞…
https://blog.csdn.net/qq_46091464/article/details/108278486
https://www.leavesongs.com/PENETRATION/apache-cve-2017-15715-vulnerability.html
web92
相比于之前那题把 ===
改成了 ==
这时候用 ==
比较的时候会进行弱类型转换, 4476.0
不能用了, 但 4476.1
可以用
另外还能用科学计数法, 123e456
就是 123x10^456
, 这里因为传参是当字符串处理的, 传递 4476e123
在类型转换后会被当作科学计数法, 而在 intval()
中则是取整, 遇到字母 e
后被截断
|
|
web93
十六进制和科学计数法不能用了, 但八进制和小数点还能用
|
|
web94
注意这里的 !strpos()
, strops()
返回对应字符第一次出现的位置
如果我们使用八进制 010574
, strpos("010574", "0")
返回0, 也就是 false, 加了 !
后反而变成 true
所以字符串中必须有0, 但0不能在首位 (过滤了八进制), 可以用小数点绕过
|
|
网上 wp 中有一个加空格的方法, 可以绕过八进制的过滤
|
|
web95
小数点过滤了, 可以用空格+八进制, 空格在转换时会被自动去除
测试的时候发现前面加上其它东西也能通过
|
|
web96
类似文件包含
|
|
或者用绝对路径和相对路径
|
|
web97
md5 0e 漏洞?
如果是 ==
的话, 任意两个字符串加密后生成的 md5 为字符串类型, 进行比较的时候, 以 0e 开头的字符串例如 0e123
和 0e456
, 会被类型转换为科学计数法, 即 0==0
, 返回 true
但这里是 ===
, 两个字符串类型的 0e123
和 0e456
进行比较时, 不会进行类型转换, 只进行字符串内容的比较, 而这里很明显不相等, 返回 false
那要怎么绕过? 用数组
利用 md5 加密数组时, 会报错并返回 NULL
而无论是 NULL == NULL
还是 NULL === NULL
, 都会返回 true, 所以就通过了这个 if 的判断
web98
&
为 php 的引用
在PHP 中引用的意思是: 不同的名字访问同一个变量内容. 与C语言中的指针是有差别的. C语言中的指针里面存储的是变量的内容, 在内存中存放的地址.
首先判断 GET 是否有参数, 有的话就把 $_POST
的引用给 $_GET
(即下面的 $_GET
全部都当作 $_POST
)
然后判断 $_POST['flag']
的内容是否为 flag
, 是的话把 $_COOKIE
的引用给 $_GET
(即下面的 $_GET
全部都当作 $_COOKIE
)
然后判断 $_COOKIE['flag']
的内容是否为 flag
, 是的话把 $_SERVER
的引用给 $_GET
(即下面的 $_GET
全部都当作 $_SERVER'
)
最后判断 $_SERVER['HTTP_FLAG']
的内容是否为 flag
, 是的话输出 $flag
的内容
payload 如下
hint 的方法如下, 省去了中间两步的判断, 没有那么绕了
|
|
其实 GET 随便一个值都可以, 只要保证 POST 的内容是 HTTP_FLAG=flag
即可
web99
本地测试了一下发现当 n=123
时概率最大, 可以通过 if
但是我们需要以 n 为文件名写文件, n 的值必须是字符串
这里考察的是 in_array()
的漏洞 (其实还是弱类型转换)
|
|
in_array()
会将待搜索的值的类型自动转换为数组中的值的类型 (string to int)
所以我们构造 n=123.php
并不影响 in_array()
的查找
访问得到 flag
该漏洞的修复方式如下
|
|
web100
不太懂, 搜了一下发现考察的是运算符的优先级
https://www.php.net/manual/zh/language.operators.precedence.php
查阅文档可以知道 =
的优先级是比 and
要高的
也就是说 $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
实际上是先执行了 $v0=is_numeric($v1)
, 然后返回 true 或者 false, 之后执行 true and is_numeric($v2) and is_numeric($v3);
或者是 false and is_numeric($v2) and is_numeric($v3);
(感觉没啥意义, 因为这个表达式的结果没有赋值)
使用 var_dump()
导出 ctfshow 这个类, 注意 v2 不能含有 ;
|
|
不写注释也行, 报错之后依然输出了 flag 值
然后替换 0x2d
为 -
web101
比上题增加了更多的过滤, 而且单引号和 $
都被过滤了
ctfshow 关键词只能以字符串的形式出现, 刚好最近在学 Java 的反射, 想着 php 会不会也有…
https://www.php.net/manual/zh/class.reflectionclass.php
下划线过滤了, 不能用 var_dump()
之类的函数, 但是 ReflectionClass 存在 __toString()
的魔术方法, 可以尝试 echo 输出
|
|
hint 提示 flag 末尾少一位, 需要手工 0-f
都试一下
web102
回调函数
这里的 is_numeric()
是识别科学计数法的, 0123e45678
返回 true, 但只要包含了非 e
的其他字母则会返回 false
$v2
必须为数字, 第一时间就想到了 hex2bin()
, 但是 <
hex 的结果是 0x3c
, 含有非 e
字母
然后又想到 php://filter, 写入的时候 base64 decode 一下或许可以?
但问题是 base64 的部分字符串 hex 之后存在非 e
字母, 例如
|
|
那么我们需要找到一个合适的 payload , 使它经过 base64 + hex 之后的字符串仅包含 [0-9]
和 e
, 才能够绕过 is_numeric()
的检测
|
|
测试出来这个
|
|
hex 码前面记得补两个0 (substr)
然后访问 a.php 得到 flag
web103
加了句这个, payload 同上
web104
sha1 0e 漏洞, 而且是 ==
以下值在 sha1 加密后以 0e 开头
|
|
或者用数组绕过
然后回过头看发现好像不太对???
直接传 v1=1
v2=1
也能得到 flag
web105
很明显的变量覆盖
首先我们 GET 不能传 error=xx
, POST 不能传 xxx=flag
因为这里的 $_POST['flag']
无法进行覆盖, 如果我们想要绕过 if(!($_POST['flag']==$flag))
, 就必须要已知 flag 的值, 或者是将 $flag
设置为空, 然后 post 传递 flag=
只能是第二种办法, 但是这会清空原来 $flag
里面的内容, 于是我们需要找到一个可以输出的变量来存储原来 $flag
的值
这里我们用 $suces
来存储 flag
GET 传递 ?suces=flag&flag
=, 同时 POST flag=
注意这里 GET 的变量覆盖是按照参数传递时从左到右的顺序进行的, 所以清空 $flag
的操作一定在后面
当然 GET 不传 flag=
也行
hint 的另一种方式
|
|
web106
这里还是 ==
, 数组绕过或者用 sha1 0e
web107
还是变量覆盖
另一种方法, 数组
|
|
web108
0x36d 转换成十进制是 877
首先参数 c 必须要以字母为开头和结尾, 然后反转再取整的结果要等于 877
不过这里的 ereg()
存在截断漏洞, %00
后的字符串不解析
构造 aa%00778
来绕过 ereg()
的检测
|
|
web109
v1 v2 必须含有字母
想到了 hex2bin
base64_encode
这些函数, 但是前面有一个 new
搜了一下发现一个 stdClass, 没有任何字段和方法
本地试了一下报错了, 发现需要 __toString
魔术方法才能正常 echo 不报错
于是想到了之前有一题里用到了 php 的反射类 ReflectionClass (暂时只知道这个…)
之后就是注意闭合括号, 注释掉 v2 后面的内容
|
|
hint 的方法, 用的是 Exception 类
|
|
发现竟然不用闭合括号…
web110
过滤的挺多的…
hint 提示是 FilesystemIterator
类
利用 FilesystemIteraor
的构造方法列目录
https://www.php.net/manual/zh/filesystemiterator.construct.php
getcwd()
返回当前工作目录, 即 /var/ww/html
类里面刚好有 __toString
可以 echo 输出
|
|
flag 在 txt 里面… (本来以为是要读 php 文件的)