vsCTF2023
# PREFACE: 快结束了才知道,反正么挺快做了三个题,虽然讲实话很多东西真没看懂,但是逆向么出了就行…
# 后面的三个题解好少… 先看别的地方的题去了…
# ps. 这题怎么越做越多…
# x0rr3al
# 早知道 c 语言有一个 main 调用 main 的操作,似乎是为数不多 c++ 不支持的 c 操作?但是这里好像第一次见了?
存在反调,可以下掉反调看 check,或者直接看逻辑
sub_5560774764F7
: 一个简单异或 0x12
sub_55607747650A
: 递归异或一个初始化好的表
单字节加密 flag,全是异或,这里不需要提取,直接爆破即可:
a = '=>8566#>8=).(;9?6.(;9?' | |
# for i in a: | |
# print(chr(ord(i) ^ 0x5A),end='') | |
# print() | |
b = f"af&`f2`!d!`a#|u2fz#'2qz&~#2j\"``!s~333" | |
# for i in b: | |
# print(chr(ord(i) ^ 0x12),end='') | |
dest = 's3cR3ts3vsctfvsctiamfrnow0kkeyw0wkeyw' | |
lld = [ 0x7E, 0x7B, 0x6B, 0x7C, 0x6E, 0x73, 0x7F, 0x3B, 0x3C, 0x63, | |
0x57, 0x3C, 0x66, 0x7C, 0x39, 0x57, 0x6C, 0x3B, 0x6A, 0x7D, | |
0x6F, 0x6F, 0x3B, 0x7A, 0x7B, 0x57, 0x3C, 0x7A, 0x3B, 0x57, 0x66, 0x38, 0x57, 0x65, 0x3C, 0x7C, | |
0x6B, 0x60, 0x57, 0x6E, 0x38, 0x7A, 0x57, 0x7C, 0x60, 0x3B, | |
0x57, 0x3B, 0x39, 0x3B, 0x3B, 0x3F, 0x75] | |
print(len(lld)) | |
# for i in range(len(lld)): | |
# print(chr(lld[i] ^ 0x12 ^ ord(dest[11]) ^ ord(dest[0]) ^ ord(dest[22]) ^ ord(dest[33]) ^ ord(dest[44])),end='') | |
for i in range(0xff + 1): | |
for j in lld: | |
print(chr(i ^ j),end='') | |
print() | |
# vsctf{w34k_4nt1_d3bugg3rs_4r3_n0_m4tch_f0r_th3_31337} |
#
# challange
用比较复杂的 stl(主要是 vector
和 string
)进行数据操作,做了个类似 RSA 的 乘方+取模
然后加点异或生成了一个 vector 用于加密,但是加密逻辑只有异或,flag 是 50 位的
这里不需要硬逆逻辑,下掉 ptrace
反调(这里原本有 exit (0); 已经 nop 掉了)
在 check 的地方把检查逻辑改掉(改成如果不相等则退出)
提取异或的 key 数据
print(hex(get_reg_value("ebx")) , end = ', ') |
直接梭就行(这里输入的是全 1 的 flag,flag 也刚好没有 1,可以多用几个试几次):
enc = [149, 148, 5, 88, 128, 22, 47, 70, 184, 117, 311, 57, 145, 224, 32, 112, 77, 185, 25, 59, 79, 4, 31, 184, 156, 79, 241, 179, 162, 68, 119, 244, 92, 109, 29, 47, 123, 154, 33, 224, 223, 125, 159, 194, 116, 63, 4, 246, 199, 250, 0] | |
# for i in range(len(enc)): | |
# print(chr(enc[i] ^ i),end='') | |
a = '11111111111111111111111111111111111111111111111111' | |
b = [0xd2, 0xd6, 0x57, 0x1d, 0xd7, 0x5c, 0x78, 0x22, 0xe7, 0x7, 0x131, 0x39, 0x90, 0x9f, 0x25, 0xd, 0x23, 0xb8, 0x58, 0x55, 0x9, 0x6a, 0x6d, 0xe6, 0xc0, 0xe, 0xa5, 0xf6, 0xfa, 0x1, 0x2f, 0xb3, 0x8, 0x3, 0x7c, 0x6c, 0x25, 0xcc, 0x4f, 0x85, 0xab, 0x1, 0xfe, 0xbf, 0x4, 0x5a, 0x70, 0x94, 0xc9, 0xb6] | |
# print(0xd2 ^ ord('1')) | |
# print(227 ^ 149) | |
# print(chr(227 ^ 149)) | |
for i in range(len(b)): | |
print(chr(enc[i] ^ b[i] ^ ord('1')),end='') | |
# vsctf{fUnC710N4L_0p_w_Competitive_Prog_TEMPLATES?} |
# teenage-wasm
# 说实话第一次做 wasm…
插件: Release Version 2.1.0 · nneonneo/ghidra-wasm-plugin (github.com)
选择的是 Ghidra + wasm
插件的反编译方案,还是比较清晰的
可以拿到一个 js 代码,和一个 wasm 代码,这里找不到 button 的处理 handle,纠结了很久,然后友 web 手说可能是 wasm 里面注册了监听,orz 真该学学 web 了…
js 代码中大概是 wasm-bindgen
编译的与 wasm 交互的产物,只负责中间件,将字符串共享给 wasm,并接收共享字符串
可以直接来看 wasm,代码很多,rust 编译的也相对抽象,经过前面说的可能是监听的 button 可以翻翻代码,然后发现了这个
flush_messages
加密逻辑也很显眼…
虽然其实还并不是狠看懂这个怎么传回去的,但是提取数据异或一下真的就是 flag 了…(他真的很喜欢异或)
a_list = ["7a", | |
"5158577471345867", | |
"4a77746f79675a70", | |
"6a4d5a776d716272", | |
"46625f373333316d", | |
"73617765766f6c69", | |
] | |
b_list = ["07", | |
"1f16203f4345352d", | |
"123a201a2e170515", | |
"072229441a103d06", | |
"760c00445a6c5c1e", | |
"47160c03020c1f1f", | |
] | |
flag = '' | |
for i in range(6): | |
a = [int(a_list[i][j: j + 2], 16) for j in range(0, len(a_list[i]), 2)] | |
b = [int(b_list[i][j: j + 2], 16) for j in range(0, len(b_list[i]), 2)] | |
for j in range(len(a)): | |
flag += chr(a[j] ^ b[j]) | |
print(flag[::-1]) | |
# vsctf{w4sm_is_n0t_aw3some_pWuTMXJmq2KwNN} |
# 9-26: 感觉本来 wasm 就见得不多,会做的更是少,还是不要草草结束了这个题,把他给逆完整一点
直觉上 flesh_message
既然找不到 x-ref
,应该还是有更多处理逻辑的,首先是这里的比对
local_18 拿到了 param2(即用户的一个输入),与 admin
字符串进行了比对,如果不是 admin 会返回 Login fail 的提示
# (可以右键更换数据类型)
这个地方感觉有机会,看看下面,找到了类似的结构:
但是这里没那么明显,先试试我怎么出的:
这里除去 admin
已经没有明显的全局变量了,能包含有字符信息的内容不多,然后突然意识到这里全都是可见字符:
那其实很蹊跷了,打印一下看看
感觉就很明显了,有明显的英文语法痕迹(?)
逆转过来:
这个就是密码…
回来看看逻辑,到底放在哪里
前面没注意,但是这里有个很明显的把参数提取成 utf-8 的操作:
然后存储 local_18
-> local_68
在下面进行比较
还是对这些符号不够敏感…