# NSSCTF Round#18 Basic Writeup

# 前言

唉,好菜,赛时只做出来一题,看 ezcrypto 看了一下午结束后才做出来,还是太菜了😭

# Rev

# ezcrypt

解包,pycdc 反编译 NSSCTF.pyc ,可以看出来是 Blowfish 加密,缺个 key,在 crypto.pyc 发现了加密逻辑,一个魔改的 tea,还有字符串每 4 位一组逆序

#include <stdio.h>
#include <stdint.h>
void decrypt(uint32_t *v, uint32_t *k)
{
    uint32_t v0 = v[0], v1 = v[1], i;
    uint32_t delta = 0x9e3779b9;
    uint32_t sum = delta * 32;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
    for (i = 0; i < 32; i++)
    {
        v1 -= ((v0 << 3) + k2) ^ (v0 + sum) ^ ((v0 >> 4) + k3) ^ 2310;
        v0 -= ((v1 << 3) + k0) ^ (v1 + sum) ^ ((v1 >> 4) + k1) ^ 596;
        sum -= delta;
    }
    v[0] = v0; v[1] = v1;
}
void reverseGroups(char *str, int groupSize) {
    int len = strlen(str);
    for (int i = 0; i < len; i += groupSize) {
        int start = i;
        int end = i + groupSize - 1;
        while (start < end) {
            char temp = str[start];
            str[start] = str[end];
            str[end] = temp;
            start++;
            end--;
        }
    }
}
int main()
{
    uint32_t data[] = {1396533857, 0xCC8AE275L, 0x89FB8A63L, 940694833};
    uint32_t key[] = {17, 34, 51, 68};
    decrypt(data, key);
    decrypt(data + 2, key);
    reverseGroups(data, 4);
    printf("%s", data);
}

解密得到 key: EzNssRevProjects ,转为十六进制字符串,然后去解 blowfish

from Crypto.Cipher import Blowfish
def decrypt_file(input_path, output_path, key):
    cipher = Blowfish.new(key, Blowfish.MODE_CBC)
    with open(input_path, 'rb') as file:
        encrypted_data = file.read()
    plaintext = cipher.decrypt(encrypted_data)
    print(cipher.iv.hex())
    with open(output_path, 'wb') as file:
        file.write(plaintext)
decrypt_file(r"\bin\NSSCTF.exe_extracted\output", r"bin\output_dec", bytearray.fromhex("457a4e737352657650726f6a65637473"))

解密后的文件去掉头部 8 个字节,可以发现是一个 elf 文件,ida 打开发现一个 vm

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int a2[8]; // [rsp+10h] [rbp-50h] BYREF
  unsigned __int64 a1[3]; // [rsp+30h] [rbp-30h] BYREF
  int v6; // [rsp+48h] [rbp-18h]
  __int16 v7; // [rsp+4Ch] [rbp-14h]
  unsigned __int64 v8; // [rsp+58h] [rbp-8h]
  v8 = __readfsqword(0x28u);
  a2[0] = 0x23;
  a2[1] = 0x12;
  a2[2] = 0x45;
  a2[3] = 0x56;
  a2[4] = 0x67;
  a1[0] = 0xB537982DAB0F8D37LL;
  a1[1] = 0xB81BED0CDA30A648LL;
  a1[2] = 0xB6ED81179824E900LL;
  v6 = 0xDE218C26;
  v7 = 0xDE04;
  vm((unsigned __int8 *)a1, (BYTE *)a2, 30);
  return 0;
}
void __fastcall vm(unsigned __int8 *a1, BYTE *a2, int a3)
{
  int i; // [rsp+1Ch] [rbp-8h]
  int v4; // [rsp+20h] [rbp-4h]
  for ( i = 0; i < a3; ++i )
  {
    v4 = i % 4;
    if ( i % 4 == 3 )
    {
      a1[i] += a2[8] + *a2;
    }
    else if ( v4 <= 3 )
    {
      if ( v4 == 2 )
      {
        a1[i] -= a2[4] ^ a2[12];
      }
      else if ( v4 <= 2 )
      {
        if ( v4 )
        {
          if ( v4 == 1 )
            a1[i] ^= *a2 - (unsigned __int8)*((_DWORD *)a2 + 2);
        }
        else
        {
          a1[i] ^= a2[16] - (unsigned __int8)*((_DWORD *)a2 + 1);
        }
      }
    }
  }
}

对着逻辑解就好了

data = bytearray.fromhex(
    "378D0FAB2D9837B548A630DA0CED1BB800E924981781EDB6268C21DE04DE")
key = [0x23, 0x12, 0x45, 0x56, 0x67]
for i in range(30):
    n = i % 4
    if (n == 0):
        data[i] = (data[i] ^ (key[4] + key[1])) & 0xff
    if (n == 1):
        data[i] = (data[i] ^ (key[0] - key[2])) & 0xff
    if (n == 2):
        data[i] = (data[i] + (key[3] ^ key[1])) & 0xff
    if (n == 3):
        data[i] = (data[i] - (key[2] + key[0])) & 0xff
for i in data:
    print(chr(i), end='')

NSSCTF{M1xtru3_Py7h0n_1N_Rev}

# EzHook

分析程序流程,在 main 函数执行之前 Hook 了 MessageBoxA

__int64 InstallHook()
{
  __int64 result; // rax
  result = HOOK(MyHook);
  Hook_MessageBoxA = result;
  return result;
}

然后走到 main 函数里输入,然后调用了一个全是异或的函数,然后外面又异或了一下,最后 check 那管你对不对都会进入 MyHook

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char fake[40]; // [rsp+20h] [rbp+0h] BYREF
  char xor_key[8]; // [rsp+48h] [rbp+28h]
  char input[40]; // [rsp+50h] [rbp+30h] BYREF
  int v7; // [rsp+78h] [rbp+58h]
  int i; // [rsp+7Ch] [rbp+5Ch]
  __int64 v9; // [rsp+C0h] [rbp+A0h]
  fake[0] = 4;
  fake[1] = 90;
  fake[2] = 6;
  fake[3] = 13;
  fake[4] = 17;
  fake[5] = -73;
  fake[6] = 15;
  fake[7] = 91;
  fake[8] = 10;
  fake[9] = 68;
  fake[10] = 68;
  fake[11] = -36;
  fake[12] = 49;
  fake[13] = 6;
  fake[14] = 21;
  fake[15] = 24;
  fake[16] = 19;
  fake[17] = -83;
  fake[18] = 43;
  fake[19] = 66;
  fake[20] = 56;
  fake[21] = 91;
  fake[22] = 25;
  fake[23] = -83;
  fake[24] = 44;
  fake[25] = 89;
  fake[26] = 19;
  fake[27] = 53;
  fake[28] = 56;
  fake[29] = -101;
  fake[30] = 5;
  fake[31] = 94;
  fake[32] = 19;
  fake[33] = 53;
  fake[34] = 12;
  fake[35] = -98;
  fake[36] = 35;
  fake[37] = 81;
  fake[38] = 70;
  fake[39] = 23;
  xor_key[0] = 0x11;
  xor_key[1] = 0x45;
  xor_key[2] = 0x14;
  xor_key[3] = 0x19;
  xor_key[4] = 0x19;
  xor_key[5] = 0x81;
  memset(input, 0, sizeof(input));
  v7 = 0;
  printf("Plz input your flag:\n", argv, envp);
  scanf("%s", input);
  xorrrrrr(input);
  for ( i = 0; i < 40; ++i )
  {
    v9 = i;
    input[i] ^= xor_key[i % 6];
  }
  if ( !memcmp(input, fake, v7) )
    MessageBoxA(0i64, input, "Right?", 0);
  else
    MessageBoxA(0i64, input, "Wrong?", 0);
  return 0;
}

这里又是一个异或,最后一个 xxtea 加密

__int64 __fastcall MyHook(__int64 handle, char *input, __int64 title, unsigned int a4)
{
  char v6[5]; // [rsp+28h] [rbp+8h] BYREF
  char v7[8]; // [rsp+2Dh] [rbp+Dh] BYREF
  char key[16]; // [rsp+38h] [rbp+18h] BYREF
  char enc[40]; // [rsp+48h] [rbp+28h] BYREF
  char xor_key[8]; // [rsp+70h] [rbp+50h]
  int i; // [rsp+78h] [rbp+58h]
  int j; // [rsp+7Ch] [rbp+5Ch]
  __int64 v13; // [rsp+C0h] [rbp+A0h]
  v6[0] = 52;
  v6[1] = 15;
  v6[2] = 1;
  v6[3] = 14;
  v6[4] = 18;
  strcpy(v7, "GGG");
  qmemcpy(key, "He1l0NsS!", 9);
  key[9] = 17;
  key[10] = 69;
  key[11] = 20;
  key[12] = 25;
  key[13] = 25;
  key[14] = -127;
  key[15] = 0;
  enc[0] = -28;
  enc[1] = -25;
  enc[2] = -2;
  enc[3] = -29;
  enc[4] = 23;
  enc[5] = 28;
  enc[6] = -34;
  enc[7] = 50;
  enc[8] = -26;
  enc[9] = -72;
  enc[10] = 104;
  enc[11] = 64;
  enc[12] = 64;
  enc[13] = -40;
  enc[14] = 114;
  enc[15] = -6;
  enc[16] = -120;
  enc[17] = 20;
  enc[18] = -31;
  enc[19] = -123;
  enc[20] = -51;
  enc[21] = -127;
  enc[22] = -86;
  enc[23] = -34;
  enc[24] = 29;
  enc[25] = -24;
  enc[26] = -110;
  enc[27] = 65;
  enc[28] = -72;
  enc[29] = 30;
  enc[30] = 94;
  enc[31] = -49;
  enc[32] = -50;
  enc[33] = 73;
  enc[34] = 39;
  enc[35] = 34;
  enc[36] = 57;
  enc[37] = 125;
  enc[38] = 80;
  enc[39] = -38;
  xor_key[0] = 17;
  xor_key[1] = 69;
  xor_key[2] = 20;
  xor_key[3] = 25;
  xor_key[4] = 25;
  xor_key[5] = 0x81;
  for ( i = 0; i < 40; ++i )
  {
    v13 = i;
    input[i] ^= xor_key[i % 6];
    input[i] ^= 0x73u;
  }
  xxtea_encrypt(input, 40, key);
  if ( !strncmp(input, enc, 0x28ui64) )
  {
    for ( j = 0; j < 8; ++j )
      v6[j] ^= 0x66u;
    xxtea_decrypt(input, 40, key);
    Hook_MessageBoxA(handle, input, v6, a4);    // Right!!!
  }
  else
  {
    xxtea_decrypt(input, 40, key);
    Hook_MessageBoxA(handle, input, title, a4);
  }
  return 0i64;
}

总结一下流程就是 input->一坨异或->异或->再异或->xxtea加密 ,但是通过调试可以发现,前面那一堆异或最后又还原了,所以最后就只剩下一个 xxtea
然而善良的出题人直接把 xxtea_decrypt 给我们了,直接改 rip 飞过去,修改 inputenc ,然后运行就得到 flag 了哈哈
NSSCTF{C0ngr@tulat1ons!H0Ok_bY_1t_s3lf!}