CTF 题目中遇到的 PHP 考点总结(一)
介绍
本篇文章主要总结了我在写 ctfshow 题目中遇到的关于 PHP 的考点。因为只总结知识点和考点会比较空洞,也不容易理解,所以我都是通过题目来总结考点,这样的话比较容易理解。
PHP 函数特性相关
一、
考点一:intval 函数传入非空数组时会返回 1 详情可以查一下 PHP 手册。【https://www.php.net/manual/zh/function.intval.php】 考点二:preg_match()只能处理字符串,当传入的是数组时将会返回 false,详情也可以查一下 PHP 手册。
例题:
例题分析:
分析上面的代码可以看出,正则匹配 0-9,匹配到则返回 true,直接 die,但是由于 preg_match()只能处理字符串,当传入的是数组时将会返回 false,从而绕过死亡函数。由于之前没怎么了解过 intval 函数,所以我直接选择查阅 php 手册【https://www.php.net/manual/zh/function.intval.php】查阅后发现 **intval()**函数用于获取变量的整数值。**intval()**函数通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。也就是说,当给 intval()函数传入一个非空的数组时,intval()函数将会返回 1,结合我们 preg_match()传入数组返回 false 的特性,这道题的 payload 就很清楚了。
1、200 多本网络安全系列电子书 2、全套工具包 3、100 份 src 源码技术文档 4、网络安全基础入门、Linux、web 安全、攻防方面的视频 5、 网络安全学习路线 6、ctf 夺旗赛解析
二、
考点一:PHP 比较运算符 ===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较值是否相等。
考点二:intval($value,$base)当 base 为 0 时,会检测 value 的格式来决定使用的进制。
例题:
例题分析:
如下图所示,通过查询 php 手册,我们发现,intval($value,$base)当 base 为 0 时,会检测 value 的格式来决定使用的进制,所以我们可以通过把 4476 转换成 16 进制,经过 base 为 0 的 intval 函数处理,会识别 16 进制的 4476,从而返回 flag,又因为===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较值是否相等,所以由于字符串类型不同会返回 false,从而绕过死亡函数。
三、
考点一:strpos()函数查找字符串在另一字符串中第一次出现的位置并返回
考点二:intval($value,$base)当 base 为 0 时,会检测 value 的格式来决定使用的进制。
例题:
例题分析:
这道题目如果我们可以用八进制的 4476 来绕过,那么会有一个问题,因为八进制需要开头指定为 0,而 strpos()会匹配到数字 0 返回 0,!0 也就是 1 从而执行死亡函数,所以我们可以在八进制前面加一个空格,这样 strpos()会返回 1,所以我们把 4476 转换为 8 进制 10574 后,前面再加一个空格即可。
payload 如下:
四、
考点一:PHP 比较运算符 ===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较值是否相等。
考点二:在 PHP 强比较中变量 a、b 两个值不一样,要求两者 md5 值相同时的绕过方法。
考点三:PHP 中 md5 函数处理数组类型会返回 falsefalse 的特性。
例题:
例题分析:
这一道题涉及到了强比较的 md5 类型,从代码我们可以得知,要求 a、b 两个值不一样但是需要这两个值得 md5 值一样,因此强比较类型,我们可以利用 md5 函数处理数组类型会返回 false 的特性,从而利用 false=false 来绕过。我之前写过一篇总结相关知识点的文章链接如下:https://www.freebuf.com/articles/web/321300.html
payload:
五、
考点一:in_array ()函数的作用是 检查数组中是否存在某个值,而当 in_array()函数没设置第三个参数时进行的比较是弱比较。
考点二:file_put_contents()函数的作用是将一个字符串写入文件。如果写入的字符串和文件名可控则可能导致任意文件上传漏洞。
例题:
例题分析:
由于之前不怎么了解 in_array()函数所以直接查了 PHP 手册https://www.php.net/manual/zh/function.in-array.php,发现这题在使用 in_array()函数时并没有设置第三个参数为 TRUE,所以此时 in_array 函数进行的比较是==的弱类型比较。也就是会先进行强制转换成相同类型,再比较两者的值是否相等,所以当我们传入 1.php 时会强制转换成数字 1,而数字 1 正好在 range(1,24)数组中,当随机生成的数字正好是 1 时就可以绕过 in_array()函数判断,导致任意文件上传漏洞。多试几次,直到不报错的那一次,说明成功传入一句话。之后访问 1.php 再通过再通过 post 传入 1=system('ls');即可查看目录,再访问这个 flag36d.php,即 post: 1=system('cat flag36d.php');
即可在网页源码中看到 flag。
payload:
六、
考点一:**is_numeric()**函数用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE。
考点二:php 有运算的优先级,而且&& > = > and
例题:
例题分析:
**is_numeric()**函数用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE。看到最后 eval,肯定是需要命令执行,这需要$v2
传入命令,$v3
需要;
结尾,但这么一来 is_numeric 一处理就变成了
但 php 有运算的优先级,也就是&&> = > and
按照运算优先级,先执行=
也就是赋值给 $a 为 true,false 就被忽略了,思路也就有了,payload 为
得到$flag_is_1ce376300x2d8dc70x2d4b870x2d9f0e0x2d1eea5dada15;
,其中 0x2d 需要替换成-,然而一共 35 位还少了一位,最后一位需要爆破获得。
七、
考点一:is_numeric() 函数用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回 true,否则返回 false。如果字符串中含有一个 e 代表科学计数法,也可返回 true
考点二:call_user_func() 函数用于调用方法或者变量,第一个参数是被调用的函数,第二个是调用的函数的参数。
考点三:file_put_contents()函数的作用是将一个字符串写入文件。如果写入的字符串和文件名可控则可能导致任意文件上传漏洞。
考点四:通过 file_put_contents()函数配合 php://协议以 base64 编码的形式写入 webshell。
例题:
例题分析:
首先,get 传参 v2 和 v3,post 传参 v1;if 中需要 v4 为真才能往下执行,而 v4 要为真就是 v2 传的参数要为数字或者数字字符串,同时 v2 也是我们要写入的 webshell,为了让 v2 为数字或者数字字符串,我们可以先把我们的 webshell 转换为 base64 编码,再把 base64 编码转换为 16 进制,这是一种办法去转换成数字。本地测试代码如下:
说明:<?=是 php 的短标签,是 echo()的快捷用法,还有一点,就是 substr()取得是从下标为 2 开始的字符串(字符串下标从 0 开始),所以我们需要在前面加 00 两位数
所以 payload 为
八、
考点:sha1()函数特性,sha1 函数无法处理数组,遇到数组会返回 NULL
例题:
**例题分析:**sha1 函数无法处理数组,遇到数组会返回 NULL,因此将两个变量都设置成数组类型即可获得 flag。
payload 如下:
九、
考点一:parse_str()函数会将传入的第一个参数设置成变量,如果设置了第二参数,则会将第一个参数的变量以数组元素的形式存入到这个数组。
例题:
例题分析:
看完上面的代码就应该可以知道,这道题的关键就在于 parse_str()函数,于是直接查 PHP 手册中关于 parse_str()的介绍。这里附链接:https://www.php.net/parse_str/ 看完后我们可以发现该函数会将传入的第一个参数设置成变量,如果设置了第二参数,则会将第一个参数的变量以数组元素的形式存入到这个数组。分析上面的代码我们知道 v1 我们可控,并且我们知道 v2 数组中有 flag 这个键,因此我们可以通过 parse_str()函数将变量 v1 的变量名和变量值写入数组 v2,那么我们就可以覆盖掉 flag 这个键值对,并且 v3 我们可控因此就可以绕过下面 $v2['flag']==md5($v3)的比较从而输出 flag。这样思路有了,我们可以开始构造 payload,payload 如下:
十、
考点: ereg()函数的匹配可以被 %00 截断
例题:
例题分析:
**ereg()**函数搜索由指定的字符串作为由模式指定的字符串,如果发现模式则返回 true,否则返回 false。搜索对于字母字符是区分大小写的
**strrev()**函数反转字符串。**intval()**函数用于获取变量的整数值。首先我们需要知道 %00 可以截断 ereg()函数的搜索,正则表达式只会匹配 %00 之前的内容;0x36d 的十进制内容为 877,我们需要字母在前来满足 if 条件的正则匹配来跳过 if 语句,接着再进行字符串的反转得到 877a,接着 intval()函数取整数部分得到 877 所以 payload 为
十一、
考点一:**call_user_func()**函数会执行回调函数,call_user_func()把第一个参数作为回调函数,其余参数都是回调函数的参数
考点二:_()是一个函数 _()等效于 gettext() 是 gettext()的拓展函数。
考点三:get_defined_vars()函数的作用: 返回由所有已定义变量所组成的数组。
例题:
例题分析:
**call_user_func()**函数把第一个参数作为回调函数,其余参数都是回调函数的参数
_()是一个函数 _()等效于 gettext() 是 gettext()的拓展函数。开启 text 扩展,需要 php 扩展目录下有 php_gettext.dll
get_defined_vars()函数作用: 返回由所有已定义变量所组成的数组 这样可以获得 $flag
整个执行流程就是
**payload: **
十二、
考点: call_user_func()函数特性
例题一:
例题分析:
直接调用 ctfshow 类中的 getFlag 方法就好,payload 为
**补充:**call_user_func()函数在 PHP 手册中的介绍:https://www.php.net/manual/zh/function.call-user-func.php
例题二:
例题分析:
在前一题基础上把冒号给 ban 了,但call_user_func()
支持传入数组形式。
call_user_func(array($ctfshow, ‘getFlag’));这时候会调用 ctfshow 中的 getFlag 方法
所以 payload 为
评论