2024ImaginaryCTF
CTFtime.org / ImaginaryCTF 2024
Unoriginal#
签到题,略
BF#
Simple equations... but in BF?!!!
BF to C Transpiler (kvbc.github.io)
有规律的,简单的加加减减,转换成 C 之后,正则匹配:
import re
lines = open("bf.c", encoding="utf-8").read();
mul_a = re.findall(r'\n\t\(\*p\)\+=(\d+);', lines);
mul_b = re.findall(r'\n\t\t\(\*p\)\+=(\d+);', lines);
checkers = re.findall(r'\n\t\(\*p\)\-=(\d+);', lines);
result = "".join([chr(checker - a * b) for a, b, checker in zip(map(int, mul_a), map(int, mul_b), map(int, checkers))])
Rust#
output.txt [missing note] rust
Rust! Enjoy 😃 Note: The message that produces the provided encryption is the flag.
插件 IDA Rust Demangler 优化一下符号

这种宏定义可以在 IDA Pro\plugins\hexrays_sdk\include\defs.h
找到
#define __PAIR128__(high, low) (((uint128)(high) << 64) | (uint64)(low))
简单的异或加密,解密脚本如下(其中 key 是通过 flag 开头 ictf 异或出来的)
#include<stdio.h>
#include<stdint.h>
__int128_t encs[] = {-42148619422891531582255418903, -42148619422891531582255418927, -42148619422891531582255418851, -42148619422891531582255418907, -42148619422891531582255418831, -42148619422891531582255418859, -42148619422891531582255418855, -42148619422891531582255419111, -42148619422891531582255419103, -42148619422891531582255418687, -42148619422891531582255418859, -42148619422891531582255419119, -42148619422891531582255418843, -42148619422891531582255418687, -42148619422891531582255419103, -42148619422891531582255418907, -42148619422891531582255419107, -42148619422891531582255418915, -42148619422891531582255419119, -42148619422891531582255418935, -42148619422891531582255418823};
int main()
{
for (int i = 0; i < 21; i++) {
__int128_t enc = encs[i];
int64_t key_part1 = 5131632107173988729;
__int128_t dec = (~enc) - 0x539;
dec = dec & 0xffffffff;
dec = dec ^ key_part1;
dec = dec >> 2;
printf("%c", (char)dec);
}
}
Oh, a Camel! #
While exploring twisted corridors of an ancient pyramid, you find a weird scroll with a camel drawn on it. You pick it up, but don't have too much time to inspect it…
在探索古代金字塔扭曲的走廊时,您会发现一个奇怪的卷轴,上面画着一只骆驼。你捡起它,但没有太多时间检查它......
使用的是 Ocaml 语言
Stackoverflow → 找到 ocamldumpobj → 在包 ocaml-nox —— 没啥用

跟踪字符串,找到这

这里的 rax 是一个 56 位的 bytes
Printf#
%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%64949c%*c%c%c%hn%c%c%c%c%c%c%c%545c%hn%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%hhn%*c%*74$c%*74$c%*74$c%*74$c%*74$c%*74$c%*74$c%*74$c%*74$c%*74$c%*74$c%*74$c%*74$c%*74$c%8c%75$hhn

在 printf 里面打印了一个巨大的字符串:data.txt ,直接运行,就会出现一大片空白
用 bindiff 看看


❯ ldd printf
linux-vdso.so.1 (0x00007fff0cb03000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdf70e41000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdf71095000)
发现并没有链接到
sudo apt install patchelf
patchelf --set-rpath ./ printf
❯ ldd printf
linux-vdso.so.1 (0x00007ffe7b5e9000)
libc.so.6 => ./libc.so.6 (0x00007fe7b8cb7000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe7b8f00000)
❯ ./printf
-------------------------------------
WELCOME TO THE PRINTF VAULT
-------------------------------------
Enter the flag:
ok

断点进入 printf ,还真是修改了 printf (可能因为改的太少了 bindiff 没识别出来)

找找函数调用
View → Open subviews → Function calls
没找出来
还有办法,“暂停”大法,栈中找调用,断在了这里:


通过给这个下硬件断点,发现这个东西只会用作下标
大概看出来 printf
程序是个 vm,对 flag 加加减减的操作都在里面执行,所有系数都能在 data.txt 里找到
%2$
指的是printf中第二个参数


经测试,4 个字符(节)为一个加密单位,如果不对,就不会继续读取了
直接 ida 写 hook 脚本,断点 + 打印寄存器,直接看日志得出算法(chatgpt真好使,不用翻idapython史书文档真舒服)
就是加加减减,6个参数决定一个4字符块
import idaapi
import idc
import ida_dbg
# 设置断点地址(请根据你的具体情况修改)
# breakpoint_address = 0x401000
output = open("D:/Documents/SZPOJP/Reverse/Shuati/2024-07-20/2024ImaginaryCTF/printf/idadeb_hook.txt", "w")
okprint = lambda x: print(x, file=output)
# 记得最后关闭文件 output.close()
# 定义断点事件处理器
class MyDbgHook(idaapi.DBG_Hooks):
def __init__(self):
idaapi.DBG_Hooks.__init__(self)
def dbg_bpt(self, tid, ea):
# print("Breakpoint hit at address: 0x{:X}".format(ea))
# 处理断点事件,如打印当前函数名
func = idaapi.get_func(ea)
if func:
func_name = idaapi.get_func_name(func.start_ea)
match func_name:
case 'ok_get_save_char':
okprint(f"get_char: {chr(idc.get_reg_value('RAX'))}")
print(f"get_char: {chr(idc.get_reg_value('RAX'))}")
case 'ok_push_num':
okprint(f"push_num: {idc.get_reg_value('RAX')}")
case 'ok_push_reg':
okprint(f"push_reg: {idc.get_reg_value('RAX')}")
case 'ok_pop_reg':
okprint(f"pop_reg: {idc.get_reg_value('RAX')}")
case 'ok_add':
okprint(f"{idc.get_reg_value('RAX')} + {idc.get_reg_value('RCX')} = {(idc.get_reg_value('RAX') + idc.get_reg_value('RCX')) & 0xFFFFFFFFFFFFFFFF}")
case 'ok_sub':
okprint(f"{idc.get_reg_value('RAX')} - {idc.get_reg_value('RDX')} = {(idc.get_reg_value('RAX') - idc.get_reg_value('RDX')) & 0xFFFFFFFFFFFFFFFF}")
case 'ok_mul':
okprint(f"{idc.get_reg_value('RAX')} * {idc.get_reg_value('RDX')} = {(idc.get_reg_value('RAX') * idc.get_reg_value('RDX')) & 0xFFFFFFFFFFFFFFFF}")
case 'ok_div':
okprint(f"{idc.get_reg_value('RAX')} / {idc.get_reg_value('RSI')} = {(idc.get_reg_value('RAX') // idc.get_reg_value('RSI')) & 0xFFFFFFFFFFFFFFFF}")
case 'ok_mod':
okprint(f"{idc.get_reg_value('RAX')} % {idc.get_reg_value('RCX')} = {(idc.get_reg_value('RAX') % idc.get_reg_value('RCX')) & 0xFFFFFFFFFFFFFFFF}")
case 'ok_xor':
okprint(f"{idc.get_reg_value('RAX')} ^ {idc.get_reg_value('RCX')} = {(idc.get_reg_value('RAX') ^ idc.get_reg_value('RCX')) & 0xFFFFFFFFFFFFFFFF}")
case _:
pass
ida_dbg.continue_process()
# print("Breakpoint in function: {}".format(func_name))
return 0
# 设置断点
# idc.add_bpt(breakpoint_address)
# 注册断点事件处理器
dbg_hook = MyDbgHook()
dbg_hook.hook()
# 提示用户调试器已准备好
print("Debugger hook set. You can now run the debugger.")
z3 尝试求解前四位
from z3 import *
flag = [BitVec('flag_%d' % i, 32) for i in range(4)]
s = Solver()
[
s.add(flag[i] > 0x20, flag[i] < 0x7e) for i in range(4)
]
calcs = [
flag[0] * 14512401101554986554,
flag[1] * 12309110382010363445,
flag[2] * 9149030501113554576,
flag[3] * 7109559677222467375
]
s.add(sum([calc & 0xFFFFFFFFFFFFFFFF for calc in calcs]) ^ 6110534783014685135 == 15450279358332388236)
if s.check() == sat:
model = s.model()
print(''.join([chr(model[flag[i]].as_long()) for i in range(4)]))
else:
print('unsat')
成功。改进idapython脚本,找到对应的数
import idaapi
import idc
import ida_dbg
##########################
tmp_char = 0
results = []
##########################
okprint = lambda x: 0
# 定义断点事件处理器
class MyDbgHook(idaapi.DBG_Hooks):
def __init__(self):
idaapi.DBG_Hooks.__init__(self)
def dbg_bpt(self, tid, ea):
global results, tmp_char
# print("Breakpoint hit at address: 0x{:X}".format(ea))
# 处理断点事件,如打印当前函数名
func = idaapi.get_func(ea)
if func:
func_name = idaapi.get_func_name(func.start_ea)
match func_name:
case 'ok_get_save_char':
okprint(f"get_char: {chr(idc.get_reg_value('RAX'))}")
print(f"get_char: {chr(idc.get_reg_value('RAX'))}")
tmp_char = idc.get_reg_value('RAX') # 存储当前字符,后续用于获取大数
case 'ok_push_num':
okprint(f"push_num: {idc.get_reg_value('RAX')}")
case 'ok_push_reg':
okprint(f"push_reg: {idc.get_reg_value('RAX')}")
case 'ok_pop_reg':
okprint(f"pop_reg: {idc.get_reg_value('RAX')}")
case 'ok_add':
okprint(f"{idc.get_reg_value('RAX')} + {idc.get_reg_value('RCX')} = {(idc.get_reg_value('RAX') + idc.get_reg_value('RCX')) & 0xFFFFFFFFFFFFFFFF}")
case 'ok_sub':
okprint(f"{idc.get_reg_value('RAX')} - {idc.get_reg_value('RDX')} = {(idc.get_reg_value('RAX') - idc.get_reg_value('RDX')) & 0xFFFFFFFFFFFFFFFF}")
case 'ok_mul':
okprint(f"{idc.get_reg_value('RAX')} * {idc.get_reg_value('RDX')} = {(idc.get_reg_value('RAX') * idc.get_reg_value('RDX')) & 0xFFFFFFFFFFFFFFFF}")
if tmp_char == idc.get_reg_value('RAX'):
results.append(idc.get_reg_value('RDX'))
case 'ok_div':
okprint(f"{idc.get_reg_value('RAX')} / {idc.get_reg_value('RSI')} = {(idc.get_reg_value('RAX') // idc.get_reg_value('RSI')) & 0xFFFFFFFFFFFFFFFF}")
case 'ok_mod':
okprint(f"{idc.get_reg_value('RAX')} % {idc.get_reg_value('RCX')} = {(idc.get_reg_value('RAX') % idc.get_reg_value('RCX')) & 0xFFFFFFFFFFFFFFFF}")
case 'ok_xor':
okprint(f"{idc.get_reg_value('RAX')} ^ {idc.get_reg_value('RCX')} = {(idc.get_reg_value('RAX') ^ idc.get_reg_value('RCX')) & 0xFFFFFFFFFFFFFFFF}")
results.append(idc.get_reg_value('RCX'))
if (len(results) == 6):
results.append(results)
results = []
case _:
pass
ida_dbg.continue_process()
# print("Breakpoint in function: {}".format(func_name))
return 0
# 设置断点
# idc.add_bpt(breakpoint_address)
# 注册断点事件处理器
dbg_hook = MyDbgHook()
dbg_hook.hook()
# 提示用户调试器已准备好
print("Debugger hook set. You can now run the debugger.")
每成功dump出6个数,就扔进z3解出来对应的4个字符,加上之后再加任意四个字符,再debug,用 result[-6:]
拿到新的6个数,以此类推,得出脚本
from z3 import *
dumps = [0xc96667e9702c2e3a, 0xaad2bead0cccee35, 0x7ef7e2c462166690, 0x62aa3adbbe49372f, 0x54ccfb02994249cf, 0xd66a6b37a1f3c38c]
dumps = [0x342e70ab89920dd9, 0x8694cfce23dc0df1, 0x5e14dede8db175aa, 0x488ede6e062613be, 0x9d29cb475e41cb8f, 0x8b6d914ccfe3e4cc]
dumps = [0x3b37278be935996a, 0x5ae8a03882f8a044, 0x656c5d76772fa64c, 0x469fd7c2f2d02294, 0xdd11c039fe5f588f, 0x36296d6ff73c1725]
dumps = [0xe6e4f9dd08b7d237, 0x683b5ae9a60b299e, 0xce7e52def829b2f, 0x60e53b578e7a8fb0, 0x9de3f8582e267573, 0xc74eea217c1780cf]
dumps = [0x81fad7adeffcbe62, 0x50227d45d028e2a7, 0x865d727a1c643594, 0x67e9e9f9ccbb1752, 0x54ccfb02994249cf, 0xb37269bc0cf143c3]
dumps = [0x8fb581c1d37fb102, 0xa3b67ebd7581d178, 0x2e5b50ca6a2ca301, 0xd9dd813ddff69e50, 0x54ccfb02994249cf, 0xf2a053d9454e1199]
dumps = [0xe3cf5a5a34477b7f, 0x17a99ead1dda85d5, 0x11c933f01965c94a, 0xdbe796533b2c5073, 0x9715552a8c24bd9f, 0xf6b85b6bab132df0]
dumps = [0x48d8b5385ca9de5e, 0x74c57cbf4dc60c5c, 0x473ffdd19d8d3e68, 0x7d1b9e50cab6cf, 0x54ccfb02994249cf, 0xb7fa65c680cb955c]
dumps = [0x4ad903a7737bf6d, 0xfe24ae2919fb080a, 0x91fee69eb0426269, 0xa311c78ac6eed0e3, 0x9715552a8c24bd9f, 0x5af0e6ba579b8c16]
dumps = [0x172a2cfcc154e672, 0xba5e6196c6d0dbaa, 0x76c5558dd76520b6, 0x7edae72078618adc, 0x9de3f8582e267573, 0x8e39f4391d389a5d]
dumps = [0x75982e11c6e9f787, 0xded3a49eaab0bbca, 0xfe3652659fa79527, 0x6b8d71916d0dc21, 0x54ccfb02994249cf, 0x4c4a722b516d0dc2]
dumps = [0xf6816364e882e5d0, 0xd3de330e05e340bf, 0xf3009bcf092a1bf3, 0xb98fd6eaeaa31de4, 0x9715552a8c24bd9f, 0xbd1b348cb14b1a0c]
dumps = [0x3cbb28b00d982646, 0xc01e2683ea8c4f04, 0xb0728f6694068514, 0x4a3cf42d4f74f903, 0xdd11c039fe5f588f, 0xca37dfc59b0d445c]
dumps = [0xcb8bcc57d18df0a7, 0x9bc154f177dba69b, 0x1ce10b01008fe0b8, 0x393ca8f9f07754f1, 0x9d29cb475e41cb8f, 0x3ea20b9736418168]
dumps = [0xa0e1d7496e8f3692, 0x792055bbe67c455e, 0x23c9f1c9f9352918, 0xee71cc15ec4af890, 0xdd11c039fe5f588f, 0x13654f2ad9a2debf]
dumps = [0xc16f11ba163fe9a6, 0xf7cceee60cad4500, 0xe296a079970a4d01, 0xe06519e67ff54956, 0xdd11c039fe5f588f, 0x38595695629e5e96]
dumps = [0x5cc42aea4fad516b, 0x654b96d840b731b3, 0x238c34b915584ceb, 0xd9401f52df46b0fb, 0x9d29cb475e41cb8f, 0x1c1c82f150f07462]
dumps = [0xf9923bc7ae594205, 0x97b9f4f4c11b13ee, 0x2452efbf17fce49, 0x7b75687e9288270a, 0x54ccfb02994249cf, 0xcfae6899f240f30a]
flag = [BitVec('flag_%d' % i, 32) for i in range(4)]
s = Solver()
[s.add(flag[i] > 0x20, flag[i] < 0x7e) for i in range(4)
]
calcs = [
flag[0] * dumps[0],
flag[1] * dumps[1],
flag[2] * dumps[2],
flag[3] * dumps[3]
]
s.add(sum([calc & 0xFFFFFFFFFFFFFFFF for calc in calcs]) ^ dumps[4] == dumps[5])
if s.check() == sat:
model = s.model()
print(''.join([chr(model[flag[i]].as_long()) for i in range(4)]))
else:
print('unsat')
Watch dog#
The keepers of the Watchdog vault have forgotten their password. Can you help them retrieve it?
长度 43

flagToNum:
flag = list(b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
加密算法:
flag = list(b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
def my_pow(i: int, j: int) -> int:
if j == 0:
return 1
if j == 1:
return i
if j & 1:
return i * my_pow(i * i, (j - 1) >> 1)
return my_pow(i * i, j >> 1)
result = []
# evalMultiPoly
for i in range(2, len(flag) + 3):
tmp = flag.copy()
# evalVectPoly
tmp_result = 0
for k in range(0, len(tmp), 1):
tmp_result += tmp[k] * my_pow(i, len(tmp) - 1 - k)
result.append(tmp_result & 0xFFFFFFFFFFFFFFFF)
print([hex(c) for c in result])
看算法像是要解 43 元一次方程组,但是 z3 解不出来,使用 sage 试试
Sage Cell Server (sagemath.org)
Absolute Flag Checker#
What's easier way than verifying flag contents more times than required?

检查方式比较简单,只要能一条路走到底,就成功了
又是解方程,我嘞个去
每一个小方块是一个方程,47 个未知数 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK
我嘞个去,长度不一定是 47,超过 字符串长度的默认为 0
试出来的经验:如果能解方程解出来,就不要加限制,如 [And(0x20 < f, f < 0x7E) for f in flag]
,会极大降低 z3/sage 效率
Minecraft#
pip install pynbt
pip install anvil-parser
用 Minecraft 1.21 打开,一行命令完成
/execute as @e[type=wolf,nbt={CollarColor:11b,variant:"minecraft:black"}] at @s run say I found a blue-collared dog!
NBT Tags for Wolf in Minecraft (Java Edition 1.16/1.17/1.18/1.19/1.20) (digminecraft.com)
VM#
Can you find what this (very inefficient) VM is doing?

In [11]: [i for i in range(len(program)) if program[i][2] == True]
Out[11]: [3441, 3445]