Back

GWCTF2019 re3 writeup

下午在上课的时候写了个 AES 加密,晚上正好遇到,立马认出来了,于是赶紧把解密部分写完,用自己的代码跑出来了,爽到。

静态分析

首先通过关键字符串 Wrong 进入 main 函数

void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
  int i; // [rsp+8h] [rbp-48h]
  char s[40]; // [rsp+20h] [rbp-30h] BYREF
  unsigned __int64 v5; // [rsp+48h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  __isoc99_scanf("%39s", s);
  if ( (unsigned int)strlen(s) != 32 )
  {
    puts("Wrong!");
    exit(0);
  }
  mprotect(&dword_400000, 0xF000uLL, 7);
  for ( i = 0; i <= 223; ++i )
    *((_BYTE *)sub_402219 + i) ^= 0x99u;
  sub_40207B((__int64)&unk_603170);
  sub_402219();
}

阅读一下代码,发现先是输入一个长度为32的字符串

随后对一个奇怪的地址执行了一个函数,但这个地址和输入应该没有关系,可以暂时不管

注意到有个 for 循环对某一个地址的值做了个异或,最后又调用了这个地址,这是简单且经典的 SMC

用 idapython patch 一下,用运行脚本功能,由于长度较短,直接在ida中手写了

start = 0x402219
end = start + 223
for i in range(start, end + 1):
    patch_byte(i, get_wide_byte(i) ^ 0x99)

运行完后进入这个地址,好像不能在 402219 处新建函数,只能在 40221A 处按 p 键新建函数,F5 查看这个函数

__int64 __fastcall sub_40221A(__int64 a1)
{
  unsigned int v2; // [rsp+18h] [rbp-D8h]
  int i; // [rsp+1Ch] [rbp-D4h]
  char v4[200]; // [rsp+20h] [rbp-D0h] BYREF
  unsigned __int64 v5; // [rsp+E8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  sub_400A71((__int64)v4, (__int64)&unk_603170);// 经典密钥生成
  sub_40196E((__int64)v4, a1);                  // 这tm是AES
  sub_40196E((__int64)v4, a1 + 16);
  v2 = 1;
  for ( i = 0; i <= 31; ++i )
  {
    if ( *(_BYTE *)(i + a1) != byte_6030A0[i] )
      v2 = 0;
  }
  return v2;
}

最后的 byte_6030A0 显然是加密结果的校验了

上面有两个函数 sub_400A71sub_40196E400A71 调用了之前出现的 603170 猜测是一个密钥,40196E 使用了两次,且分别是对输入的前一半与后一半的操作,猜测是加密函数。密钥生成和输入无关,完全可以通过动态调试得到结果,所以查看加密函数。

__int64 __fastcall sub_401828(__int64 a1, __int64 a2)
{
  unsigned __int8 i; // [rsp+1Fh] [rbp-1h]

  sub_400B0A(0LL, a1, a2);                      // 这是aes哦
  for ( i = 1; i <= 9u; ++i )
  {
    sub_400BAC(a1);
    sub_400C1F(a1);
    sub_400D27(a1);
    sub_400B0A(i, a1, a2);
  }
  sub_400BAC(a1);
  sub_400C1F(a1);
  return sub_400B0A(10LL, a1, a2);
}

一开始被各个函数吓一跳,但点到某个函数里发现好像是 $4\times 4$ 的一个方阵,又发现某些函数重复出现的规则和 AES 很像,于是推测这个是 AES 加密算法,稍微看了下具体的函数实现,应该没有大问题,于是着手解密了。

解密

首先动态调试拿到密钥

直接步过这句代码: sub_40207B((__int64)&unk_603170);

gdb-peda$ b *0x4021e4
Breakpoint 2 at 0x4021e4
gdb-peda$ c
gdb-peda$ n
gdb-peda$ x/50x 0x603170
0x603170:       0xcb    0x8d    0x49    0x35    0x21    0xb4    0x7a    0x4c
0x603178:       0xc1    0xae    0x7e    0x62    0x22    0x92    0x66    0xce
0x603180:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00

拿到密钥

随后直接到 ida 相应地址中取出密文

用之前写过的 AES 脚本解密

import my_aes
from libnum import n2s

key     = 0xcb8d493521b47a4cc1ae7e62229266ce
cipher1 = 0xBC0AADC0147C5ECCE0B140BC9C51D52B
cipher2 = 0x46B2B9434DE5324BAD7FB4B39CDB4B5B

print(n2s(my_aes.aes_encrypt(cipher1, key)), end = '')
print(n2s(my_aes.aes_encrypt(cipher2, key)))

输出如下:

b'flag{924a9ab2163'b'd390410d0a1f670}'

得到 flag

Built with Hugo
Theme Stack designed by Jimmy