强网杯2024 Re WriteUp

Posted by Aaron on November 28, 2024

solve2-apk

我们首先使用jeb进行分析,搜索关键词success定位到逻辑 image-20241129233403123

外层函数是一个魔改tea,过了前32字节检测才能进入下一个函数进行判断 image-20241129233648640

#include <iostream>
#include <cstdio>
#include <stdint.h>  // For uint32_t
using namespace std;

void tea_decrypt(uint32_t* v) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i; 
    uint32_t delta = 0x9e3779b9;
    uint32_t k[5] = { 598323648, 1213115916, 970832168, 274853062};
    
    for (i = 0; i < 32; i++) {

        v1 -= (((v0 << 4) + k[2] ^ v0) + (sum ^ (v0 >> 5)) + k[3]);
        v0 -= (((v1 << 4) + k[0] ^ v1) + (sum ^ (v1 >> 5)) + k[1]);
		sum -= delta;
    }
    v[0] = v0;
    v[1] = v1;
}

uint32_t switchEndian(uint32_t num) {
    return ((num >> 24) & 0x000000FF) | // 取最高字节
           ((num >> 8) & 0x0000FF00) | // 取第二字节
           ((num << 8) & 0x00FF0000) | // 取第三字节
           ((num << 24) & 0xFF000000); // 取最低字节
}

int main() {
    uint32_t key[] = { 598323648, 1213115916, 970832168, 274853062 };

    uint32_t data[] = {
        0x5E5440B0, 2057046228, 0x4A1ED228, 0x233FE7C, 0x96461450, 0x88A670ED, 0xF79BFC89, 0x20C3D75F,0
    };

    for (int i = 0; i < 8; i += 2) {
        tea_decrypt(&data[i]);
    }
    
    for (int i = 0; i < 8; ++i) {
         data[i] = switchEndian(data[i]);
    }
	printf("%s",data);
    
    return 0;
}
// Come on you are about to get it>

即可得到前32位的正确数据,将后面的测试数据放在>后继续在H0.a.successWithString()中进行二轮check 进入这个函数即可看到两个[256]的sbox,将部分数据搜索即可知为twofish算法 image-20241130153432307

image-20241130153245125

找到源码与jeb里的很相似

   /**
    * Use (12, 8) Reed-Solomon code over GF(256) to produce a key S-box
    * 32-bit entity from two key material 32-bit entities.
    *
    * @param  k0  1st 32-bit entity.
    * @param  k1  2nd 32-bit entity.
    * @return  Remainder polynomial generated using RS code
    */
   private static final int RS_MDS_Encode( int k0, int k1) {
      int r = k1;
      for (int i = 0; i < 4; i++) // shift 1 byte at a time
         r = RS_rem( r );
      r ^= k0;
      for (int i = 0; i < 4; i++)
         r = RS_rem( r );
      return r;
   }

image-20241130154603346

我们在H0.a.c处下断点动调获取key

image-20241130164840825

image-20241130165101102

即可得到twofish的key 根据代码可知有两段data[16],我们可以对v2[15]下断点得到所有的data image-20241130165748281

import twofish

key = bytes.fromhex("000102030405060708090a0b0c0d0e0f")  # key
tf = twofish.Twofish(key)
data1 = bytes([159, 46, 128, 211, 56, 34, 22, 223, 236, 150, 252, 143, 26, 34, 136, 115])
decrypted1 = tf.decrypt(data1)
print(decrypted1)
#flag{iT3N0t7H@tH

即可得到前半段flag,我们将前半部分flag输入进去再进行check即可得到part2的check

Come on you are about to get it>flag{iT3N0t7H@tH111111111111111}

之后有对我们传入的测试值的flag的part2的异或数据提取出来(这第二段算法是rc4,直接将加密后的值异或回去即可得到) image-20241130174040159

将这段数据异或我们的输入再异或data2[16]即可还原得到第二段flag

data2 = [169, 217, 118, 189, 119, 187, 86, 154, 49, 179, 222, 168, 101, 142, 26, 50]
enc1 = bytes([0xD8, 0xAD, 0x71, 0xC8, 0x76, 0xD3, 0x28, 0xFD, 0x37, 0xEA, 0xA6, 0xF7, 0x3F, 0xEC, 0x1B, 0x32])
enc2 = b'111111111111111}'
dec2 = ''.join(chr(data2[i] ^ enc1[i] ^ enc2[i]) for i in range(len(data2)))
print(dec2)
#@E6D0YOV7hInkS0}

boxx

分析程序发现为一个推箱子的游戏,使用上下左右进行各个函数的判断

49d27e3743e1df9b5ad11260819f70d8

在最开始输出的Buffer里有游戏规则和提示: flag是每个关卡中每个箱子移动的最短的次数拼接的md5码值和几个字符,1.flag{四个字符_md5值},2.注意同一张图箱子不一定只有一个哦3.同一关需要计算所有箱子的总的最小移动次数,将每一关的最短次数拼接 解释:例如第一关是3第二关是5,就是md5(35…)

67ec12f07b0d4184b86a455452cc80a6

函数包含一个20x20的地图 809801031f640cf9e710b13ce9a6a008

我们将迷宫提取为20x20的格式

6a206ca9b4b94c4163ecab299b0cf1c4

可以看到最后包含 qwb!的明文,猜测这里就是四个字符 然后我们可以开始求map的3到4的最短路径

map1:

e821be5c89d87024620a3329bcb6b516

最短路径为2 ……以此类推……

14aea87b987605426e765c07faeea1cc

按照提示将所有的最短路径拼接在一起进行md5后再添加四个字符即可得到flag

Mips

mips_bin中有一串假flag,发现下方mmap了0x23000的地址 image-20241207000454379

搜索了mips_bin没看到其他的关键逻辑,转头分析emu的代码

image-20241207001530659

sub_33D48E是rc4和一些异或移位操作的混合,并且将输入的data异或了一个未知数,再将data的7,11和12,16调换位置,再进行最后的check

image-20241207003656475

可以得到脚本

from Crypto.Cipher import ARC4

for i in range(256):
    rc4_key = b'6105t3'
    data = bytearray(
        [0xC4, 0xEE, 0x3C, 0xBB, 0xE7, 0xFD, 0x67, 0x1D, 0xF8, 0x97, 0x68, 0x9D, 0x0B, 0x7F, 0xC7, 0x80, 0xDF, 0xF9,
         0x4B, 0xA0, 0x46, 0x91])
    # swap
    data[7], data[11] = data[11], data[7]
    data[16], data[12] = data[12], data[16]
    #xor unknow_num
    for j in range(22):
        data[j] ^= i
    #rc4 decrypt
    rc4 = ARC4.new(rc4_key)
    enc = rc4.decrypt(data)

    key = [0xde, 0xad, 0xbe, 0xef]
    enc = bytearray(enc)
    for k in range(22):
        enc[k] ^= key[k % 4]

    flag = [102,108,97,103,123] # b'flag{' is prefix
    for i in range(22):
        for t in range(256):
            v7 = (((((t << 7) & 0xff) | (t >> 1)) << 6) ^ 0xC0 | ((((t << 7) | (t >> 1)) & 0xff) >> 2) ^ 0x3B) ^ 0xBE
            v7 &= 0xff
            # v8 = (((32 * v7) | (v7 >> 3)) ^ 0xAD)
            # v9 = (((16 * (((32 * v7) | (v7 >> 3)) ^ 0xAD)) | ((((32 * v7) | (v7 >> 3)) ^ 0xAD) >> 4)) ^ 0xDE)
            cmp = ((((((16 * (((32 * v7) | (v7 >> 3)) ^ 0xAD)) | (((((32 * v7) | (v7 >> 3)) ^ 0xAD) & 0xff) >> 4)) ^ 0xDE) & 0xff) >> 5) | (8 * (((16 * (((32 * v7) | (v7 >> 3)) ^ 0xAD)) | (((((32 * v7) | (v7 >> 3)) ^ 0xAD) & 0xff) >> 4)) ^ 0xDE)))
            cmp &= 0xff
            if cmp == enc[i]:
                flag.append(t)
                # print(bytes(flag))
                break

#len 22 add "flag{" len is 27
    if(len(flag) == 27 and flag[-1] == ord('}')):
        print(bytes(flag))