main函数校验flag长度是否为16位,若是则依次对flag加密后,而后若flag正确则输出常量"Bingo!"(此处有多解)
if ( v6 > 0x10 ) // flag.len == 16
{
puts("input is too long!");
}
else if ( v6 == 16 )
{
v7 = sub_55A527C5BC00((unsigned __int64)&flag, 16, &ptr);// v7 == 8,ptr为flag 变换后的八个字符
if ( v7
&& (v8 = sub_55A527C5C180(ptr, v7, (__int64)&::a3, 16, &dest), (v9 = (char *)v8) != 0LL)
&& dest > 0
&& (unsigned __int16)sub_55A527C5C3D0((__int64)v8, dest) == 0x69E2 )
{
for ( i = 0LL; dest > (signed int)i; ++i )
v9[i] ^= 0x17u;
puts(v9);
if ( ptr )
free(ptr);
free(v9);
}
else
{
puts("input flag is wrong!");
}
}
else
{
puts("input is too short!");
}
第一个函数很简单,主要是对flag通过置换表进行置换,如 ’ab' 则 置换为 0xab
由于小端序,所以如果我们输入是'1234567890123456'(128bits)那么经过置换后变成一个64位的整数 0x5634129078563412(64bits)(由于置换表的缘故此处也存在多解,譬如 'f' 'F' '~' 均被置换为0xF)
第二个函数是对flag置换后形成的64位整数进行32轮的轮换加密形成一个新的64位整数X ,然后校验高8位是否小于4,并将BYTE(BYTE7(X))(X)置0
轮换加密函数很长,但是由于a2 < -1且 v27 <= 2 所以只需分析那一小部分即可
if ( a2 < -1 )
{
v24 = -a2;
v25 = 0x9E3779B9 * (52 / v24 + 6); // v24 == 2
if ( v25 )
{
srch = &src[v24 - 1];
v27 = ~v4;
v44 = &src[~v4];
v28 = ~v4 - 2 - ((~v4 - 3) & 0xFFFFFFFE);
do
{
v29 = v25 >> 2;
if ( v27 <= 2 ) // true
{
v31 = v27;
}
else // false
{
略
}
srcl_ = &src[v31];
do
{
srcl = *(srcl_ - 1);
--srcl_;
srch_next = srcl_[1]
- (((srch_next ^ v25)
+ (srcl ^ *(_DWORD *)(tables + 4LL * (((unsigned __int8)v29 ^ (unsigned __int8)v31) & 3)))) ^ (((srch_next >> 3) ^ 16 * srcl) + ((srcl >> 5) ^ 4 * srch_next)));
srcl_[1] = srch_next; // 后八位0x56341290替换为 0x45d10f13 小端
--v31;
}
while ( v31 );
srcl_next = *src
- (((((unsigned int)*srch >> 5) ^ 4 * srch_next) + (16 * *srch ^ (srch_next >> 3))) ^ ((*(_DWORD *)(tables + 4LL * (v29 & 3)) ^ *srch) + (v25 ^ srch_next)));
v42 = v25 == 0x9E3779B9;
v25 += 0x61C88647;
srch_next = srcl_next;
*src = srcl_next; // 前四位0x78654321替换为 0x35158711 小端
}
while ( !v42 );
}
return 0LL;
}
return result;
}
第三个函数是一些位操作,并且验证返回值是否为 0x69E2,如果是则将X按字节从低到高异或0x17输出
题目提示输出'Bingo!',所以将其异或回去得到一个长度为6的byte数组,(由于小端序所以是64位整数的低48位)合理猜测高8位为0x02,所以只需爆破次高8位数据即可(由此构造一个64位数经前两轮解密而后动态调试验证第三个函数返回了0x69E2且输出'Bingo!')
题目提示 md5(rctf{your answer}) == 5f8243a662cf71bf31d2b2602638dc1d
由此可以爆破flag了,算了一下大概需要 0xff*(2^n+km)次运算(n取决于flag经过第一轮置换字母的数量,km取决了flag经过第一轮置换f的数量,k是常量)
权衡了一下先爆破第二轮加密过程的多解,第一轮默认为小写,且0xf均视为'f'
幸运的是爆破到第二个flag就出来了
rctf{05e8a376e4e0446e}
加解密脚本如下
#include
#include
#include
#include
#include
using namespace std;
// -lcrypto
string MD5(const string& src )
{
MD5_CTX ctx;
string md5_string;
unsigned char md[16] = { 0 };
char tmp[33] = { 0 };
MD5_Init( &ctx );
MD5_Update( &ctx, src.c_str(), src.size() );
MD5_Final( md, &ctx );
for( int i = 0; i < 16; ++i )
{
memset( tmp, 0x00, sizeof( tmp ) );
sprintf( tmp, "%02X", md[i] );
md5_string += tmp;
}
return md5_string;
}
// encrypt01
unsigned long long encrypt01 (string str)
{
if (str.length() != 16) return 0;
unsigned char byte_5626AE9EB620[55] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
unsigned long long res = 0;
unsigned long long ptr = 0;
for (int i = 0; i < str.length(); i+=2)
{
unsigned char a = byte_5626AE9EB620[str[i] - 0x30];
unsigned char b = byte_5626AE9EB620[str[i+1] - 0x30];
ptr = (a << 4) + b;
ptr <<= 4*i;
res += ptr;
}
return res;
}
string decrypt01 (unsigned long long src)
{
unsigned char byte_5626AE9EB620[55] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
char chars [16]={0};
unsigned char dest = 0xff;
for (int i = 0; i < 16; i+=2)
{
unsigned char ch = src & dest ;
src >>= 8;
if (((ch & 0xf0) >> 4) <= 9)
chars[i] = ((ch & 0xf0) >> 4) + 0x30;
else
chars[i] = ((ch & 0xf0) >> 4) + 0x57;
if ((ch & 0x0f) <= 9)
chars[i+1] = (ch & 0x0f) + 0x30;
else
chars[i+1] = (ch & 0x0f) + 0x57;
}
return chars;
}
// encrypt02
unsigned long long encrypt02 (unsigned long long src){
unsigned int v25 = (0x9E3779B9 * (52 / 2 + 6) & 0xffffffff);
unsigned int l = src & 0xffffffff;
unsigned int r = (src >> 32) & 0xffffffff;
int i = 0;
bool flag = 0;
do{
int v31 = 1;
unsigned int v29 = v25 >> 2;
int table[] = {0xE0C7E0C7, 0xC6F1D3D7, 0xC6D3C6D3, 0xC4D0D2CE};
r = r - (((l^v25) + (l^table[(v29^v31)&3])) ^ (((l>>3)^16*l) + ((l>>5)^4*l)));
l = l - (((v25^r) + (r^table[(v29&3)])) ^ (((r>>5)^4*r) + (16*r^(r>>3))));
flag = v25 == 0x9E3779B9;
v25 += 0x61C88647;
}while (!flag);
unsigned long long res = ((unsigned long long)r << 32) + l;
return res;
}
//decrypt02
unsigned long long decrypt02(unsigned long long dest){
unsigned int v25 = 0x9E3779B9;
unsigned int l = dest & 0xffffffff;
unsigned int r = (dest >> 32) & 0xffffffff;
bool flag = 0;
do{
int v31 = 1;
unsigned int v29 = v25 >> 2;
int table[] = {0xE0C7E0C7, 0xC6F1D3D7, 0xC6D3C6D3, 0xC4D0D2CE};
l = l + (((v25^r) + (r^table[(v29&3)])) ^ (((r>>5)^4*r) + (16*r^(r>>3))));
r = r + (((l^v25) + (l^table[(v29^v31)&3])) ^ (((l>>3)^16*l) + ((l>>5)^4*l)));
flag = v25 ==(0x9E3779B9 * (52 / 2 + 6) & 0xffffffff);
v25 -= 0x61C88647;
}while(!flag);
unsigned long long res = ((unsigned long long)r << 32) + l;
return res;
}
int main(){
//BYTE7(crack) == 02
unsigned long long crack = (unsigned long long)0x02 << 56;
string str = "Bingo!";
int i = str.length();
while(i--) crack += ((unsigned long long)(str[i]^0x17) << (i * 8));
string flagmd5 = "5F8243A662CF71BF31D2B2602638DC1D";
for (unsigned long long i = 0x0; i <= 0xff; i+=0x1)
{
crack += (unsigned long long)0x1 << 48;
cout << hex << crack << endl;
string flag = "";
flag += "rctf{";
flag += decrypt01(decrypt02(crack));
flag += "}";
if (MD5(flag) == flagmd5)
{
cout << "success!" << endl;
cout << flag << endl;
return 0;
}
}
//encrypt03
// int v11 = r >> 24;
// if (v11 <= 4)//true
// {
// int v13 = 8 - v11;//v13 == 6
// //dest = v13
// //将src[v13]置0
// }
//encrypt04
//i=0
//v13 = 8 - 2
//依次将v[i++]二进制逆序,循环进行下列level1算法直到v[v13-1],算出最终结果为0x4796
//encrytp05
//移位操作0x4796 -> 0x69e2
return 0;
}