BUUCTF REVERSE

rsa

首先要知道rsa加密
附件有两个,其中一个是公钥,得到e=65537,n=8693448229604811919066606200349480058890565601720302561721665405
8378322103517
利用网站http://www.factordb.com/index.php?直接分解N得到p和q,p= 285960468890451637935629440372639283459,q=304008741604601924494328155975272418463
知道了p和q后我们根据L=lcm(p-1,q-1) (L为p-1、q-1的最小公倍数)就可以算出L,有了L和E可以根据1 < D < L,E*D mod L = 1算出D,有了D和N我们就可以根据明文=密文^D mod N来解密出明文了

import gmpy2 
import rsa 
 
e = 65537
n = 86934482296048119190666062003494800588905656017203025617216654058378322103517
p = 285960468890451637935629440372639283459
q = 304008741604601924494328155975272418463

phin = (q-1)*(p-1)

d = gmpy2.invert(e, phin)

key = rsa.PrivateKey(n, e, int(d), p, q)

with open("D:\\CTF\\output\\flag.enc", "rb+") as f:
    f = f.read()
    print(rsa.decrypt(f, key))

注意:解这道题需要额外下载python的插件,但我现在用的是python3.11版本,没有找到对应版本的gmpy插件

level1

附件有两个文件,output中全为数字,level1的文件拖入64位IDA,F5反编译阅读函数命令。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+4h] [rbp-2Ch]
  FILE *stream; // [rsp+8h] [rbp-28h]
  char ptr[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  stream = fopen("flag", "r");//打开文件flag用来读取
  fread(ptr, 1uLL, 0x14uLL, stream);//读取并显示数据
  fclose(stream);//关闭流stream
  for ( i = 1; i <= 19; ++i )//进入for循环
  {
    if ( (i & 1) != 0 )
      printf("%ld\n", (unsigned int)(ptr[i] << i));
    else
      printf("%ld\n", (unsigned int)(i * ptr[i]));
  }
  return 0;
}

exp:

a = [198,232,816,200,1536,300,6144,984,51200,570,92160,1200,565248,756,1474560,800,6291456,1782,65536000]

for i in range(19):
    if ((i+1) & 1):
        print(chr(a[i] >> (i+1)), end="")
    else:
        print (chr(a[i] // (i+1)),end="")

运行结果用flag包上

CrackRTF

拖入32位IDA,F5反编译

printf("pls input the first passwd(1): ");
 scanf("%s", Destination);
 if ( strlen(Destination) != 6 )
 {
   printf("Must be 6 characters!\n");
   ExitProcess(0);
 }

从上述代码可知第一个密码长度为6,atoi 是将字符串转化成整型的函数

v7 = atoi(Destination);
 if ( v7 < 100000 )
   ExitProcess(0);

并且转化为整型的数要大于100000

strcat(Destination, "@DBApp");//将@DBApp连接到密码后面
  v3 = strlen(Destination);//连接之后的长度为12

sub_10400A函数是个加密函数,用来进行哈希加密的

int __cdecl sub_401230(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
  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);// 用于释放从CryptAcquireContext调用返回的句柄      return 1;
    }
    else
    {
      CryptDestroyHash(phHash);
      CryptReleaseContext(phProv, 0);
      return 0;
    }
  }
  else
  {
    CryptReleaseContext(phProv, 0);
    return 0;
  }
}

代码中的0x8004是特殊标识,是sha1算法
用python中的hashlib模块爆破第一部分密码

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

结果为12321@DBApp,即第一部分密码为123321
第二部分密码同样为6位
研究函数sub_40100F

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);
  //这一段代码的含义就是,从AAA文件中查找字符,然后如果没有找到就返回,找到了的话就计算出资源的大小,把资源第一个字符出的指针传给lpBuffer

继续向下看,跟进函数sub_401005

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;
}

一个主逻辑是异或的函数,转化成正常C如下:

unsigned int result; // eax
unsigned int i; // [esp+4Ch] [ebp-Ch]
unsigned int v5; // [esp+54h] [ebp-4h]

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

资源的每一位和密码的每一位循环异或,异或结束之后,生成一个rtf文件
.rtf的文件头,异或之后一定生成的是.rtf的文件头内容,查到.rtf文件头的前六位是”{\rtf1”
知道一个新的软件叫ResourceHacker,用来查看资源
资源前6位
在这里插入图片描述
脚本:

s='{\\rtf1'
a=[0x05,0x7d,0x41,0x15,0x26,0x01]
flag=''
for i in range(6):
    flag+=chr(ord(s[i])^a[i])
print(flag)

运行结果为~!3a@0
输入两部分密码后在文件夹生成了一个RTF文件,打开即开找到flag
在这里插入图片描述
总结:这道题含有大量看不懂的计算加密函数,需要大量资料

HRSRC FindResourceA(
HMODULE hModule,
LPCSTR lpName,
LPCSTR lpType
);
FindResourceA function
Determines the location of a resource with the specified type and name in the specified module.
确定具有指定类型和名称的资源在指定模块中的位置。
hModule:处理包含资源的可执行文件的模块。NULL值则指定模块句柄指向操作系统通常情况下创建最近过程的相关位图文件。
lpName:指定资源名称。
lpType:指定资源类型。
返回值:如果函数运行成功,那么返回值为指向被指定资源信息块的句柄。为了获得这些资源,将这个句柄传递给LoadResource函数。如果函数运行失败,则返回值为NULL。

SizeofResource表示该函数返回指定资源的字节数大小。

LoadResource function
检索一个句柄,该句柄可用于获取指向内存中指定资源的第一个字节的指针。

这三个函数,一个找句柄,一个通过句柄找指针,一个范围查找的资源的大小