找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

676

积分

0

好友

86

主题
发表于 4 天前 | 查看: 14| 回复: 0

免责声明:本文内容仅用于信息安全防御技术的交流与学习,请严格遵守国家法律法规,切勿用于任何非法用途。

前言

近期应几位师傅之邀,对一套支付类系统源码进行了代码安全审计。最初目标是寻找可实现远程代码执行的前台漏洞,但未能成功,最终发现了一处后台SQL注入点。本文将分享此次审计的完整思路与过程,特别是对系统鉴权机制的深度分析与一次失败的绕过尝试。

系统架构与初步审计

拿到源码后,首先查看了其整体文件结构。该系统为非框架开发,结构清晰,便于快速定位核心文件。

支付系统源码文件目录结构

审计的第一步通常是分析其认证机制。同时,我也对前台功能进行了常规的敏感函数排查,例如寻找 move_uploaded_filefile_put_contents 等可能导致文件上传或写入漏洞的函数,但均未发现可利用点。

在PHP文件中搜索move_uploaded_file函数

随后转向寻找前台的SQL注入点。然而,检查多个关键接口后发现,代码中对用户输入普遍使用了预编译或 addslashes 转义等安全措施,防护较为到位。

使用了addslashes转义的订单查询代码

例如上图所示的订单查询逻辑,对 trade_no 参数进行了转义,同时结合intval处理整型参数,有效防御了字符型和数字型注入。尝试绕过这些过滤函数未果后,我将审计重点转向了系统的鉴权逻辑,以期找到突破口。

后台鉴权机制深度剖析

系统后台的入口文件会包含一个公共文件,并检查 $islogin 变量来判断用户登录状态。

检查$islogin变量的后台入口代码

追踪 $islogin 变量的赋值逻辑,发现其位于 common.php 或类似文件中。核心鉴权逻辑如下:系统从 Cookie 中读取名为 admin_token 的值,使用 authcode 函数(一个自定义的加解密函数)进行解密,然后将解密后的字符串按制表符 \t 分割为三部分:$user$sid$expiretime

后台鉴权逻辑:解析admin_token

接着,系统从配置中读取管理员用户名 ($conf['admin_user'])、密码 ($conf['admin_pwd']) 和一个固定的密码盐值 ($password_hash),将三者拼接后进行 MD5 哈希,得到 $session。最后,校验这个 $session 是否等于解密得到的 $sid,并且 $expiretime 是否未过期。若两者均满足,则 $islogin 被赋值为 1,代表登录成功。

后台鉴权逻辑:计算并比对session

那么,这个 admin_token 是在哪里生成的呢?分析登录接口代码,在用户输入正确的账号密码后,系统会以同样的规则生成 $session,再与用户名、过期时间一起用 \t 连接,最后调用 authcode 函数加密并设置到 Cookie 中。

登录成功后生成并设置admin_token的代码

一次失败的鉴权绕过尝试

梳理清楚流程后,我萌生了一个绕过想法。鉴权的核心是比较 $session$sid 是否相等。在 PHP 中,== 是松散比较,存在著名的“魔法哈希”问题:如果两个字符串都以 0e 开头,后面全是数字,PHP 会将它们视为科学计数法的零,从而判断为相等。

为了测试,我首先编写了一个小脚本来模拟生成合法的 Token。

模拟生成合法admin_token的PHP代码

加密输出结果类似:7dc74lFTKXYo0ULE69Uax+mhT3RRcC/AfaE86rtGZIiFdAn73K2HDtiY2LWC8yFAiWr1YrZHY2rbSK18rW9bVVgBSk5L1ZF9Ip0dlwxj

再编写解密脚本进行验证:

解密admin_token的PHP代码

解密后得到格式为:admin\t5e2f212328fea275e18ce8714e71b18c\t1771332095。其中第二部分 5e2f21... 就是 $sid

鉴权代码中使用的正是 == 进行比较:

使用==进行弱比较的鉴权代码

我立即验证了 PHP 的弱比较特性:

验证MD5弱比较的PHP代码

运行结果证实了判断:

MD5弱比较验证成功输出“两个一样”

于是,一个绕过思路浮现:能否构造一个 admin_token,使其解密后的 $sid 部分是一个以 0e 开头的 MD5 字符串?这样,无论实际的 $session 值是什么,只要它也是一个以 0e 开头的数字字符串,比较结果就可能为真。

这意味着我需要碰撞出“用户名+密码+盐值”这个组合的 MD5 哈希值恰好是 0e 开头。我修改了本地测试环境的密码,尝试碰撞出这样的哈希。

尝试生成0e开头的MD5哈希值

虽然成功得到了 0e 开头的 $session,并可以生成对应的 Token,但在实际测试中,绕过并未成功。回顾代码发现,问题在于鉴权时用于比对的 $session 是从当前配置 ($conf['admin_user'], $conf['admin_pwd']) 计算得出的。攻击者无法控制这个值,除非目标系统管理员的密码本身就满足这个极其特殊的条件,或者我们能篡改配置文件。

重新审视鉴权比对逻辑

此外,常见的“魔法哈希”字符串是有限的,例如:

QNKCDZO -> 0e830400451993494058024219903391
240610708 -> 0e462097431906509019562988736854
s878926199a -> 0e545993274517709034328855841020

这进一步降低了利用成功的概率。这次尝试虽然失败了,但加深了对鉴权逻辑的理解,也是一次宝贵的代码审计经验。

发现并验证后台SQL注入漏洞

在鉴权绕过路径受阻后,我转而审计后台已授权功能中的漏洞。很快,在多个后台模块中发现了未经过滤的 SQL 语句拼接,存在明显的注入点。这与前台严格的过滤形成了鲜明对比。

存在SQL注入的批量转账页面代码

例如,在上图的 transfer_batch.php 文件中,batch 参数直接被拼接进 SQL 查询语句。然而,在尝试利用时遇到了障碍:页面首先检查会话中存储的支付密码 ($_SESSION['paypwd']),不正确则直接拦截。

支付密码错误导致无法访问注入点

既然直接利用有障碍,我便寻找其他无需二次验证的注入点。在另一个后台文件 ajax.php 中,发现了 settle 操作分支存在同样的问题。

ajax.php中另一个SQL注入点代码

参数 batch 在第16行直接拼接到 SQL 语句中($rs=$DB->query(“SELECT * from pre_settle_batch WHERE batch=’$batch’ and (type=1 or type=’$type’)”);),且该接口无额外密码校验。使用 sqlmap 工具可以轻松验证漏洞:

sqlmap -u “http://target.com/admin/ajax.php?act=settle&batch=xxx&type=common“ --batch --level 3

使用sqlmap成功检测到时间盲注漏洞

如上图所示,sqlmap 成功识别出 batch 参数存在基于时间的盲注漏洞,后端数据库为 MySQL。至此,一个后台 SQL 注入漏洞被确认。这个案例表明,对于PHP开发的系统,即使在核心鉴权机制较为健全的情况下,业务逻辑层面的参数过滤缺失,依然可能导致严重的安全风险。

希望这次对某支付系统源码的审计过程分享,能为大家在云栈社区的交流学习中提供一些参考。




上一篇:Web Worker 实战:解决Excel导出与图片压缩的页面卡顿问题
下一篇:try:把本地那些 “试试看” 的目录收拢起来
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-24 01:42 , Processed in 1.439814 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表