PHP 函数安全
is_numeric
检测变量是否为数字或者数字字符串。
当前 PHP 最新版本的 is_numeric 会忽略 %0a 等字符,但 %00 不会被忽略。
PHP 精度
PHP 为双精度格式,IEEE 754。
简单说,IEEE 754 是一个规定了计算机如何表示和处理“浮点数”的技术标准,在 IEEE 754 标准出现之前,各家电脑公司用自己的方法表示小数,结果同一段计算程序在不同电脑上算出来的结果可能不一样。IEEE 754 就像是为计算机世界统一了“小数的语法”,确保了计算的一致性和可靠性。
IEEE 754 表示方法:
IEEE754 一般分为半精度浮点数、单精度浮点数、双精度浮点数
这双精度浮点数为例,在这个长度下会使用 1 位符号,11 位指数,52 位尾数。
- 符号:\(0\) 为正,\(1\) 为负。
- 指数:偏移量,单精度为 \(127\),双精度为 \(1023\),存储的指数 = 真实的指数 + 偏移量。
- 尾数:只存储的小数部分。(因为整数部分永远是 \(1\))。
$flag = 'flag{test}';
extract($_GET);
if (strstr($num,'1')) {die('Out!');
}
if($num==1){var_dump($flag);
}
这个例子大致一个情况就是,如果 num=1 则输出 flag,但是由于使用了 strstr 函数,如果我们输入 1 的话自然会返回 Out!,为此可以使用精度漏洞绕过去
?num=0.999999999999999999999999
比较和类型转换漏洞
PHP 包括松散比较 == 和严格比较 ===。
字符串转化成数字后会是 \(0\)。如果一个字符串要转化成数值类型,首先对字符串进行一个判断,如果字符串里面包括 e, ., E, 则会作为 float 来取值,否则作为 int 来取值。如果字符串起始部分为 数值 ,则采用 起始的数值 ,否则一律为 \(0\)。
PHP 弱类型比较
strcmp
strcmp(str1, str2)
如果 str1 小于 str2 返回 <0,如果 str1 大于 str2 返回 >0,如果 str1 等于 str2,则返回 0,如果 strcmp 比较出错后,会返回 null,null 则为 \(0\)。
为了使 strcmp 比较出错,可以传入一个数组:?str[]。
is_numeric
is_numeric 用于检测参数是否为数值,如果遇到这个函数,可以用上述转换类型的特性(< PHP 8.0.0),如果传入的是字符串,会先将字符串转换为数值。
is_switch
方法与类型转换差不多。
$a = "233a"; # 字符串转换为 223
$flag = "flag{Give you FLAG}";
switch ($a) {case 1:echo "No Flag";break;case 2:echo "No Flag";break;case 233:echo $flag;break;default:$a = 233;echo "Haha...";
}
MD5()
MD5(Message-Digest Algorithm 5)是一种广泛使用的密码散列函数,可以生成一个128位(16字节)的哈希值,通常表示为32个字符的十六进制数。
MD5 就像一个“数字指纹生成器”——它能把任意长度的数据(文件、密码、文字等)转换成一个固定长度的、唯一的“指纹字符串”。
特点:
- 固定长度输出:无论输入多大,输出永远是 \(32\) 位十六进制数(\(128\) 位二进制)。
- 确定性:相同的输入永远产生相同的输出。
- 快速计算:计算 MD5 哈希值非常快速。
- 雪崩效应:输入微小变化,输出截然不同。
函数 md5()
md5(string, var2)
计算 字符串 的 MD5 散列值,如果 var2 为真的话将返回 16 字符长度的原始二进制格式。
md5 在处理哈希字符串的时候,如果 md5 编码后的哈希值时 \(0e\) (科学计数法)开头的,都一律解释为 \(0\),所以当两个不同的值经过哈希编码后他们的值都是以 \(0e\) 开头的,则每个值都是 \(0\)。
常见的 MD5 以 0e 开头的值。
数值型:
240610708 0e462097431906509019562988736854 返回:0
314282422 0e990995504821699494520356953734 返回:0
571579406 0e972379832854295224118025748221 返回:0
903251147 0e174510503823932942361353209384 返回:0
$md5 md5($md5)
0e00275209979 0e551387587965716321018342879905
0e00506035745 0e224441551631909369101555335043
0e00540451811 0e057099852684304412663796608095
0e00678205148 0e934049274119262631743072394111
0e00741250258 0e899567782965109269932883593603
0e00928251504 0e148856674729228041723861799600
0e01350016114 0e769018222125751782256460324867
0e01352028862 0e388419153010508575572061606161
0e01392313004 0e793314107039222217518920037885
0e01875552079 0e780449305367629893512581736357
0e01975903983 0e317084484960342086618161584202
0e02042356163 0e335912055437180460060141819624
0e02218562930 0e151492820470888772364059321579
0e02451355147 0e866503534356013079241759641492
0e02739970294 0e894318228115677783240047043017
0e02760920150 0e413159393756646578537635311046
0e02784726287 0e433955189140949269100965859496
0e03298616350 0e851613188370453906408258609284
0e03393034171 0e077847024281996293485700020358
字符型:
QLTHNDT 0e405967825401955372549139051580 返回:0
QNKCDZO 0e830400451993494058024219903391 返回:0
EEIZDOI 0e782601363539291779881938479162 返回:0
TUFEPMC 0e839407194569345277863905212547 返回:0
===(数组比较)
如果 MD5 比较用的 ===,这个时候就得使用数组来绕过。如果传入一个数组的值,就会报错,报错后就相当于绕过 === 这个条件了。
在 PHP8.0.0 这个方法行不通了!
MD5 碰撞
目前看不太懂,在慢慢啃…………
sha1
一种哈希函数,算法强度比 MD5 强,但是仍不太安全。
sha1 的参数不能为数组,传入数组会返回 NULL,所以先传一个数组使得 sha1 函数报错,接着再左右两边传入不一样的内容,两边条件自然 =1,相等即可绕过。
$flag = "flag{Chain!}";
$get = $_GET['get'];
$teg = $_GET['teg'];
if ($get != $teg && sha1($get) === sha1($teg)) {
#if ($get != $teg && sha1($get) == sha1($teg)) {echo $flag;
}else{echo 'Out!';
}
?get[]=&teg[]=1。
变量覆盖漏洞
变量如果未被初始化,且能够被用户所控制,那么很可能会导致安全问题,如果传入一个参数,并且这个参数把原有的变量值给覆盖掉了则叫做变量覆盖漏洞。
$$
$$: 可变变量,简单概括就是一个可变变量获得了一个普通变量的值,举个例子。
$a = "A";
$$a = "B";
代码含义:给变量 \(a\) 赋值 "A",而 $$a 就相当于 $"A",即 $"A" = "B"。
$flag = "flag{Chain!}";
foreach ($_GET as $key => $value) {$$key = $value;
}
if ($id === "admin") {echo $flag;
}else{echo "Out!";
}
?id=admin, 将 id 付给 $key, admin 赋给 $value, $$key 即为 $id, 所以 $id === "admin"。
extract()
extract(array, flags, prefix)
本函数用来将变量从数组中导入到当前的符号表中。
检查每个键名看是否可以作为一个合法的变量名,同时也检查和符号表中已有的变量名的冲突。
array: 数组flags:
EXTR_OVERWRITE- 如果有冲突,覆盖已有的变量。(默认)
EXTR_SKIP- 如果有冲突,不覆盖已有的变量。
EXTR_PREFIX_SAME- 如果有冲突,在变量名前加上前缀 prefix。(需要 prefix 参数)prefix: 该参数规定了前缀。前缀和数组键名之间会自动加上一个下划线。
parse_str()
parse_str(string $string, array &$result): void
如果 string 是通过 URL 传入的查询字符串,则将其解析,并将 key 设置到指定 result 数组中。如果未提供 result 数组,则会将值设置为当前作用域中的变量。
register_globals
register_globals 设置为 on 的时候,传递的参数会自动注册为全局变量。
ini_set("register_globals", "On");
伪协议
后面慢慢啃……