PHP代码审计


前言

代码审计就是对各种漏洞不但要知其然,还要知其所以然。


一、代码审计定义

代码审计就是检查源代码中的安全缺陷,检查程序源代码是否存在安全隐患,或者有编码不规范的地方,通过自动化工具或者人工审查的方式,对程序源代码逐条进行检查和分析,发现这些源代码缺陷引发的安全漏洞,并提供代码修订措施和建议。
这是一项需要多方面技能的技术,包括对编程的掌握(能看懂代码),漏洞形成的原理和搭建系统和中间件等的理解

二、代码审计思路

1)逆向追踪
检查敏感函数的参数,然后回溯变量,判断变量是否可控并且没有经过严格过滤。
2)正向追踪
先找出哪些文件在接受外部传输的函数,然后跟踪变量传递的过程,观察是否有变量传入到高危函数里边,或者传递过程中是否有代码逻辑漏洞。这种正向追踪的方式,比逆向追踪挖掘得更全。
3)经验判断直接挖掘功能点漏洞
根据自身的经验判断该类应用通常在哪些功能中会出现漏洞,直接全篇阅读该功能代码。


三、漏洞实例

file_put_contents、copy、file_get_contents等读取写入操作与unlink、file_exists等删除判断文件函数之间对于路径处理的差异导致的删除绕过


<?php
$filename = __DIR__ . '/tmp/' . $user['name'];
$data = $user['info'];

file_put_contents($filename, $data);
if (file_exists($filename)) {
    unlink($filename);
}
?>

查看php源码,其实我们能发现,php读取、写入文件,都会调用php_stream_open_wrapper_ex来打开流,而判断文件存在、重命名、删除文件等操作则无需打开文件流。

我们跟一跟php_stream_open_wrapper_ex就会发现,其实最后会使用tsrm_realpath函数来将filename给标准化成一个绝对路径。而文件删除等操作则不会,这就是二者的区别。

所以,如果我们传入的是文件名中包含一个不存在的路径,写入的时候因为会处理掉“…/”等相对路径,所以不会出错;判断、删除的时候因为不会处理,所以就会出现“No such file or directory”的错误

于是乎linux可以通过xxxxx/…/test.php、test.php/.windows可以通过test.php:test test.ph<来绕过文件删除

此外发现还可以使用伪协议php://filter/resource=1.php在file_ge_contents、copy等中读取文件内容,却可以绕过文件删除

extract()、parse_str() 等变量覆盖

extract函数从数组导入变量(如$_GET、 $_POST),将数组的键名作为变量的值。而parse_str函数则是从类似name=Bill&age=60的格式字符串解析变量.如果在使用第一个函数没有设置EXTR_SKIP或者EXTR_PREFIX_SAME等处理变量冲突的参数时、第二个函数没有使用数组接受变量时将会导致变量覆盖的问题

intval()整数溢出、向下取整和整形判断的问题

浮点数精度问题导致的大小比较问题

当小数小于10^-16后,PHP对于小数就大小不分了


var_dump(1.000000000000000 == 1) >> TRUE

var_dump(1.0000000000000001 == 1) >> TRUE
is_numeric()intval()特性差异

is_numeric函数在判断是否是数字时会忽略字符串开头的’ ‘、’\t’、’\n’、’\r’、’\v’、’\f’。而’.’可以出现在任意位置,E、e能出现在参数中间,仍可以被判断为数字。也就是说is_numeric(“\r\n\t 0.1e2) >> TRUE

intval()函数会忽略’’ ‘\n’、’\r’、’\t’、’\v’、’\0’ ,也就是说intval(“\r\n\t 12) >> 12

strcmp()数组比较绕过

int strcmp ( string $ str1 , string $str2 )

参数 str1第一个字符串。str2第二个字符串。如果 str1 小于 str2 返回 < 0;

如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。

但是如果传入的两个变量是数组的话,函数会报错返回NULL,如果只是用strcmp()==0来判断的话就可以绕过

sha1()、md5() 函数传入数组比较绕过

sha1() MD5()函数默认接收的参数是字符串类型,但是如果如果传入的参数是数组的话,函数就会报错返回NULL。类似sha1($_GET[‘name’]) === sha1($_GET[‘password’])的比较就可以绕过
弱类型==比较绕过

eregi()匹配绕过

eregi()默认接收字符串参数,如果传入数组,函数会报错并返回NULL。同时还可以%00 截断进行绕过
PHP变量名不能带有点[.] 和空格,否则在会被转化为下划线[_]

parse_str("na.me=admin&pass wd=123",$test);
var_dump($test); 

array(2) {
  ["na_me"]=>
  string(5) "admin"
  ["pass_wd"]=>
  string(3) "123"

php中 = 赋值运算的优先级高于and

$c = is_numeric($a) and is_numeric($b) 程序本意是要a、b都为数字才会继续,但是当 a 为 数 字 时 , 会 先 赋 值 给 a为数字时,会先赋值给 ac,所以可能导致$b绕过检测
parse_url与libcurl对与url的解析差异可能导致ssrf

url标准的灵活性导致绕过filter_var与parse_url进行ssrf

filter_var()函数对于http://evil.com;google.com 会返回false也就是认为url格式错误,但是对于0://evil.com:80;google.com:80/ 、0://evil.com:80,google.com:80/、0://evil.com:80\google.com:80/却返回true。
通过file_get_contents获取网页内容并返回到客户端有可能造成xss

if(filter_var($argv[1], FILTER_VALIDATE_URL)) {
// parse URL
$r = parse_url($argv[1]);
print_r($r);
// check if host ends with google.com
if(preg_match(/baidu.com$/, $r[‘host’])) {
// get page from URL
$a = file_get_contents($argv[1]);
echo($a);
} else {
echo “Error: Host not allowed”;
}
} else {
echo “Error: Invalid URL;
}

虽然通过filter_var函数对url的格式进行检查,并且使用正则对url的host进行限定

但是可以通过data://baidu.com/plain;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pgo=

页面会将代码返回给客户端,就有可能造成XSS

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值