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在线解密