BUUCTF-RE-CrackRTF

这道题很有难度,有些地方还是一知半解,看了很多别人的题解,在这里留下记录
首先我们对程序进行逆向分析,我们可以很容易的得到逆向逻辑

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  DWORD v3; // eax
  DWORD v4; // eax
  char Str[260]; // [esp+4Ch] [ebp-310h] BYREF
  int v7; // [esp+150h] [ebp-20Ch]
  char String1[260]; // [esp+154h] [ebp-208h] BYREF
  char Destination[260]; // [esp+258h] [ebp-104h] BYREF

  memset(Destination, 0, sizeof(Destination));
  memset(String1, 0, sizeof(String1));
  v7 = 0;
  printf("pls input the first passwd(1): ");
  scanf("%s", Destination);
  if ( strlen(Destination) != 6 )
  {
    printf("Must be 6 characters!\n");
    ExitProcess(0);
  }
  v7 = atoi(Destination);
  if ( v7 < 100000 )
    ExitProcess(0);
  strcat(Destination, "@DBApp");
  v3 = strlen(Destination);
  crypto1((BYTE *)Destination, v3, String1);
  if ( !_strcmpi(String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )
  {
    printf("continue...\n\n");
    printf("pls input the first passwd(2): ");
    memset(Str, 0, sizeof(Str));
    scanf("%s", Str);
    if ( strlen(Str) != 6 )
    {
      printf("Must be 6 characters!\n");
      ExitProcess(0);
    }
    strcat(Str, Destination);
    memset(String1, 0, sizeof(String1));
    v4 = strlen(Str);
    crypto2((BYTE *)Str, v4, String1);
    if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", String1) )
    {
      if ( !(unsigned __int8)sub_40100F(Str) )
      {
        printf("Error!!\n");
        ExitProcess(0);
      }
      printf("bye ~~\n");
    }
  }
  return 0;
}

这里我们可以看到我们要进行两次输入,然后分别进行两次判断
首先我们看第一部分,我们需要输入一个长度为6的字符串,然后使用atoi将其转换为整数形式,且其要大于100000,也就是说,前面的数字一定是一个10000~999999的范围的数字,然后使用strcat函数在后面加上"@DBApp",然后对其进行加密得到加密后的数据6E32D0943418C2C33385BC35A1470250DD8923A9,我们来看看加密的程序时是情况,我们进入加密函数发现啥也看不懂,实际上这是一个hash加密的API接口我们可以看到特征码0x8004,这说明它是SHA-1加密,我们后面返回的数值刚好长度是40,说明我们的判断是正确的,我们可以爆破出正确的密码:

import hashlib
flag2 = "@DBApp"
for i in range(100000,999999):
    h2 = hashlib.sha1((str(i)+flag2).encode())
    flags = h2.hexdigest()
    if "6e32d0943418c2c33385bc35a1470250dd8923a9" == flags:
            print (str(i)+flag2)
            print (flags)

我们解的完整的密码是123321@DBApp,接下来我们可以进入第二部分进行进一步的加密,我们看到仍然是一个六位密码的输入,然后将123321@QBApp接在后面,进行MD5加密(这是因为其特征码为0x8003),但是这次并没有给出我们密码的大小范围,所以爆破是不太可能的。我们需要另寻其他的方法,接下来的内容参考一位师傅的帖子
BUUCTF中CrackRTF题详细解法 - 吾爱破解 - 52pojie.cn
简单来说就是我们去看判断函数的内容:

char __cdecl sub_4014D0(LPCSTR lpString)
{
  LPCVOID lpBuffer; // [esp+50h] [ebp-1Ch]
  DWORD NumberOfBytesWritten; // [esp+58h] [ebp-14h] BYREF
  DWORD nNumberOfBytesToWrite; // [esp+5Ch] [ebp-10h]
  HGLOBAL hResData; // [esp+60h] [ebp-Ch]
  HRSRC hResInfo; // [esp+64h] [ebp-8h]
  HANDLE hFile; // [esp+68h] [ebp-4h]

  hFile = 0;
  hResData = 0;
  nNumberOfBytesToWrite = 0;
  NumberOfBytesWritten = 0;
  hResInfo = FindResourceA(0, (LPCSTR)0x65, "AAA");
  if ( !hResInfo )
    return 0;
  nNumberOfBytesToWrite = SizeofResource(0, hResInfo);
  hResData = LoadResource(0, hResInfo);
  if ( !hResData )
    return 0;
  lpBuffer = LockResource(hResData);
  sub_401005(lpString, (int)lpBuffer, nNumberOfBytesToWrite);
  hFile = CreateFileA("dbapp.rtf", 0x10000000u, 0, 0, 2u, 0x80u, 0);
  if ( hFile == (HANDLE)-1 )
    return 0;
  if ( !WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0) )
    return 0;
  CloseHandle(hFile);
  return 1;
}

大概的意思就是程序内有一个叫AAA的资源,这段程序会将其的内容提取出来并进行处理,其处理逻辑如下:

sub_401005(lpString, (int)lpBuffer, nNumberOfBytesToWrite);
unsigned int __cdecl sub_401420(LPCSTR lpString, int a2, unsigned int a3)
{
  unsigned int result; // eax
  unsigned int i; // [esp+4Ch] [ebp-Ch]
  unsigned int v5; // [esp+54h] [ebp-4h]

  v5 = lstrlenA(lpString);
  for ( i = 0; ; ++i )
  {
    result = i;
    if ( i >= a3 )
      break;
    *(_BYTE *)(i + a2) ^= lpString[i % v5];
  }
  return result;
}

我们先用ResourcehHacker获取程序中的资源可以看到被加密的文件AAA

我们观察上面的程序加密是根据lpstring的前六位进行的,也就是说我们需要知道lpstring的前六位是啥,找个rtf文件看看前六位是啥

是"{\rtf1",然后我们可以写出解密脚本:

c = [0x05,0x7D,0x41,0x15,0x26,0x01]
key = "{\\rtf1"
pwd = ""
for i in range(len(c)):
    pwd += chr(c[i]^ord(key[i]))
print(pwd)

我们拿到第二部分的密码~!3a@0,也就是说完整的密码是~!3a@0123321@DBApp,打开程序将我们的密码输入,会生成一个rtf文件,里面放了我们的flag

实际上,还有一种特别快捷的方法,既然我们已知MD5值,所以我们可以直接进行MD5的解密直接得到我们的密码:

下面放个链接MD5在线解密

posted @ 2025-02-23 22:57  Ylin07  阅读(30)  评论(0)    收藏  举报