PHP代码审计入门教程
0x00. 代码审计目的
代码审计指的是对源代码进行检查,寻找代码中的bug以及安全缺陷(漏洞)。这是一项需要多方面技能的技术,需要掌握以下知识:
- 编程能力
- 安全工具使用
- 漏洞原理
- 漏洞修复方式
- 函数缺陷
- 设计模式和编程思想
- MVC框架和常见框架
核心原则:"一切存在用户输入的地方都有可能存在漏洞"
0x01. 代码审计基础
必备基础知识
-
基础语法:
- HTML/JS基础语法
- PHP基础语法
- 面向对象思想
-
开发经验:
- PHP小项目开发(Blog、注册登录、表单、文件上传、留言板等)
-
安全知识:
- Web漏洞挖掘及利用
- Web安全工具基本使用(burpsuite、sqlmap等)
- 代码审计工具(seay审计系统、zend studio+xdebug等)
代码审计基本方式
-
通读全文源码:
- 最全面但最麻烦的方法
- 适合了解整个Web应用的业务逻辑
- 能挖掘到更多有价值的漏洞
-
功能点审计:
- 根据漏洞对应发生函数进行审计
- 常用逆向溯源数据流方法
代码审计基本方法
-
正向追踪数据流:
- 跟踪用户输入参数 → 代码逻辑 → 审计缺陷 → 构造payload
-
逆向溯源数据流:
- 搜索指定操作函数 → 跟踪可控参数 → 审计缺陷 → 构造payload
CMS分类
-
单入口CMS:
- 所有模块使用同一个入口文件
- 常见于MVC框架
-
多入口CMS:
- 每个模块有独立入口文件
- 如前端index.php,后端admin.php
0x02. 代码审计思路
-
三个层次:
- 确定审计源码的语言
- 确定是单入口还是多入口
- 确定该语言的各种漏洞函数
-
建议路径:
- 先做"程序员"再做审计
- 学习面向对象/面向过程编程
- 编写项目提升代码理解能力
- 掌握漏洞挖掘利用和危害理解
0x03. PHP核心安全配置
重要安全配置项
-
safe_mode:
- 限制文档存取、环境变量存取、外部程序执行
- PHP5.4.0已移除
-
safe_mode_allowed_env_vars:
- 指定PHP可改变的环境变量前缀
-
safe_mode_exec_dir:
- 限制exec()函数执行的可执行文件目录
-
disable_functions:
- 禁止敏感函数使用
- 注意要包含dl()函数
-
com.allow_dcom:
- 建议设置为false防止COM()函数创建系统组件
-
register_globals:
- 建议设置为off
- 防止自动注册全局变量
-
magic_quotes_gpc:
- 自动转义SQL查询中的特殊字符
- PHP5.4.0已移除
-
allow_url_include:
- 建议设置为off
- 防止远程文件包含
-
allow_url_open:
- 控制是否允许通过URL打开文件
-
expose_php:
- 建议设置为off
- 防止通过HTTP头泄漏PHP版本信息
-
upload_tmp_dir:
- 设置文件上传临时目录
-
open_basedir:
- 限制PHP脚本访问目录
-
display_errors:
- 生产环境建议设置为off
- 防止错误信息泄露
-
error_reporting:
- 设置错误报告级别
- 部署时可设为E_ALL & ~Enotice
0x04. 代码审计环境
推荐工具
-
PHP源码部署环境:
- Phpstudy 2018
-
集成开发环境:
- Zend Studio
- Phpstorm
-
数据库工具:
- Navicat for MySQL 12
- MySQLMonitor
-
文本编辑工具:
- Sublime_Text3
-
代码审计辅助工具:
- Seay源代码审计系统
- Search and Replace
- Rips 0.55
-
安全工具:
- 渗透版火狐
- BurpSuite
- Sqlmap
0x05. 手动调试代码
常用调试方法
-
输出调试:
echo exit(); print_r(); var_dump(); debug_zval_dump(); debug_print_backtrace(); -
弹窗调试:
echo "<script>alert($estr);</script>"; die("<script>alert($estr);</script>");
0x06. PHP的弱类型问题
1. 比较符号 == 与 ===
-
==:会进行类型转换var_dump(0=="admin"); //true var_dump(1=="1admin"); //true var_dump(1=="admin1"); //false var_dump(0=="admin1"); //true -
===:先判断类型是否相同var_dump("0e123456"=="0e4456789"); //true var_dump("1e1"=="10"); //true
2. array_search与is_array
if(!is_array($_GET['test'])){ exit(); }
$test = $_GET['test'];
for($i = 0;$i<count($test) ;$i++ ){
if($test[$i] === "admin"){
echo "error";
exit();
}
$test[$i] = intval($test[$i]);
}
if(array_search("admin",$test) === 0){
echo "flag";
}else{
echo "false";
}
绕过方法:传入test[]=0
3. in_array()函数
$array=[0,1,2,'3'];
var_dump(in_array('abc', $array)); //true
var_dump(in_array('1bc', $array)); //true
4. is_number()函数
$temp = $_GET['password'];
is_numeric($temp) ? die("no numeric") : NULL;
if($temp>9999){
echo '我giao';
}
绕过方法:10000+
5. strcmp()函数
$pd = "6666";
if(strcmp($_GET['pwd'],$pd) == 0){
echo "giao";
}else{
echo "?";
}
绕过方法:传入数组
6. switch()语句
$pwd = "1ad";
switch($pwd){
case 1: echo "giao"; break;
case 2: echo "?"; break;
}
问题:会将参数转换为int类型
7. md5()函数
var_dump(md5('240610708') == md5('QNKCDZO'));//true
$array1=[1,2,3];
$array2=[4,5,6];
var_dump(md5($array1)===md5($array2)) //true
8. sha1()函数
$array1=[1,2,3];
$array2=[4,5,6];
var_dump(sha1($array1)===sha1($array2)); //true
9. empty与isset
$a = null; $b = 0; $c = "";
var_dump(empty($a)); //true
var_dump(empty($b)); //true
var_dump(empty($c)); //true
var_dump(isset($a)); //false
var_dump(isset($b)); //true
var_dump(isset($c)); //true
0x07. 漏洞函数学习
1. 全局变量/超全局变量
- 全局变量:定义在函数外部,作用域到文件结尾
- 超全局变量:始终可用,包括:
- $GLOBALS
- $_SERVER
- $_REQUEST
- $_POST
- $_GET
- $_FILES
- $_ENV
- $_COOKIE
- $_SESSION
2. SQL注入相关
- SQL语句:
- select
- update
- insert into
- delete
3. 代码执行函数
- eval()
- usort()
- uasort()
- assert()
- array_map()
- preg_replace()
- array_filter()
- call_user_func()
- create_function()
- call_user_func_array()
- 动态函数:
$_GET['a']($_GET['b'])
4. 命令执行函数
- system()
- exec()
- passthru()
- shell_exec()
5. XSS相关函数
- print_r
- echo
- printf
- die
- var_dump
- var_export
6. 文件上传漏洞
- move_uploaded_file()
7. 文件包含漏洞
- include()
- include_once()
- require()
- require_once()
伪协议:
- file://
- http://
- ftp://
- php://
- zlib://
- data://
- glob://
- phar://
- ssh2://
- rar://
- ogg://
- expect://
8. 任意文件下载
- fopen()
- readfile()
- file_get_contents()
9. 任意文件删除
- unlink()
10. 任意文件读取
- file()
- fgets()
- fgetss()
- fopen()
- readfile()
- fpassthru()
- parse_ini_file()
- file_get_contents()
11. 变量覆盖
\[ - extract() - parse_str() - import_request_variables() (PHP4.1~PHP5.4) ### 12. 反序列化漏洞 - unserialize() **魔术方法**: - __construct() - __destruct() - __call() - __callStatic() - __get() - __set() - __isset() - __unset() - __sleep() - __wakeup() - __toString() - __invoke() - __set_state() - __clone() - __debuginfo() ## 0x08. 审计入门总结 ### 学习路径 1. 理解Web漏洞原理 2. 学习漏洞挖掘利用 3. 学习PHP开发 4. 掌握漏洞对应函数 5. 学习正则表达式 ### 审计路线 Demo → 综合漏洞靶场 → 网上审计过的CMS → 多入口CMS → 单入口CMS → 框架 → 函数缺陷 ### 推荐练习 - 从简单Demo开始 - 综合漏洞靶场练习 - 审计已知漏洞的CMS - 逐步挑战更复杂的系统\]