PHP代码审计
代码审计流程
1.反向查找流程
- 通过可控变量(输入点)回溯危险函数
- 查找危险函数确定可控变量
- 传递的过程中触发漏洞
反向漏洞挖掘特点
- 根源:危险函数导致漏洞
- 特点:
- 暴力:全局搜索危险函数
- 简单:无需过多理解目标网站功能与架构
- 快速:适用于自动化代码审计工具
- 命中率低:简单的漏洞越来越少
- 无法挖掘逻辑漏洞:逻辑漏洞多数不存在危险函数,或危险函数的参数“看似”不可控
- 适应性较差:不适合存在全局过滤的站点
2.正向查找流程
- 从入口点函数出发
- 找到控制器,理解URL派发规则
- 跟踪控制器调用,以理解代码为目标进行源码阅读
- 阅读代码的过程中,可能发现漏洞
正向漏洞挖掘特点
根源:程序员疏忽或逻辑问题导致漏洞
特点:
- 复杂:需要及其了解目标源码的功能与架构
- 跳跃性大:涉及M/V/C/Service/Dao等多个层面
- 漏洞的组合:通常是多个漏洞的组合,很可能存在逻辑相关的漏洞
- 潜力无限:安全研究人员的宝库
3.双向查找流程
- 略读代码,了解架构
- 是否有全局过滤机制?
- 有:是否可以绕过?
- 可以:寻找漏洞触发点。
- 不可以:寻找没有过滤的变量。
- 没有:那么它是如何处理的?
- 完全没有处理:可以挖成筛子。
- 有处理::找遗漏的处理点。
- 有:是否可以绕过?
- 找到了漏洞点,漏洞利用是否有坑?
- 否:成功利用!
- 是:利用所知的语言知识(trick)解决问题。
双向漏洞挖掘特点:
- 根源:理解程序执行过程,找寻危险逻辑
- 特点:
- 高效:如挖隧道,双向开工,时间减半
- 知识面广:需要同时掌握正向、反向挖掘技巧,并进行结合
- 以及所有正向、反向的优点
SQL注入漏洞挖掘技巧
- PHP+MySQL连接方式
- MySQL(废弃)
- Mysqli[^1]
- PDO[^2]
- SQL注入漏洞常见过滤方法
- intval[^3]/addslashes[^4] /mysql_real_escape(三个函数)
1 | <?php |
1 | <?php |

mysqli_escape_string /mysqli_real_escape_string / mysqli::escape_string
PDO::quote
参数化查询
常见注入过滤绕过方法
intval
addslashes /mysql_real_escape
宽字符注入
寻找字符串转换函数
- urldecode
- base64_decode
1
2
3
4
5
6<?php
$id = addslashes($_GET['id']);
$id = base64_decode($id); //当出现这个语句,那么只要传入的是base64加密语法,不存在引号,并且存在解码base64,那么就是绕过了
$sql = "SELECT * FROM abc WHERE id = '$id' " //字符型- iconv
- json_decode
- stripshasles
- simple_xml_loadstring
mysqli::escape_string / PDO::quote
与addslashes区别:是否会主动加引号包裹
1
2
3
4<?php
$id = addslashes($_GET['id']);
$sql = "SELECT * FROM abc WHERE id = $id " //数字型,不存在引号,addslashes无效宽字符注入
参数化查询
- 寻找非SQL值位置(下面的?处为SQL值,其他为非SQL值)
- SELECT `name` FROM `users` WHERE `id` = ? ORDER BY `login_time` LIMIT 1
实例
- 寻找遗漏过滤的利用点
- 案例:贷齐乐系统header注入
- 略读代码,了解架构
- 非MVC架构
- 全局GPC转义
- 目标:找到没有进行过滤的输入点
- 结果:$_SERVER[HTTP_*]均无过滤,导致注入
- 入手点
- 开发者不熟悉的边缘功能
- 常被复制粘贴代码的功能
- 寻找过滤无效的利用点
- 案例:ThinkSNS某版本SOL注入漏洞
- 略读代码,了解架构
- 基于ThinkPHP3.1开发
- MVC架构
- 利用t函数过滤变量
- 目标:找到t函数过滤完成以后也可以注入的点
- 结果:表名位置SQL注入漏洞
1 | function t($text){ |
- 寻找过滤可被绕过的利用点
- 案例:Metinfo企业网站管理系统SOL注入漏洞
- 略读代码,了解架构
- 非MVC架构
- 全局覆盖的方式注册变量
- 全局GPC转义
- 目标:获取绕过全局GPC的方法
- 结果:利用base64_decode来引入单引号
思路总结
- 开发者容易遗漏的输入点
HTTP头
X-Forwarded-For
User-Agent
Referer
PHP SELF
REQUEST URI
文件名 $_FILES[][name]
php://input
- 引入单引号(转义符)的方法
- stripslashes
- base64_decode
- urldecode
- substr
- iconv
- str_replace(‘0’,”,$sql)
- xml
- json encode
实践
(1)存在SQL注入漏洞
1 |
|
(2)不存在SQL注入漏洞,不考虑宽字节
1 |
|
(3)存在SQL注入漏洞,未设置ENT_QUOTES参数,单引号不会被转换
1 | ’htmlspecialchars( |
& (& 符号) |
& |
|---|---|
| 字符 | 替换后 |
" (双引号) |
",除非设置了 ENT_NOQUOTES |
' (单引号) |
设置了 ENT_QUOTES 后, ' (如果是 ENT_HTML401) ,或者 ' (如果是 **ENT_XML1**、 ENT_XHTML 或 ENT_HTML5)。 |
< (小于) |
< |
> (大于) |
> |
(4)存在SQL注入漏洞,数字型注入可绕过
1 |
|
(5)存在SQL注入,如果输入\‘ => \\‘,那么斜杠被转义,单引号未被转义
1 |
|
(6)不存在SQL注入
1 | intval(mixed $value, int $base = 10): int |
(7)存在SQL注入,未把过滤后的内容传给查询语句
1 |
|
(8)存在SQL注入,虽然有404,但是不影响下面的查询语句执行,应该把查询放到else里面
1 |
|
(9)不存在SQL注入,添加了exit,就不存在漏洞了
1 |
|
(10)存在SQL注入漏洞
1 |
|
(11)存在SQL注入,只判断了有正则,而没有严格限制
1 | preg_match:执行匹配正则表达式 |
(12)不存在SQL注入漏洞,标准的预编译
1 |
|
(13)存在SQL注入,urldecode
1 |
|
任意文件操作
文件操作函数
- 文件包含
- include / require / include_once / require_once / spl_autoload
- 文件读取
- file_get contents / fread / readfile / file / highlight_file / show_source
- 文件写入
- file_put_contents / fwrite / mkdir / fputs
- 文件删除
unlink / rmdir - 文件上传
move_uploaded_file / copy / rename
文件上传漏洞
- 文件上传流程
- 检查文件大小、后缀、类型
- 检查文件内容(如图片头等)
- 提取文件后缀
- 生成新文件名
- 将上传临时文件拷贝到新文件名位置
- 文件上传逻辑常见错误
- 只检查文件类型不检查文件后缀
- 文件后缀黑名单导致遗漏
- 使用原始文件名,导致\0截断等漏洞
[^1]:mysqli (MySQL Improved) 是一个用于访问 MySQL 数据库的 PHP 扩展。它是 PHP 默认的数据库访问接口,取代了较早的 mysql 扩展。与 mysql 相比,mysqli 提供了更多的功能和特性,包括:支持预处理语句和存储过程,提高了数据库访问的安全性和性能。支持事务处理和多语句查询。提供了面向对象的接口,使代码更加清晰和易维护。支持异步查询,可以并发执行多个查询。提供了更丰富的错误处理和调试功能。
[^2]:PDO (PHP Data Objects) 是 PHP 中的一个数据库抽象层,它提供了一种统一的接口来访问不同种类的数据库,包括 MySQL、PostgreSQL、Oracle 等。
[^3]:通过使用指定的进制 base 转换(默认是十进制),返回变量 value的int数值。 intval() 不能用于 object,否则会产生 E_WARNING 错误并返回 1
[^4]:使用反斜线引用字符串,返回需要在转义字符之前添加反斜线的字符串