ASIS_CTF2023

# PREFACE:赛中做了算三个吧,sid 有点小变态,爆出来了,但是没找到 flag,经别的师傅点醒才知道没有格式,反正么只是来看看题而已,作业好多还要上学…
# 9-25: 小补一个题,解数毕竟比 sid 多,虽然说题越做越多,能做的题确实越来越少了,高手题几乎完全没法碰…

# grid

warmup 题,加密逻辑很简单,就是对着 box 反映射一下

box = [1, 118,   2,  44,   3,  59,   4,  69,   5, 189,
    6, 242,   7, 146,   8,  93,   9, 145,  10,  45,
   11,  46,  12, 127,  13, 233,  14,  60,  15, 120,
   16, 159,  17, 138,  18, 165,  19, 113,  20, 148,
   21,  63,  22, 167,  23, 219,  24, 232,  25, 246,
   26, 164,  27, 153,  28, 230,  29,  34,  30,  64,
   31, 255,  32, 220,  33, 238,  34, 234,  35,  36,
   36, 109,  37,  88,  38,   2,  39, 162,  40, 221,
   41, 134,  42, 158,  43, 161,  44, 225,  45,  25,
   46, 224,  47, 100,  48, 106,  49, 179,  50,  94,
   51,  43,  52, 187,  53,  56,  54,  79,  55,  48,
   56,  83,  57,  68,  58, 117,  59, 101,  60, 249,
   61,  75,  62, 193,  63,  35,  64, 178,  65, 190,
   66, 213,  67,   5,  68, 128,  69,  97,  70, 254,
   71,  96,  72, 227,  73,  50,  74, 133,  75, 244,
   76, 207,  77,  90,  78,  58,  79,  67,  80, 214,
   81, 111,  82,  84,  83, 185,  84, 204,  85, 245,
   86, 181,  87,  89,  88,  22,  89, 231,  90, 169,
   91, 174,  92, 241,  93,   8,  94,  62,  95, 125,
   96, 228,  97, 222,  98, 198,  99,  28, 100, 235,
  101, 182, 102, 151, 103,  21, 104,  81, 105, 124,
  106, 173, 107, 205, 108,  39, 109,  10, 110,  53,
  111,  95, 112, 212, 113,  19, 114, 160, 115,  20,
  116, 103, 117, 122, 118, 140, 119,  74, 120, 114,
  121,  47, 122, 218, 123, 239, 124,  73, 125,  14,
  126, 211, 127, 250, 128,  61, 129, 192, 130,  51,
  131, 102, 132, 170, 133, 206, 134, 252, 135, 176,
  136,  98, 137, 147, 138,  65, 139, 215, 140,  72,
  141, 191, 142, 202, 143, 200, 144,   3, 145, 126,
  146,  15, 147, 155, 148, 203, 149,  86, 150, 216,
  151, 121, 152,  82, 153, 163, 154, 171, 155, 104,
  156,  66, 157, 237, 158, 129, 159, 132, 160, 156,
  161,  16, 162,  91, 163,  11, 164,  26, 165, 112,
  166, 248, 167,  99, 168, 166, 169, 175, 170,  32,
  171, 152, 172,   1, 173,  52, 174,  41, 175, 226,
  176, 251, 177,  23, 178, 154, 179, 172, 180,   4,
  181,  92, 182,  54, 183, 186, 184, 217, 185, 177,
  186, 247, 187, 195, 188,  80, 189, 209, 190, 188,
  191, 229, 192, 144, 193,  37, 194,  12, 195, 136,
  196, 184, 197,  18, 198, 210, 199,  27, 200,  71,
  201,  77, 202, 196, 203,   7, 204,  30, 205, 135,
  206, 123, 207,  87, 208, 240, 209, 253, 210,  85,
  211,  29, 212, 105, 213, 223, 214, 108, 215,  42,
  216, 236, 217, 110, 218, 199, 219,  40, 220, 142,
  221, 194, 222, 130, 223, 157, 224,  78, 225,  24,
  226, 115, 227, 149, 228, 201, 229,  13, 230, 137,
  231, 141, 232, 107, 233, 139, 234,  70, 235,  17,
  236,  33, 237, 116, 238,  76, 239,   9, 240, 243,
  241, 197, 242, 208, 243, 131, 244, 183, 245,  31,
  246,  55, 247, 180, 248,  38, 249,   6, 250,  49,
  251, 150, 252,  57, 253, 168, 254, 119, 255, 143]
# enc = '6A35BB7DA9EE7D387CE330EFB932B9BE0D4F50A54DE597DE738DE617D542BE3670EEEEE61443AB627CFBEE31C7DD4F5'
enclist = ['6A35BB7DA9EE7D387CE330EFB932B9BE','D4F50A54DE597DE738DE617D542BE367','0EEEEE61443AB627CFBEE31C7D'] #这里需要删掉一点点
flag = ''
for enc in enclist:
    a = [int(enc[i:i + 2], 16) for i in range(0, len(enc), 2)]
    def create_mapping(lst):
        odd_numbers = []
        even_numbers = []
        mapping = {}
        for num in range(256):
            if num % 2 == 0:
                even_numbers.append(lst[num])
            else:
                odd_numbers.append(lst[num])
        for odd, even in zip(odd_numbers, even_numbers):
            mapping[odd] = even
        return mapping
    map = create_mapping(box)
    # print(map)
    tmpflag = ''
    for i in a:
       tmpflag += chr(map[i])
    tmpflag = tmpflag[::-1]
    flag += tmpflag
    print(tmpflag)
print()
print(flag)
# ASIS{7Hi5_!Z_4n0tH3R_Ea5Y_WaRmUp_cHALleN9E!!}

# buzz

对于 flag 的每一位加密,打印的 BUZZ [] 数量可以转化为数字,即加密后的数

ios 的没设备没环境不能调,否则直接打印一张大映射表就行,只能硬逆,好在逻辑比较简单

但其实不好看,这个逻辑不知道怎么做的编译器优化,很抽象:

image-20230924085147175

不过可以辨认出 xor shl *55 + 97 +44 等操作,其中 *55 这个会生成一个比较大的数,可以根据这个判断,发现是每七位一组循环加密

先提取 enc[]

def read_brackets_from_file(filename):
    bracket_count = 0
    result = []
    with open(filename, 'r') as f:
        while True:
            char = f.read(1)
            if not char:
                break
            if char == ']':
                bracket_count += 1
            else:
                if bracket_count > 0:
                    result.append(bracket_count)
                    bracket_count = 0
    if bracket_count > 0:
        result.append(bracket_count)
    return result
filename = "./flag.enc"
result = read_brackets_from_file(filename)
print(result)
# 可以发现后续都是填充用的,不需要管

介于加密逻辑不能调确实静态看挺麻烦的,反正就猜猜测测拿到 exp.py:

enc = [34, 99, 1168, 332, 6765, 197, 117, 5, 118, 784, 396, 6435, 205, 128, 60, 68, 832, 460, 5885, 192, 92, 5, 111, 1872, 312, 5500, 148, 158, 48, 68, 1040, 440, 5500, 130, 154, 90, 111, 1856, 288, 2805, 192, 117, 13, 126, 1616, 456, 5225, 216, 123, 17, 123, 1680, 440, 3905, 150, 139, 12, 118, 1520, 392, 2695, 207, 141, 49, 73, 528, 500, 550]
print(len(enc))
for i in range(len(enc)):
    if ((i + 1) % 7 == 1):
        enc[i] ^= ord('A') ^ 34
    if ((i + 1) % 7 == 2):
        enc[i] ^= 0x30
    if ((i + 1) % 7 == 3):
        enc[i] //= 16
    if ((i + 1) % 7 == 4):
        enc[i] //= 4
    if ((i + 1) % 7 == 5):
        enc[i] //= 55
    if ((i + 1) % 7 == 6):
        enc[i] -= 97
    if ((i + 1) % 7 == 0):
        enc[i] -= 44
for i in enc:
    print(chr(i),end='')
print()
# ASIS{dIfF1culT_t4sk_0f_uNd3rStAnd!n9_tH3_InNer_wOrKinG5_oF_b1naRy!}

# sid

cpp 玩意跟混淆了一样… 真的难顶

image-20230924085847189

不过其实也就用了点 stl string 多做点可以猜猜

坏了,现在复盘的时候发现了,前面已经截掉 ASIS{ 了,昨天爆破的头晕眼花了把这事给忘了…

image-20230924085932475

有些大数操作看起来是编译器优化的产物,动调过去就知道没啥用了

主要加密逻辑(已经手动写了符号):

image-20230924090138635

其中比较难逆的就是 string_append_and_some_kind_of_enc 这个玩意(低水平起名)

里面都是很看不懂的大数操作和 string 操作

image-20230924090248263

不过进来就可以看到特征数了

image-20230924090324244

注意这里还做了魔改,这个 some_enc 看起来就是流加密 chacha20 或者 salsa20 的流密钥初始化,但是他下面还加了异或随机数

image-20230924090624372

分别写 chacha20 脚本和 salsa 脚本测试一下:

from Crypto.Cipher import ChaCha20
# chacha20
key = b'\x07\x05\x0B\x0D\x0F\x0D\x0B\x05\x07\x05\x1B\x1D\x1F\x1D\x1B\x05\x07\x05\x0B\x0D\x0F\x37\x35\x3B\x3D\x3F\x07\x05\x0B\x0D\x0F\x0D'
plaintext = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_"
nonce = b'\x00\x00\x00\x00\x00\x00\x00\x00'
cipher = ChaCha20.new(key=key,nonce=nonce)
ciphertext = cipher.encrypt(plaintext)
# 获取 nonce(用于解密)
# nonce = cipher.nonce
# 解密
# cipher = ChaCha20.new(key=key, nonce=nonce)
# decrypted_text = cipher.decrypt(ciphertext)
a = [hex(i) for i in ciphertext]
print(a)
b = [    0x20, 0xFD, 0x17, 0xA2, 0x34, 0x4E, 0x3C, 0x87, 0x80, 0x06,
  0x77, 0x53, 0x27, 0x13, 0x73, 0x9E, 0xFA, 0x61, 0x75, 0x1D,
  0x49, 0xDB, 0x27, 0x5D, 0xEC, 0x63, 0xD4, 0x09, 0x1C, 0x4C,
  0x7E, 0x83, 0xC5, 0x63, 0xA3, 0xD9, 0x47, 0x6B, 0xFB, 0x65,
  0x40, 0x6B, 0xF5, 0x5F, 0x9C, 0xCF, 0x49, 0x17, 0x6C, 0xE8,
  0x99, 0xEF, 0xC3, 0xED, 0x95, 0x75, 0xAF, 0x70, 0x4D, 0x70,
  0x47, 0xD1, 0x36, 0x00]
for i in range(32):
    print(hex(int(a[i],16) ^ b[i]),end=' ')
from Crypto.Cipher import Salsa20
# Salsa20
key = b'\x07\x05\x0B\x0D\x0F\x0D\x0B\x05\x07\x05\x1B\x1D\x1F\x1D\x1B\x05\x07\x05\x0B\x0D\x0F\x37\x35\x3B\x3D\x3F\x07\x05\x0B\x0D\x0F\x0D'
nonce = b'\x00\x00\x00\x00\x00\x00\x00\x00'
plaintext = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_"
cipher = Salsa20.new(key=key , nonce=nonce)
ciphertext = cipher.encrypt(plaintext)
a = [hex(i) for i in ciphertext]
print(a)
b = [  0xFA, 0xCD, 0xCD, 0x92, 0xEE, 0x7E, 0xE6, 0xB7, 0x5A, 0x36,
  0xAD, 0x63, 0xFD, 0x23, 0xA9, 0xAE, 0x20, 0x51, 0xAF, 0x2D,
  0x93, 0xEB, 0xFD, 0x6D, 0x36, 0x53, 0x0E, 0x39, 0xC6, 0x7C,
  0xA4, 0xB3, 0xC5, 0x63, 0xA3, 0xD9, 0x47, 0x6B, 0xFB, 0x65,
  0x40, 0x6B, 0xF5, 0x5F, 0x9C, 0xCF, 0x49, 0x17, 0x6C, 0xE8,
  0x99, 0xEF, 0xC3, 0xED, 0x95, 0x75, 0xAF, 0x70, 0x4D, 0x70,
  0x47, 0xD1, 0x36, 0x00]
for i in range(32):
    print(hex(int(a[i],16) ^ b[i]),end=' ')

发现 chacha20 得到的是 2 个 * 16 组的随机数,故而确定这里是 chacha20+随机数 的流加密魔改

除此之外,还有几个 xor 操作,比较好逆,结合看一下最后的输出,就有整个流程:

输入32位的flag和若干位的pass(32位)

flag = flag ^ pass

tmp = chacha20(key = flag, plaintext = pass, nonce = b'\x00\x00\x00\x00\x00\x00\x00\x00')

cybertext1 = flag ^ random1 # random2也是两个随机数重复16次

cybertext2 = tmp ^ random2 # random1是两个随机数重复16次

output = hex_dump(cybertext1 + cybertext2)

解密时需要爆破四个随机数…0xff ^ 4 的解空间,这里用的是 python 硬爆,需要 12 小时

image-20230924091010562

毕竟不是冲分比赛,如果是的话可以到此找台队里好的电脑或者服务开爆(×)

不过自己做可以想想优化,可以看到 pass 全程都是被流加密的,原理上只做了单位异或

也就是说可以少爆一个 random 值,爆破两次,分别拿到 flag 的奇偶位,这样就是 0xff ^ 3 * 2 的解空间,而 0xff ^ 3 大概是四分钟左右

image-20230924091433563 再做一点细微优化,可以缩减到两分半钟每次,那么总的单次爆破时间就缩减到了六分钟,调试和 debug 变得基本可行(本身流程比较复杂确实也没有一次写对,犯低级错误浪费时间了)

image-20230924091828715

然后大概优化一下判断匹配标准,并且把奇偶位拼接上,基本上就是最后脚本了(个人还是觉得这里的一步步想法推进挺有意思的,就是 debug 过程很痛苦,每调一次就得在一堆乱七八糟的玩意里找乱七八糟的 flag,这里把 ASIS {的标志位留下来感觉体验会好很多…)

cybertext1 = 'e5be2ec5c9d2c1532abbbd7a907217a2fd078f46b102817d062124b0b02a5de9'
cybertext2 = 'dcc3955c839144d7115f093c7fc198107779920a2c1b6ba2ae44b372849e6187'
cybertext1_ori = [int(cybertext1[i:i+2], 16) for i in range(0, len(cybertext1), 2)]
cybertext2_ori = [int(cybertext2[i:i+2], 16) for i in range(0, len(cybertext2), 2)]
cybertext1 = [int(cybertext1[i:i+2], 16) for i in range(0, len(cybertext1), 2)]
cybertext2 = [int(cybertext2[i:i+2], 16) for i in range(0, len(cybertext2), 2)]
print(cybertext1)
print(cybertext2)
from Crypto.Cipher import ChaCha20
from tqdm import tqdm
total_iterations = (0xff+1) * (0xff+1) * (0xff+1)
flag_list_odd = []
flag_list_even = []
flag_list = []
with open('./forceOdd.bin','ab') as file:
    with tqdm(total=total_iterations) as pbar:
        for random1_1 in range(0, 1):
            for random1_2 in range(0, 0xff + 1):
                for i in range(len(cybertext1)):
                    if (i % 2 == 0):
                        cybertext1[i] = cybertext1_ori[i] ^ random1_1
                    else:
                        cybertext1[i] = cybertext1_ori[i] ^ random1_2
                for random2_1 in range(0, 0xff + 1):
                    for random2_2 in range(0, 0xff + 1):
                        for i in range(len(cybertext2)):
                            if (i % 2 == 0):
                                cybertext2[i] = cybertext2_ori[i] ^ random2_1
                            else :
                                cybertext2[i] = cybertext2_ori[i] ^ random2_2
                        key = bytes(cybertext2)
                        cybertext = bytes(cybertext1)
                        nonce = b'\x00\x00\x00\x00\x00\x00\x00\x00'
                        cipher = ChaCha20.new(key=key, nonce=nonce)
                        plain = cipher.encrypt(cybertext)
                        plain = bytes([x ^ y for x, y in zip(plain, key)])
                        if all(33 <= int(plain[i]) <= 126 for i in range(1, len(plain), 2)):
                            modified_plain = bytearray([plain[i] for i in range(1, len(plain), 2)])  # 创建一个可变的字节数组
                            file.write(bytes(modified_plain) + b'\n')
                            flag_list_odd.append(bytes(modified_plain) + b'\n')
                        pbar.update(1)
with open('./forceEven.bin', 'ab') as file:
    with tqdm(total=total_iterations) as pbar:
        for random1_1 in range(0, 0xff + 1):
            for random1_2 in range(0, 1):
                for i in range(len(cybertext1)):
                    if (i % 2 == 0):
                        cybertext1[i] = cybertext1_ori[i] ^ random1_1
                    else:
                        cybertext1[i] = cybertext1_ori[i] ^ random1_2
                for random2_1 in range(0, 0xff + 1):
                    for random2_2 in range(0, 0xff + 1):
                        for i in range(len(cybertext2)):
                            if (i % 2 == 0):
                                cybertext2[i] = cybertext2_ori[i] ^ random2_1
                            else :
                                cybertext2[i] = cybertext2_ori[i] ^ random2_2
                        key = bytes(cybertext2)
                        cybertext = bytes(cybertext1)
                        nonce = b'\x00\x00\x00\x00\x00\x00\x00\x00'
                        cipher = ChaCha20.new(key=key, nonce=nonce)
                        plain = cipher.encrypt(cybertext)
                        plain = bytes([x ^ y for x, y in zip(plain, key)])
                        if all(33 <= int(plain[i]) <= 126 for i in range(0, len(plain), 2)):
                            modified_plain = bytearray([plain[i] for i in range(0, len(plain), 2)])
                            file.write(bytes(modified_plain) + b'\n')
                            flag_list_even.append(bytes(modified_plain) + b'\n')
                        pbar.update(1)
for odd in flag_list_odd:
    for even in flag_list_even:
        tmp_flag = ''
        for i, j in zip(odd, even):
            tmp_flag += chr(j)
            tmp_flag += chr(i)
        flag_list.append(tmp_flag)
print(flag_list)

这里最后的版本需要肉眼在六百多条里面找真 flag,应该算目前我能想的最优的爆破了,大概效果如下(还是相当精神污染的,可能是自己题做少了也说不准…):

image-20230924092335126

(后话:结果是 python 本身 Crypto 库的瓶颈,导致需要这样优化,其他师傅自己 cpp 写的满 0xff ^ 4 空间的爆破也就十几分钟左右,同样空间下 python 就得十几个小时了…)

# scrat

这个题其实和 sid 差不多,也是麻烦的加密和爆破(解数比 sid 多的原因完全可能是很多师傅和我一开始一样 sid 爆出来了但是没看见 flag)

yysy,算是练习调试、 cpp string 导致的一些奇怪的汇编代码、猜测能力吧,新东西确实没有啥了

几处加密:

image-20230925191134745

image-20230925191920010

image-20230925191831529

流程:

n
tmp = flag[i] * (39 + 1 - i)
s = sum(flag)
s = (s + (-1) ^ (i) * tmp) % 0x4141a15b
s = s * 39 % 0x4141a15b
seed = 1
v33 = 2
while s != 0:
    if s & 1 != 0:
        seed = v33 * seed % 0x4141A15B
    v33 = v33 * v33 % 0x4141A15B
    s >>= 1
for i in range(39):
    seed = seed * 166013 + 4148159
    flag[i] ^= (seed // 0x10000) & 0xff

exp.py (比赛结束了抄个 JANlittle 师傅的,感觉基本功确实还缺点 qaq)

enc = bytes.fromhex('3a392465ffefc6b485193c32cefdb99df95cb6d6566478a17873b92b2df5dea31e5c0edbb1cc9b')
for s in range(5000000):
    seed = 1
    v33 = 2
    while s != 0:
        if s & 1 != 0:
            seed = v33 * seed % 0x4141A15B
        v33 = v33 * v33 % 0x4141A15B
        s >>= 1
    t = list(enc)
    for i in range(39):
        seed = seed * 166013 + 4148159
        t[i] ^= (seed // 0x10000) & 0xff
        if i == 3 and not bytes(t).startswith(b'ASIS'):
            break
    if bytes(t).startswith(b'ASIS'):
        print(bytes(t))
# ASIS{___A_9!Rl_w1Th_Dr4G0N_7AtTO5___!!}