intval($var, $base)
intval()
函数用于获取变量的整数值。其中,$base
参数用于指定进制, base=0
表示根据 $var
开始的数字判断进制:0x
或 0X
开头的为 16 进制,0
开头的为 8 进制,其他为 10 进制
intval()
函数转换数组时,不关心数组的内容,只判断数组中有没有元素。空数组返回 0,非空数组返回 1
md5()
& sha1()
1 | if (md5($_POST['a']) === md5($_POST['b'])) |
数组不能被
md5()
和sha1()
加密,对数组加密返回null
所以可以构造如下 payload 绕过
1 | a[]=1&b[]=2 |
md5(string, true)
md5()
函数第二个参数默认为false
,返回 32 字符的十六进制数字,每个字符代表4位二进制数据,总共 128 位- 当
md5
第二个参数为true
时,返回 16 字符的原始二进制数据,每个字节由 8 位组成,总共也是 128 位,但用二进制形式紧凑地表示,而不是人类可读的十六进制文本,所以一般会有乱码
所以可以利用 md5(string, true)
构造出我们想要的字符串,脚本如下,其中 stripos
中填入我们想要构造的字符串
1 | # 构造 sql 闭合语句 ' or ' |
md5()
弱比较
在进行 ==
弱比较时,会先将两边的变量类型转化成相同的,再进行比较
0e 在比较的时候会将其视作为科学计数法,所以无论 0e 后面是什么,0 的多少次方还是 0
于是可以去寻找明文不同但 MD5 值为 “0exxxxx”,比如 QNKCDZO 和 s878926199a
变量覆盖
$$
动态变量覆盖
1 | $$a = $$b |
该语句的效果类似于将 $a
的地址指向 $b
,所以无论 $b
怎么改变值,$a
的值也会跟着改变
extract()
函数变量覆盖
extract()
函数从数组中将变量导入到当前的变量表。该函数使用数组键名作为变量名,使用数组键值作为变量值
1 |
|
该函数中第二个参数
flags
默认值为EXTR_OVERWRITE
,表示如果有重复的变量名,将会覆盖原有变量
parse_str
函数变量覆盖
parse_str()
函数把字符串解析成多个变量。其作用就是解析字符串并注册成变量
其在注册变量之前不会验证当前变量是否存在,所以直接覆盖掉已有变量
register_globals = on
变量覆盖
全局变量注册,该配置在 PHP5.3 之前,默认开启;PHP5.3 默认关闭,PHP5.6 及 5.7 已经被移除
全局变量设置开启时,传递过来的值(POST/GET/Cookie)会被直接注册为全局变量而使用,这会造成全局变量覆盖
1 | http://127.0.0.1/blfg.php?authorized=1 |
import_request_variables()
变量覆盖
该函数将 GET/POST/COOKIE 变量导入到全局作用域中,当禁止 register_golbals
却又想用一些全局变量,可以尝试使用该函数
1 | import_request_variables('G'); |
ereg()
ereg()
函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回 true,否则,则返回 false
该函数存在 NULL
%00
截断漏洞,读取到%00
就不继续读取了
==
比较运算符 ==
会进行类型转换(称为弱类型比较)
PHP 在进行弱类型比较时,会将字符串转换成整数(如果可能),然后再进行比较
PHP 会从字符串的开头开始读取,直到遇到第一个非数字字符为止。如'123abc'
会被转换成123
,'abc123'
会被转换成0
trim()
trim()
函数用于删除字符串两端的空白字符或其他预定义字符
1 | trim($num)!=='36' |
去除普通空格、制表符(%09)、换行符(%0a)、回车符(%0d)、空字节符(%00)、垂直制表符(%0b),但不去除换页符(%0c)
PHP GET/POST 字符串解析特性
PHP在解析查询字符串时,需要将所有参数转换为有效的变量名,需要做两件事:
- 删除空白符
- 将某些字符转换为下划线(包括空格
添加空白符
有些 WAF 会不让 num 参数传入字母,但是又需要传入字母来构成我们的命令,那么可以在参数之前添加一个空格,这样在 PHP 的语言特性下会默认删除这个空格,但是 WAF 会因为这个空格导致检测不到 num 这个参数,最终导致 WAF 被绕过
1 | http://node4.buuoj.cn:25591/calc.php?num=a #被拦截 |
转换 [
为下划线 _
1 | if(isset($_POST['CTF_SHOW.COM'])) |
如上例所示,通常来说,由于 PHP 中变量名只有数字字母下划线,被 GET/POST 传递的变量名中如果含有 .
+
[
会被转换成 _
但 PHP 有个特性就是如果传入
[
,他被转换为_
之后,后面的字符会被保留下来不会被替换,所以可以构造如下 payload 生成CTF_SHOW.COM
变量
1 | CTF[SHOW.COM = 1 |
get_defined_vars
该函数会返回由所有已定义变量所组成的数组
可以尝试利用该函数获取
$flag
变量
_()
函数
_()
是一个函数,它是 gettext()
函数的简写形式,其具体作用参考:关于PHP中gettext的用法
当需要构造一个函数时,但函数名的过滤条件字母数字,如下,则可以尝试使用
_()
函数绕过
1 | function check($str){ |
PHP PCRE 正则表达式匹配次数特性
PHP 为了防止正则表达式的拒绝服务攻击,给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match
将不再返回非 1
和 0
,而是 false
因此,当遇到下面的匹配机制时
1 | include("flag.php"); |
可以构建一个很大的数据绕过匹配:
1 | import requests |
PHP 内置类(原生类)& 匿名类
PHP 的类分为 内置类、用户自定义类、匿名类
内置类
PHP 原生类(Built-in Classes)是在标准 PHP 库中已经封装好的类,它们是由 PHP 本身提供的,不需要用户手动定义。在其中,有些类具有一些功能(例如文件读取、目录遍历等),这就有了利用机会,我们只需要实例化这些类,就可以实现文件读取等敏感操作
常用的内置类有如下几个
Error
: Error 是所有PHP内部错误类的基类,用于自动自定义一个Error
。该类中有一个_toString
方法,如果把它当做字符串使用,就会触发该魔术方法。例如我们对其进行输出操作echo new Error()
,此时就会自动调用__toString
魔术方法。(适用于 PHP7 及以上版本)Exception
: Exception 是所有用户级异常的基类,它与Error
类似,也存在一个_toString
方法。(适用于 PHP5 及以上版本)SplFileObject
: 该类的构造方法可以构造一个新的文件对象用于后续的读取。其大致原理为当类中__toString
魔术方法被触发时,如果类中内容为存在文件名,那么它会对此文件名进行内容获取1
2
3
4
5
$context = new SplFileObject('/etc/passwd');
echo $context;
# 上述代码会输出 `/etc/passwd` 文件的内容,但只能读取一行,想读取多行的话可以 `foreach` 遍历输出FilesystemIterator
: 该类可以理解为一个文件系统迭代器,其构造方法将会创建一个指定目录的迭代器。该类可以用于遍历指定目录中的文件名,其大致原理为当类中__toString
魔术方法被echo
等方法触发时,会返回这个迭代器的第一项,亦即返回文件名1
2
3
4
5
$dir=new FilesystemIterator("/");
foreach($dir as $f){
echo($f.'<br>');
}DirectoryIterator
: 该类可以理解为一个目录迭代器,其构造方法将会创建一个指定目录的迭代器,可以获取其指定目录下全部文件名。其利用方法类似于FilesystemIterator
GlobIterator
: 类似于DirectoryIterator
和FilesystemIterator
,但是可以通过模式匹配来寻找文件路径1
2
3
4
5
6
highlight_file(__FILE__);
$dir=new GlobIterator("/*flag*");
echo $dir;
# 匹配当前目录下所有包含 flag 的文件
匿名类
PHP7 的新特性,匿名类是一种没有类名的类,可以用来实例化对象,通过 new class
关键字来实例化匿名类
无字母数字绕过正则表达式
如果题目过滤字母数字,如下所示
1 |
|
可以通过脚本通过异或构造出 payload 绕过
如 system('ls')
可以构造为 ("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b")
具体见 无字母数字绕过正则表达式总结
php_mt_seed
php 通过 mt_srand(seed)
函数通过分发 seed 种子,依靠 mt_rand()
使用 Mersenne Twister 算法返回随机整数
1 |
|
如何生成的随机整数,它与设置的 seed 值和调用该函数的次数有关
使用 php_mt_seed 即可通过随机数进行种子的爆破
1 | time ./php_mt_seed 984489752 |
从 PHP 4.2.0 开始,随机数生成器自动播种,因此可以不使用
mt_srand
函数。当不使用随机数播种函数时,php 也会自动为随机数播种,因此是否确定种子都不会影响正常运行