BUUCTF--CrackRTF--WP

BUUCTF–CrackRTF

⚠注:本文引入了题目的全部伪代码,所以会显得篇幅稍长。

题目链接

题目描述

在互联网时代,兼容就是胜利,兼容就是王道。为了遏制微软一家独大的趋势,小明自诩民族斗士,向微软CEO挑战,CEO给了他一个文件,据说破解后能得到一个微软的多信息文本格式文件。只有得到了才能获得挑战CEO的机会。小明绞尽脑汁,最后不得不求助大家。。。兄弟们该出手时就出手! 注意:得到的 flag 请包上 flag{} 提交

解题步骤

  1. 运行exe文件,交互界面是让我们输入passwd1,初步猜测密码分段处理
  2. 惯例,查壳。发现无壳,32位,丢到ida32中反编译,看字符串发现一堆可疑字符,随便点一个跟进,跟到_main_0函数中

⚠注:不看字符串直接从main函数一步一步跟进也可以

  1. 主函数开头如下,没有特别值得关注的,memset根据经验就是初始化数组,不用过多关注
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;
  1. 出现的信息:
    ①出现交互passwd(1)
    ②用户将第一段密码读入dst中,并且读入的密码为6位
    ③atoi查询后发现为类型转化函数,作用为“将字符串中存储的数字读到int型变量中”,因此dst,即这6位密码可以判定为6个十进制数字
    ④6位数字 ∈ \in (100000,999999),左界为if条件决定,右界为6个十进制数字(③)范围决定
    ⑤dst变成了“xxxxxx@DBApp”
    ⑥对dst做了一些变换,存到了str1中,与6E32…作比较,即enflag1=“6E32…”,搁置sub_40100A,继续向下读
  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);
  sub_40100A((BYTE *)Destination, v3, String1);
  if ( !_strcmpi(String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )
  {
    printf("continue...\n\n");
  1. 出现的信息:
    ①passwd2也为6位字符,不过可能性为所有字符(数字、大小写字母、特殊符号)的6次方
    ②dst被拼接到str中,此时flag=“xxxxxxxxxxxx@DBAPP”,总结一下,flag1是xxxxxx+@DBApp,flag2是xxxxxx,flag=flag2+flag1
    ③对str做了一些变换,存到了str1中,与2701xxx作比较,即enflag=“2701…”,搁置sub_401019,继续向下读
    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);
    sub_401019((BYTE *)Str, v4, String1);
    if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", String1) )
  1. 突然看到sub_40100F也对str做了一些手脚,所以flag2有两个地方比较可疑
    {
      if ( !(unsigned __int8)sub_40100F(Str) )
      {
        printf("Error!!\n");
        ExitProcess(0);
      }
      printf("bye ~~\n");
    }
  }
  return 0;
}
  1. 跟进sub_40100A, 一堆粉呼呼的API,直接给我看傻了,不过看到Hash,能猜到是杂凑函数,直接搜最可疑的CryptCreateHash, 查到0x8004u是SHA1,猜测100A函数可能对flag1进行了SHA1加密,得出的160位特征变成十六进制转化为6E32…
int __cdecl sub_401230(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
  int result; // eax
  DWORD i; // [esp+4Ch] [ebp-28h]
  CHAR String2[4]; // [esp+50h] [ebp-24h] BYREF
  BYTE v6[20]; // [esp+54h] [ebp-20h] BYREF
  DWORD pdwDataLen; // [esp+68h] [ebp-Ch] BYREF
  HCRYPTHASH phHash; // [esp+6Ch] [ebp-8h] BYREF
  HCRYPTPROV phProv; // [esp+70h] [ebp-4h] BYREF

  if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
    return 0;
  if ( CryptCreateHash(phProv, 0x8004u, 0, 0, &phHash) )
  {
    if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
    {
      CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);
      *lpString1 = 0;
      for ( i = 0; i < pdwDataLen; ++i )
      {
        wsprintfA(String2, "%02X", v6[i]);
        lstrcatA(lpString1, String2);
      }
      CryptDestroyHash(phHash);
      CryptReleaseContext(phProv, 0);
      result = 1;
    }
    else
    {
      CryptDestroyHash(phHash);
      CryptReleaseContext(phProv, 0);
      result = 0;
    }
  }
  else
  {
    CryptReleaseContext(phProv, 0);
    result = 0;
  }
  return result;
}
  1. 结合4.④,可以写爆破脚本了,得出flag1=123321@DBApp
import hashlib

string='@DBApp'
for i in range(100000,999999):
    flag=str(i)+string
    x=hashlib.sha1(flag.encode("utf8"))
    y=x.hexdigest()
    if("6e32d0943418c2c33385bc35a1470250dd8923a9" == y):
        print(flag)
        break
  1. 跟进sub_401019,类比100A可知1019函数是MD5,不过没有6位数字的范围,很难爆破,因此继续跟进sub_40100F,读了一下代码,大概知道
    ①从文件“AAA”找出资源x
    ②x与flag进入1005函数做变换
    ③创建一个文档
    ④如果文档的hfile属性符合某个条件,就把这个文档write出来
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;
}
  1. 不知道要做什么,我们继续跟进1005函数,就是将资源x对照flag按字节进行位异或,所以我们知道这个函数改变的是资源x,那么现在要做三件事,第一件事是找到资源x,第二件事是找到异或后的结果,第三件事是异或后的x造成了什么影响
unsigned int __cdecl sub_401420(LPCSTR lpString, int a2, 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;
}
  1. 后续步骤如下
    ①看了别人的博客,知道资源x应该用ResourceHacker[Here](Resource Hacker (angusj.com))(P.S.这个网站居然是http…搞得我不是很敢下,不过其他的资源都是xx软件园,逼得我不得不下…kuso),把文件拖到RH中,看到这样的结果
05 7D 41 15 26 01 6D 53 5D 40 5B 6D 21 2A 31 28 13 00 19 18 00 57 1C 54 54 54 55 03 6E 55 25 22 
2E 20 1E 17 4F 11 00 52 1C 54 54 54 5F 52 5C 56 26 21 70 71 45 42 05 7D 55 0E 2E 44 45 50 5F 48 
6E 57 70 18 24 2C 1F 14 1B 53 5D 3D 26 40 43 43 05 6F 54 52 28 25 30 32 15 04 4F 12 07 41 1C 17 
52 50 6F 14 51 54 1C 63 21 22 2C 57 1B 14 08 1C 3D 3D 3B 49 6F 19 6E 56 25 2A 27 33 11 04 11 53 
13 2C 33 56 45 57 57 5A 46 11 75 6A 76 70 5E 41 4B 0F 02 54 71 05 0A 4F 6F 45 5B 54 37 2F 2B 2F 
14 44 22 54 50 50 1C 40 50 40 57 6F 5E 50 2E 23 70 71 45 42 22 47 03 3D 26 43 03 02 13 75 5E 50 
27 18 39 0F 40 2F 33 11 41 04 1F 76 43 57 56 6C 70 44 27 37 1E 3C 2C 00 1F 53 3E 6B 3D 3D 3B 32 

②回看100F,WriteFile函数里用到了资源x作为形参,也就是说上面的文档被使用了,而CreateFileA函数告诉我们将要创建的函数是rtf后缀,整理一下,资源x与flag异或以后,作为形参传入WriteFile进而创建了一个文件,这个文件与rtf有关。可以猜测,rtf的文件头就是资源x与flag按字节异或后的结果,于是可以写脚本解密flag(从网上扣了一个脚本XD)

rtf = '{\\rtf1' \\需要注意,\r需要转义,变成\\r
A = [0x05, 0x7D, 0x41, 0x15, 0x26, 0x01]
password=''
for i in range(len(rtf)):
    x = ord(rtf[i]) ^ A[i]
    password+=chr(x)
print(password)
  1. 你以为这就完了?NO。我注意到与资源x异或的明明是完整的flag,而并不是单独的与文件头对应的前六位,于是更新一下脚本
s = "{\\rtf1\\ansi\\ansicpg936\\deff0"
a = [0x05,0x7D,0x41,0x15,0x26,0x01,0x6d,0x53,0x5d,0x40,0x5b,0x6d,0x21,0x2a,0x31,0x28,0x13,0x00,0x19,0x18,0x00,0x57,0x1c,0x54,0x54,0x54,0x55,0x03]
flag = ""
for i in range(0,len(s)):
    x = ord(s[i]) ^ a[i]
    flag += chr(x)
print(flag)

结果是~!3a@0123321@DBApp~!3a@01233,很乐,我们发现了作者的偷懒行为😡,AAA文件是一个正常的rtf文件不停的与flag进行xor生成的。
13. ~~ok,那么flag就这样破解了,即~!3a@0123321@DBApp ~~
14. 提交flag以后发现居然不行(,也就是说这不是flag,不过突然想到exe的交互面板就是让我们输入两段密码,并且会生成一个文件,我们又重燃了希望,于是运行文件输入两段密码,生成了一个rtf文件,打开后得到flag:
flag{N0_M0re_Free_Bugs}

第一次写这么长的WP,这道题真的很亏贼,可以算是re新手的第一道门槛,网上的WP大多都是错误的或者不完整的,我并不觉得我的wp就一定完整,但至少执行了一定的逻辑补全计划XD!

总结

  1. 先宏观后微观,整体把握程序做了什么事情,在分析整体中对可疑函数做标注,然后进一步跟进可疑函数
  2. 对可疑函数采取先查后读再猜的顺序,尤其是带有意义的名字的可疑函数,应该先查询它是否为某API,然后边读边猜(如4.③)
  3. 做完题目要对题目中的可疑点多尝试
  4. 多注意形参为指针的函数,这样的函数一般会做变换,一旦做变换就很有可能藏有信息
  5. 判断语句中很有可能也执行了某些函数,最简单的例子(if(a==++i))

一些问题

  1. 题目描述什么鬼啊,“兼容”是啥提示啊?多信息文本格式我搜到是RTF了,但我完全不知道该用到resource hacker啊?这两点是靠积累吗?
  2. 什么是句柄

Writer:SDU x Mini_Hash x Gap

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WP-REST-API 是WordPress 的一种接口,它通过提供标准化的RESTful API,允许开发人员使用HTTP请求来访问和操作WordPress站点的内容和数据。通过这个接口,开发人员可以使用不同的编程语言和技术来与WordPress进行交互,从而使得开发更加灵活和自由。 JWT(JSON Web Token)是一种用于认证和授权的开放标准。它通过将用户信息和权限信息编码成一种加密的令牌,以实现跨服务器和跨域的身份验证。JWT 是由三部分组成的:头部、负载和签名。头部包含令牌的加密算法和类型信息,负载包含用户的相关信息,签名用于验证令牌的真实性和完整性。 WP-REST-API JWT整合了WordPress的REST API和JWT的认证机制,使得在使用WP-REST-API进行开发的过程中,可以增加身份验证和授权的功能。它允许开发人员在请求WordPress REST API时,通过在请求头或参数中提供有效的JWT令牌来验证用户的身份和权限,并根据令牌中的负载信息来进行授权。 WP-REST-API JWT的使用具有很多优势。首先,它提供了一种轻量级的身份验证方式,减少了开发的复杂性。其次,通过JWT令牌的机制,可以实现无状态的认证和授权,提高了性能和可扩展性。此外,JWT还提供了一种可靠的机制来防止伪造和篡改请求数据,增强了系统的安全性。 总而言之,WP-REST-API JWT为开发人员提供了一种方便、灵活和安全的方式来使用WordPress的REST API。它简化了身份验证和授权的过程,并通过使用JWT令牌提高了系统的性能和安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值