美文网首页CTF Re&&Pwn
DragonTeaserCTF2018_Re&&

DragonTeaserCTF2018_Re&&

作者: Kirin_say | 来源:发表于2018-10-04 18:49 被阅读12次

Brutal Oldskull

首先看到程序需要输入4个code和最终flag
首先通过查找字符串定位到关键函数:sub_4017D5()

void sub_4017D5()
{
  char *v0; // eax
  CHAR v1; // [esp+30h] [ebp-498h]
  unsigned int v2; // [esp+130h] [ebp-398h]
  CHAR String[4]; // [esp+134h] [ebp-394h]
  int v4; // [esp+138h] [ebp-390h]
  int v5; // [esp+13Ch] [ebp-38Ch]
  int v6; // [esp+140h] [ebp-388h]
  DWORD ExitCode; // [esp+144h] [ebp-384h]
  CHAR v8; // [esp+148h] [ebp-380h]
  struct _PROCESS_INFORMATION ProcessInformation; // [esp+248h] [ebp-280h]
  struct _STARTUPINFOA StartupInfo; // [esp+258h] [ebp-270h]
  char Buffer[512]; // [esp+29Ch] [ebp-22Ch]
  unsigned __int16 v12; // [esp+49Ch] [ebp-2Ch]
  unsigned __int16 v13; // [esp+49Eh] [ebp-2Ah]
  unsigned __int16 v14; // [esp+4A0h] [ebp-28h]
  unsigned __int16 v15; // [esp+4A2h] [ebp-26h]
  BOOL v16; // [esp+4A4h] [ebp-24h]
  FILE *v17; // [esp+4A8h] [ebp-20h]
  void *v18; // [esp+4ACh] [ebp-1Ch]
  void *v19; // [esp+4B0h] [ebp-18h]
  void *v20; // [esp+4B4h] [ebp-14h]
  void *v21; // [esp+4B8h] [ebp-10h]
  int i; // [esp+4BCh] [ebp-Ch]

  sub_4016C2("Processing...");
  for ( i = 0; i <= 3; ++i )
  {
    *(_DWORD *)String = 0;
    v4 = 0;
    v5 = 0;
    v6 = 0;
    GetWindowTextA(*(&dword_40C034 + i), String, 8);
    if ( sscanf(String, "%x", &v2) != 1 )
    {
      sprintf(&v1, "Incorrect Code %i format (16-bit HEX)", i + 1);
      sub_4016C2(&v1);
      return;
    }
    if ( v2 > 0xFFFF )
    {
      sub_4016C2("Incorrect Code.");
      return;
    }
    *(&v12 + i) = v2;
  }
  v21 = sub_4016E4((int)&unk_405020, 0x4C8Eu, v12);
  if ( v21 )
  {
    v20 = sub_4016E4((int)v21, 0x4C6Eu, v13);
    if ( v20 )
    {
      v19 = sub_4016E4((int)v20, 0x4C4Eu, v14);
      if ( v19 )
      {
        v18 = sub_4016E4((int)v19, 0x4C2Eu, v15);
        if ( v18 )
        {
          free(v21);
          free(v20);
          free(v19);
          memset(Buffer, 0, sizeof(Buffer));
          GetTempPathA(0x100u, Buffer);
          v0 = &Buffer[strlen(Buffer)];
          *(_DWORD *)v0 = 'dlo\\';
          *((_DWORD *)v0 + 1) = 'luks';
          *((_DWORD *)v0 + 2) = 'hc_l';
          *((_DWORD *)v0 + 3) = 'ekce';
          *((_DWORD *)v0 + 4) = 'xe.r';
          *((_WORD *)v0 + 10) = 'e';
          v17 = fopen(Buffer, "wb");
          if ( v17 )
          {
            fwrite(v18, 1u, 0x4C0Eu, v17);
            fclose(v17);
            free(v18);
            memset(&StartupInfo, 0, sizeof(StartupInfo));
            StartupInfo.cb = 68;
            ProcessInformation.hProcess = 0;
            ProcessInformation.hThread = 0;
            ProcessInformation.dwProcessId = 0;
            ProcessInformation.dwThreadId = 0;
            memset(&v8, 0, 0x100u);
            GetWindowTextA(dword_40C044, &v8, 64);
            *(_WORD *)&Buffer[strlen(Buffer)] = 32;
            strcat(Buffer, &v8);
            v16 = CreateProcessA(0, Buffer, 0, 0, 0, 0x8000000u, 0, 0, &StartupInfo, &ProcessInformation);
            if ( v16 )
            {
              if ( WaitForSingleObject(ProcessInformation.hProcess, 0x7D0u) != 0 )
              {
                TerminateProcess(ProcessInformation.hProcess, 9u);
                CloseHandle(ProcessInformation.hProcess);
                CloseHandle(ProcessInformation.hThread);
                sub_4016C2("Checker crashed. Sorry.");
              }
              else
              {
                ExitCode = -1;
                v16 = GetExitCodeProcess(ProcessInformation.hProcess, &ExitCode);
                if ( v16 )
                {
                  if ( ExitCode )
                    sub_4016C2("Wrong Flag.");
                  else
                    sub_4016C2("Well Done! But you know that :)");
                  CloseHandle(ProcessInformation.hProcess);
                  CloseHandle(ProcessInformation.hThread);
                }
                else
                {
                  CloseHandle(ProcessInformation.hProcess);
                  CloseHandle(ProcessInformation.hThread);
                  sub_4016C2("Checker failed. Sorry.");
                }
              }
            }
            else
            {
              sub_4016C2("Couldn't spawn checker");
            }
          }
          else
          {
            sub_4016C2("Couldn't write the checker file.");
            free(v18);
          }
        }
        else
        {
          sub_4016C2("Wrong Code 4.");
          free(v21);
          free(v20);
          free(v19);
        }
      }
      else
      {
        sub_4016C2("Wrong Code 3.");
        free(v21);
        free(v20);
      }
    }
    else
    {
      sub_4016C2("Wrong Code 2.");
      free(v21);
    }
  }
  else
  {
    sub_4016C2("Wrong Code 1.");
  }
}

首先注意到:

.text:00401A7A mov     dword ptr [eax], 'dlo\'
.text:00401A80 mov     dword ptr [eax+4], 'luks'
.text:00401A87 mov     dword ptr [eax+8], 'hc_l'
.text:00401A8E mov     dword ptr [eax+0Ch], 'ekce'
.text:00401A95 mov     dword ptr [eax+10h], 'xe.r'
.text:00401A9C mov     word ptr [eax+14h], 'e'
.text:00401AA2 mov     dword ptr [esp+4], offset aWb   ; "wb"
.text:00401AAA lea     eax, [ebp+Buffer]
.text:00401AB0 mov     [esp], eax                      ; char *
.text:00401AB3 call    fopen

程序在此处创建了可执行文件oldskull_checker.exe
同时分析关键的判断函数sub_4016E4

void *__cdecl sub_4016E4(int encode_data, size_t size, int input_code)
{
  __int16 _input_code; // [esp+1Ch] [ebp-3Ch]
  int v5; // [esp+20h] [ebp-38h]
  void *decode_data; // [esp+48h] [ebp-10h]
  size_t i; // [esp+4Ch] [ebp-Ch]

  _input_code = input_code;
  decode_data = malloc(size);
  memset(decode_data, 0, size);
  for ( i = 0; i < size; ++i )
  {
    *((_BYTE *)decode_data + i) = (*(_BYTE *)(encode_data + i) ^ _input_code) - HIBYTE(_input_code);
    _input_code *= 0x62F3;
  }
  memset(&v5, 0, 0x28u);
  md5_func(decode_data, size - 32, (int)&v5);
  if ( !memcmp(&v5, (char *)decode_data + size - 32, 0x20u) )
    return decode_data;
  free(decode_data);
  return 0;
}

通过md5_func中调用的sub_403579函数中预定义的数组:

  a1[2] = 0x67452301;
  a1[3] = 0xEFCDAB89;
  a1[4] = 0x98BADCFE;
  a1[5] = 0x10325476;

判断这里是标准的md5 function
可以看到sub_4016E4函数的流程:

传入预先在data段定义的加密过的长为size的encode_data
利用我们输入的input_code对其进行解密
而后对解密后的decode_data前部分decode_data[:-32]进行md5加密
加密后产生的hash字符串存到栈中
而后将栈中hash字符串与decode_data[-32:]比较
以此进行四次循环对预定义数据连续四次解密
我们需要使得每次data经input_code解密后:
md5(decode_data[:-32])=decode_data[-32:]

PS:看到md5_func中:
int __cdecl md5_func(void *a1, size_t a2, int a3)
{
  int result; // eax
  unsigned __int8 v4[16]; // [esp+14h] [ebp-B4h]
  int v5; // [esp+24h] [ebp-A4h]
  int i; // [esp+BCh] [ebp-Ch]

  sub_403579(&v5);
  sub_4035BA((int)&v5, a1, a2);
  result = sub_4036F1((int)v4, &v5);
  for ( i = 0; i <= 15; ++i )
    result = sprintf((char *)(2 * i + a3), "%.2x", v4[i]);
  return result;
}
最后存储形式为%.2x,即32位md5值,每一位转ascii,最终32位以string形式存储(类似在内存中进行一次hex编码,"\x11"->"\x31\x31")

若四个code正确,预先定义的data段中的encode_data便经四次正确解密后写入oldskull_checker.exe中,而后程序调用oldskull_checker.exe判断最终flag
由此我们需要先获得4个正确code:
这里可以通过爆破获得:

import hashlib

file=open(r"","rb")
file.seek(0x4020)
str_data=file.read(0x4c8e)
data=[]
for i in str_data:
    data.append(ord(i))
key1=[0]*0x4c8e
ans=""
for i in range(0x10000):
    l=i
    print i
    for j in range(0x4c8e):
        key1[j]=(data[j]^(l&0x00ff))-(l>>8)
        l*=0x62f3
        l=l&0xffff
        key1[j]=(key1[j]+0x100 if key1[j]<0 else key1[j])
    ans="".join(chr(k) for k in key1)
    hash=hashlib.md5(ans[:-32]).hexdigest()
    if hash==ans[-32:]:
        print hex(i)
        break
file.close()
//此处爆破code1,爆破后更新data而以此类推爆破出code2、3、4

虽然这里也很快,不过感觉可以优化一下
四次解密每次产生的最后32字节每一位都在16个字符"0123456789abcdef"中(md5->%.2x->16进制)
所以我们可以只解密后面的字节,判断是否为md5的hash字符串,以此缩小范围,不过这里比较简单,判断后每次只有一个解,而后利用此解爆破一下中间不断累乘的数值即可,最终几秒即可得出4个code:

def check(l):
    for i in l:
        if chr(i) not in key:
            return 0
    return 1

file=open(r"","rb")
file.seek(0x4020)
key="0123456789abcdef"
str_data=file.read(0x4c8e)
data=[]
for i in str_data:
    data.append(ord(i))
key1=[0]*0x4c8e
ans=[]
final_ans=[]
for n in range(4):
    for i in range(0x10000):
        l=i
        for j in range(32*(4-n)):
            key1[0x4c8e-32*4+j]=(data[0x4c8e-32*4+j]^(l&0x00ff))-(l>>8)
            l*=0x62f3
            l=l&0xffff
            key1[0x4c8e-32*4+j]=(key1[0x4c8e-32*4+j]+0x100 if key1[0x4c8e-32*4+j]<0 else key1[0x4c8e-32*4+j])
        if check(key1[0x4c8e-32*(n+1):0x4c8e-32*n])==1:
                ans.append(i)
                for q in range(32*4):
                    data[0x4c8e-32*4+q]=key1[0x4c8e-32*4+q]
                break
for n in range(4):
    for i in range(0x10000):
        if (pow(0x62f3,0x4c8e-32*4,0x10000)*i)&0xffff==ans[n]:
            final_ans.append(str(n+1)+"->"+hex(i))
print final_ans
file.close()

得到4个code后
动态调试时通过fopen的文件名参数找到程序写入磁盘的最终check flag的oldskull_checker.exe
ida载入后看到oldskull_checker.exe中的main:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  const char *v4; // [esp+18h] [ebp-8h]
  unsigned int i; // [esp+1Ch] [ebp-4h]

  sub_402B90();
  if ( argc != 2 )
    return 1;
  v4 = argv[1];
  if ( strlen(argv[1]) != 20 )
    return 2;
  for ( i = 0; i <= 0x13; ++i )
  {
    if ( v4[i] != (byte_404008[i] ^ 0x8F) )
      return 3;
  }
  return 0;
}

很简单的异或加密,提取出数据再异或0x8F即得最终flag:

key=[0xCB, 0xFD, 0xE8, 0xE1, 0xDC, 0xF4, 0xD8, 0xEE, 0xEE, 0xEE,0xF6, 0xDB, 0xE0, 0xE0, 0xCA, 0xD5, 0xAE, 0xAE, 0xBE, 0xF2]
flag=""
for i in key:
    flag+=chr(i^0x8f)
print flag

(未完,暂时有事)

相关文章

网友评论

    本文标题:DragonTeaserCTF2018_Re&&

    本文链接:https://www.haomeiwen.com/subject/ntmtaftx.html