网络安全之CTF专题赛RE题解

发布于:2025-06-14 ⋅ 阅读:(19) ⋅ 点赞:(0)

easyre

hap文件改成zip格式然后解压去反编译abc文件即可拿到源码

这里推荐一个网站.abcD

蛮好用的

下载反编译结果,解压后用vscode打开分析。

这里可以看到一些目录结构,我们先看看flag目录

x_2_2.count位1000000的时候就会输出flag那么大概率是一个点击程序可以用DevEco Studio里面的模拟器安装一下这个程序。

点击到100万次不太可能,我们还是看代码逻辑

这是flag的生成方式

router_.getParams().hint1 + x_2_2.getH2(x_2_2.magic)

第二部分flag

这里我们看到getH2的逻辑,实际上就是decodeToString函数

我们去到coder里面看它的逻辑

里面的话调了两个函数

x_1_8是标准的base解码

而x_1_7是反转

x_1_7 = function convertToString(p1) {
  let r10, r19, r32, r33;

  // 将输入参数 p1 赋值给 r10
  r10 = p1;

  // 如果 p1 是 ArrayBuffer(原始二进制数据),转换为 Uint8Array 后再调用 x_1_1(可能是解码函数)
  if (p1 instanceof globalThis.ArrayBuffer) {
    const r5 = new globalThis.Uint8Array(p1); // 转为字节数组
    r10 = x_1_1(r5); // 调用自定义函数 x_1_1 处理
  }

  // 再次赋值 r10 给 r19
  r19 = r10;

  // 如果 r10 是 Uint8Array 类型,再调用一次 x_1_1 处理
  if (r10 instanceof globalThis.Uint8Array) {
    r19 = x_1_1(r10);
  }

  // 如果处理后的 r19 不是字符串,抛出异常
  if (typeof r19 != 'string') {
    throw Error('Unsupported type');
  } else {
    // 倒序构造字符串
    r32 = r19.length - 1; // 从最后一个字符开始
    r33 = ''; // 用于保存倒序结果

    while (true) {
      if (r32 < 0) {
        return r33; // 倒序完成,返回结果
      } else {
        r32 = r32 - 1;
        r33 = r33 + r19[r32 + 1]; // 注意此处 r32 先减再加回,所以还是访问从尾到头的字符
      }
    }
  }
};

我们根据这个逻辑去解一下magic就能拿到第二部分flag

part2:38bad98fa3074dd6adc8cc434f22c48b4d4

第一部分flag

第一部分逻辑在index

密文

自解密部分,这块实际上就是首先+上自己的长度,然后反转,逐字符-i在反转

拿密文正向跑这个程序即可

hint1 = 'tlfr`llakodZbjW_aR'

r10 = ''.join([chr(ord(c) + len(hint1)) for c in hint1])


r10_rev = r10[::-1]


r43 = ''.join([chr(ord(c) - i) for i, c in enumerate(r10_rev)])


final_hint1 = r43[::-1]
print(final_hint1)

part1:universityofoxford

arkts

这题还是蛮简单的

这里用jadx-dev-all去反编译,因为上面的那个网站反编译不完全。

将hap改成zip后解压,把abc文件直接拖到工具里即可

这里可以看到加密顺序先经过rc4然后再base64再rsa加密一下

这里rc4和base64都不是标准的

这里是密文数组和一个假的key

调用onPageShow的时候会把key替换

所以key是OHCTF2026

我们先去看看rc4

魔改点在sbox和加密的地方

生成sbox只用了一个i

加密的地方把异或变成了+

解rc4

我们去看看base64

很明显的换表逻辑

解base64

最后我们去看rsa

e是7

N是75067

N很小直接拿yafu去分了

得到p = 271 q=277

那么私钥d也就能求了

完整解密脚本

from Crypto.Util.number import *
import base64

enc = ["ndG5nZa=", "nte3ndK=", "nJy2nJi=", "mtK0mJG=", "nde5mZK=", "mtiWnda=", "ntq1nZm=", "mZG0mJq=", "nJe4ma==", "nJG4mW==", "mJa0mZG=", "mty1mte=", "mtu3odq=", "nJyZmJy=", "nJeWody=", "mJy1ntm=", "ntaWody=", "ma==", "ntqYodK=", "ndK2nJm=", "nJyZndq=", "ntaWody=", "ndGYndi=", "nJG4mW==", "mJu5mG==", "mtiYmda=", "mZmWnde=", "mteXndC=", "ndqXndm=", "mte1mZi=", "mJy5ntq=", "mZC4mtC=", "mJe4nW==", "nJC3odu=", "ndyXmdK=", "ndG5nZa=", "ndaZnZa=", "mtK0nJa="]

def custom_base64_decode(custom_b64_str):
    custom_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"
    standard_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"


    trans_table = str.maketrans(custom_chars, standard_chars)

    standard_b64_str = custom_b64_str.translate(trans_table)

    decoded_bytes = base64.b64decode(standard_b64_str)

    return decoded_bytes

def DeRsa(num):
    c=num
    n = 75067
    p = 271
    q = 277
    e = 7
    phi = (p - 1) * (q - 1)
    d = inverse(e, phi)
    result = pow(c, d, n)
    return result


def rc4_decrypt(key, data):
    S = list(range(256))
    j = 0
    key_length = len(key)

    for i in range(256):
        j = (j + S[j] + key[j % key_length]) % 256
        S[i], S[j] = S[j], S[i]

    i = j = 0
    out = []

    for b in data:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        K = S[(S[i] + S[j]) % 256]
        val = (b - K) % 256
        out.append(val)

    return bytes(out)
key = "OHCTF2026"
key = [ord(i) for i in key]

decoded_numbers = [int(custom_base64_decode(i).decode()) for i in enc]
decoded_numbers = [DeRsa(i) for i in decoded_numbers]
plain = rc4_decrypt(key, decoded_numbers)
print(plain)

Secret

这题还是蛮新颖的,解手势锁,然后解魔改sm4,上传解出来的图片得到flag

要先解手势锁

这里我还是用上面那个网站反编译的代码,虽然有部分代码反编译不完全,但不影响分析

直接来看lock

有个默认密码,但是那个没有用,因为后面验证的密文不是这个

很明显的check逻辑

从@normalized:Y&&&libsecret.so&.mjs加载的。我们去看libsecret.so

交叉引用过去下面有个函数

很明显的check逻辑

看看init_proc

unsigned __int64 __fastcall init_proc_(unsigned int *a1)
{
  unsigned int v1; // eax
  unsigned int v2; // r15d
  unsigned int v3; // r8d
  unsigned int v4; // ebx
  unsigned int v5; // r9d
  unsigned int v6; // ebp
  unsigned int v7; // r14d
  unsigned int v8; // ecx
  unsigned int v9; // r11d
  int v10; // edx
  unsigned int v11; // ebx
  __int64 v12; // r14
  int v13; // r12d
  int v14; // ecx
  unsigned int v15; // edi
  unsigned int v17; // [rsp+8h] [rbp-80h]
  unsigned int v18; // [rsp+18h] [rbp-70h]
  unsigned int v20; // [rsp+28h] [rbp-60h]
  __int128 v21; // [rsp+40h] [rbp-48h]
  unsigned __int64 v22; // [rsp+50h] [rbp-38h]

  v22 = __readfsqword(0x28u);
  v21 = what;
  v1 = a1[8];
  v2 = *a1;
  v3 = a1[1];
  v4 = a1[4];
  v17 = a1[5];
  v5 = a1[6];
  v6 = a1[2];
  v7 = a1[3];
  v8 = a1[7];
  v9 = -1640531527;
  v10 = -11;
  do
  {
    v18 = v4;
    v11 = v7;
    v20 = v8;
    v12 = (v9 >> 2) & 3;
    v13 = *((_DWORD *)&v21 + v12);
    v2 += (((v1 >> 5) ^ (4 * v3)) + ((v3 >> 3) ^ (16 * v1))) ^ ((v9 ^ v3) + (v13 ^ v1));
    v3 += (((v2 >> 5) ^ (4 * v6)) + ((v6 >> 3) ^ (16 * v2))) ^ ((v9 ^ v6)
                                                              + (v2 ^ *((_DWORD *)&v21 + ((v9 >> 2) & 3 ^ 1))));
    v6 += (((v3 >> 5) ^ (4 * v11)) + ((v11 >> 3) ^ (16 * v3))) ^ ((v9 ^ v11)
                                                                + (v3 ^ *((_DWORD *)&v21 + ((v9 >> 2) & 3 ^ 2))));
    v14 = *((_DWORD *)&v21 + ((unsigned int)v12 ^ 3));
    v7 = v11 + ((((v6 >> 5) ^ (4 * v18)) + ((v18 >> 3) ^ (16 * v6))) ^ ((v9 ^ v18) + (v6 ^ v14)));
    v4 = v18 + ((((v7 >> 5) ^ (4 * v17)) + ((v17 >> 3) ^ (16 * v7))) ^ ((v9 ^ v17) + (v7 ^ v13)));
    v15 = (((((((v4 >> 5) ^ (4 * v5)) + ((v5 >> 3) ^ (16 * v4))) ^ ((v9 ^ v5)
                                                                  + (v4 ^ *((_DWORD *)&v21 + ((v9 >> 2) & 3 ^ 1)))))
           + v17) >> 5) ^ (4 * v20))
        + ((v20 >> 3) ^ (16
                       * (((((v4 >> 5) ^ (4 * v5)) + ((v5 >> 3) ^ (16 * v4))) ^ ((v9 ^ v5)
                                                                               + (v4 ^ *((_DWORD *)&v21
                                                                                       + ((v9 >> 2) & 3 ^ 1)))))
                        + v17)));
    v17 += (((v4 >> 5) ^ (4 * v5)) + ((v5 >> 3) ^ (16 * v4))) ^ ((v9 ^ v5)
                                                               + (v4 ^ *((_DWORD *)&v21 + ((v9 >> 2) & 3 ^ 1))));
    v5 += v15 ^ ((v9 ^ v20) + (v17 ^ *((_DWORD *)&v21 + ((v9 >> 2) & 3 ^ 2))));
    v8 = v20 + ((((v5 >> 5) ^ (4 * v1)) + ((v1 >> 3) ^ (16 * v5))) ^ ((v9 ^ v1) + (v5 ^ v14)));
    v1 += (((v8 >> 5) ^ (4 * v2)) + ((v2 >> 3) ^ (16 * v8))) ^ ((v9 ^ v2) + (v8 ^ v13));
    v9 -= 1640531527;
    ++v10;
  }
  while ( v10 );
  a1[1] = v3;
  *a1 = v2;
  a1[2] = v6;
  a1[3] = v7;
  a1[4] = v4;
  a1[5] = v17;
  a1[7] = v8;
  a1[6] = v5;
  a1[8] = v1;
  return __readfsqword(0x28u);
}

xxtea加密

key是what

密文是is

注意小端,建议用ida get_wide_dword()去提取

#include <stdint.h>
#include <stdio.h>

void f(uint32_t* a, int n, uint32_t k[4]) {
    if (n < 2) return;
    uint32_t d = 0x9E3779B9;
    int r = 6 + 52 / n;
    uint32_t s = r * d;
    uint32_t x, y, z, e;
    int i;
    x = a[0];
    while (r--) {
        e = (s >> 2) & 3;
        for (i = n - 1; i > 0; i--) {
            z = a[i - 1];
            uint32_t t1 = (z >> 5) ^ (x << 2);
            uint32_t t2 = (x >> 3) ^ (z << 4);
            uint32_t t3 = s ^ x;
            uint32_t t4 = k[(i & 3) ^ e] ^ z;
            a[i] -= (t1 + t2) ^ (t3 + t4);
            x = a[i];
        }
        z = a[n - 1];
        uint32_t t1 = (z >> 5) ^ (x << 2);
        uint32_t t2 = (x >> 3) ^ (z << 4);
        uint32_t t3 = s ^ x;
        uint32_t t4 = k[e] ^ z;
        a[0] -= (t1 + t2) ^ (t3 + t4);
        x = a[0];
        s -= d;
    }
}

int main() {
    uint32_t k[4] = { 0xB, 0x2D, 0xE, 0x1BF52 };
    uint32_t a[9] = {
        0xeb159b69, 0x71efca1b, 0x91c9c6c6, 0x957af873, 0xd3deab9, 0x27894343, 0x61d6415b, 0x1f80fed8, 0xdf62f1d9
        };
    f(a, 9, k);
    for (int i = 0; i < 9; i++) printf("[%d] 0x%08X\n", i, a[i]);
    for (int i = 0; i < 9; i++) {
        uint8_t* b = (uint8_t*)&a[i];
        for (int j = 0; j < 4; j++) printf("%c", b[j]);
    }
    printf("\n");
    return 0;
}

134507286

要我上传一个图片,我在资源文件里看到了个enc文件,应该是要解这个

获取上传的内容,base64编码后传给bb.txt

从资源文件种加载enc给x_4_1

和enc check

我们去看一下这个ValidateCiphertext

看它下面的这个函数

sm4轮密钥生成

sm4 32轮迭代

最后再base64

所以这就是enc的加密逻辑

其中sm4有魔改 多异或了一个tea的delta常量

解下来编写解密代码,首先先解base64

我没有写文件io去解这个文件,而是手动填密文进去

转换一下格式

enc ="""填密文"""
enc = enc.split("\n")
print(len(enc))
print(len(enc)/4)
for i in range(len(enc)):
    print("0x"+enc[i],end=",")

然后写个sm4解密脚本

密文填到这里面即可

再解一层base64

很明显的图片文件 我们保存一下

传到程序里

得到flag

解sm4脚本

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdint.h>

typedef uint32_t u32;
typedef uint8_t u8;

u8 Sbox[256] = {
     0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6,
  0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05, 0x2B, 0x67, 0x9A, 0x76,
  0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86,
  0x06, 0x99, 0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A,
  0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62, 0xE4, 0xB3,
  0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA,
  0x75, 0x8F, 0x3F, 0xA6, 0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73,
  0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
  0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB,
  0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35, 0x1E, 0x24, 0x0E, 0x5E,
  0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21,
  0x78, 0x87, 0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52,
  0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E, 0xEA, 0xBF,
  0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE,
  0xF9, 0x61, 0x15, 0xA1, 0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34,
  0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
  0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29,
  0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F, 0xD5, 0xDB, 0x37, 0x45,
  0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C,
  0x5B, 0x51, 0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F,
  0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8, 0x0A, 0xC1,
  0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12,
  0xB8, 0xE5, 0xB4, 0xB0, 0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96,
  0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
  0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE,
  0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48
};

u32 FK[4] = { 0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC }; // 固定参数FK
u32 CK[32] = {

    0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
};

u32 functionB(u32 b) {
    u8 a[4];
    a[0] = (b >> 24) & 0xFF;
    a[1] = (b >> 16) & 0xFF;
    a[2] = (b >> 8) & 0xFF;
    a[3] = b & 0xFF;

    return (Sbox[a[0]] << 24) | (Sbox[a[1]] << 16) | (Sbox[a[2]] << 8) | Sbox[a[3]];
}

u32 loopLeft(u32 a, short length) {
    length = length % 32;
    return (a << length) | (a >> (32 - length));
}

u32 functionL1(u32 a) {
    return a ^ loopLeft(a, 2) ^ loopLeft(a, 10) ^ loopLeft(a, 18) ^ loopLeft(a, 24);
}

u32 functionL2(u32 a) {
    return a ^ loopLeft(a, 13) ^ loopLeft(a, 23);
}

u32 functionT(u32 a, short mode) {
    if (mode == 1)
        return functionL1(functionB(a));
    else
        return functionL2(functionB(a));
}

void extendFirst(u32 MK[], u32 K[]) {
    for (int i = 0; i < 4; i++) {
        K[i] = MK[i] ^ FK[i];
    }
}

void extendSecond(u32 RK[], u32 K[]) {
    for (int i = 0; i < 32; i++) {
        K[(i + 4) % 4] = K[i % 4] ^ functionT(K[(i + 1) % 4] ^ K[(i + 2) % 4] ^ K[(i + 3) % 4] ^ CK[i], 2);
        RK[i] = K[(i + 4) % 4];
    }
}

void getRK(u32 MK[], u32 K[], u32 RK[]) {
    extendFirst(MK, K);
    extendSecond(RK, K);
}

void iterate32(u32 X[], u32 RK[]) {
    for (int i = 0; i < 32; i++) {
        u32 tmp = functionT(X[(i + 1) % 4] ^ X[(i + 2) % 4] ^ X[(i + 3) % 4] ^ RK[i], 1);
        X[(i + 4) % 4] = X[i % 4] ^ tmp ^ 0x9E3779B9;
    }
}

void reverse(u32 X[], u32 Y[]) {
    for (int i = 0; i < 4; i++) {
        Y[i] = X[3 - i];
    }
}

void encryptSM4(u32 X[], u32 RK[], u32 Y[]) {
    iterate32(X, RK);
    reverse(X, Y);
}

void decryptSM4(u32 X[], u32 RK[], u32 Y[]) {
    u32 reverseRK[32];
    for (int i = 0; i < 32; i++) {
        reverseRK[i] = RK[31 - i];
    }
    iterate32(X, reverseRK);
    reverse(X, Y);
}


int main(void) {
    u32 enc[11316] = { 0 };
    u32 X[4] = { 0 };
    u32 Y[11316] = { 0 };
    u32 MK[4] = { 0xE52BCC34, 0x1F1B5B18, 0x5F1ED75A, 0xF108FE7F };
    u32 K[4] = { 0 };
    u32 RK[32];

    getRK(MK, K, RK);
    int i = 0;
    for (i; i < 2829; i++) {
        decryptSM4(&enc[i * 4], RK, &Y[i * 4]);
    }

    for (int i = 0; i < 11316; i++) {
        u8* p = (u8*)&Y[i];
        for (int j = 3; j >= 0; j--) {
            putchar(p[j]);
        }
    }
    printf("\n");
    return 0;
}