初音ミクの消失

rctf_babyre2

字数统计: 1.3k阅读时长: 7 min
2019/05/21 Share

Rctf_babyre2

rctf_babyre2

思路

alarm

程序一开头就有一个alarm函数,这会使我们在调试的时候中途退出,直接把call alarm nop掉即可

sub_55761565891A

__int64 __fastcall sub_55761565891A(void *a1)
{
unsigned int v2; // [rsp+10h] [rbp-10h]
signed int v3; // [rsp+14h] [rbp-Ch]

v2 = 0;
print_notice();
memset(a1, 0, 0x100uLL);
read(0, a1, 0xFFuLL);
v3 = strlen((const char *)a1);
if ( v3 <= 16 )
{
if ( v3 > 7 )
v2 = 1;
else
print_notice();
}
else
{
print_notice();
}
return v2;
}

sub_5576156589CC

__int64 __fastcall sub_5576156589CC(void *a1)
{
unsigned int v2; // [rsp+1Ch] [rbp-14h]
signed int i; // [rsp+20h] [rbp-10h]
signed int v4; // [rsp+24h] [rbp-Ch]

v2 = 0;
print_notice();
memset(a1, 0, 0x100uLL);
read(0, a1, 0xFFuLL);
v4 = strlen((const char *)a1);
if ( v4 <= 16 )
{
if ( v4 > 7 )
{
v2 = 1;
for ( i = 0; i < v4; ++i )
{
if ( *((_BYTE *)a1 + i) <= 9 || *((_BYTE *)a1 + i) > 99 )
{
print_notice();
return 0;
}
}
}
else
{
print_notice();
}
}
else
{
print_notice();
}
return v2;
}

简单的读入账户和密码,不赘述

不赘述就出问题了

读密码的时候你没发现一个奇怪的事情吗?

if ( *((_BYTE *)a1 + i) <= 9 || *((_BYTE *)a1 + i) > 99 )

密码可以不是可见字符

sub_557615658ACE

char *__fastcall sub_557615658ACE(__int64 a1, __int64 a2)
{
__int64 v2; // rdx
int i; // [rsp+10h] [rbp-40h]
__int64 v5; // [rsp+14h] [rbp-3Ch]
__int64 dest; // [rsp+20h] [rbp-30h]
__int64 v7; // [rsp+28h] [rbp-28h]
char v8; // [rsp+30h] [rbp-20h]
char v9; // [rsp+31h] [rbp-1Fh]
char v10; // [rsp+32h] [rbp-1Eh]
char v11; // [rsp+33h] [rbp-1Dh]
char v12; // [rsp+34h] [rbp-1Ch]
char v13; // [rsp+35h] [rbp-1Bh]
char v14; // [rsp+36h] [rbp-1Ah]
char v15; // [rsp+37h] [rbp-19h]
char v16; // [rsp+38h] [rbp-18h]
char v17; // [rsp+39h] [rbp-17h]
char v18; // [rsp+3Ah] [rbp-16h]
char v19; // [rsp+3Bh] [rbp-15h]
char v20; // [rsp+3Ch] [rbp-14h]
char v21; // [rsp+3Dh] [rbp-13h]
char v22; // [rsp+3Eh] [rbp-12h]
char v23; // [rsp+3Fh] [rbp-11h]
char v24; // [rsp+40h] [rbp-10h]
unsigned __int64 v25; // [rsp+48h] [rbp-8h]

v25 = __readfsqword(0x28u);
v8 = -8;
v9 = -44;
v10 = -43;
v11 = -36;
v12 = -55;
v13 = -38;
v14 = -49;
v15 = -50;
v16 = -41;
v17 = -38;
v18 = -49;
v19 = -46;
v20 = -44;
v21 = -43;
v22 = -56;
v23 = -102;
v24 = -79;
dest = 0LL;
v7 = 0LL;
v5 = (unsigned int)strlen((const char *)a1);
if ( (signed int)v5 <= 16 )
{
memcpy(&dest, (const void *)a1, (signed int)v5);// 复制内存
for ( i = 0; 16 - (signed int)v5 > i; ++i )
*((_BYTE *)&dest + i + (signed int)v5) = i + 1;// 对于没有满16位的account 自动补全16位(补全一个从0开始的递增序列)
}
else
{
v2 = *(_QWORD *)(a1 + 8);
dest = *(_QWORD *)a1;
v7 = v2;
}
return sub_55761565981E(&v8, 17, (__int64)&dest, 16, (int *)a2);
}
char *__fastcall sub_55761565981E(const void *buf, int a2, __int64 a1, int a4, int *a5)
{
int *v6; // [rsp+0h] [rbp-40h]
__int64 acc; // [rsp+8h] [rbp-38h]
signed int v8; // [rsp+24h] [rbp-1Ch]
int v9; // [rsp+28h] [rbp-18h]
char *dest; // [rsp+30h] [rbp-10h]

acc = a1;
v6 = a5;
dest = 0LL;
v8 = 0;
if ( buf )
{
if ( a2 > 0 )
{
if ( a1 )
{
if ( a4 == 16 )
{
if ( a5 )
{
*a5 = 0;
v9 = 4 * (a2 / 4 + 1);
dest = (char *)malloc(v9 + 1);
if ( dest )
{
memcpy(dest, buf, a2);
memset(&dest[a2], 4 - a2 % 4, 4 - a2 % 4);// 填充了3
dest[v9] = 0;
xtea((unsigned int *)dest, v9 / 4, acc);// 这里是执行了加密的过程,account作为密钥
*v6 = v9;
v8 = 1;
}
}
}
}
}
}
if ( !v8 )
{
if ( dest )
{
free(dest);
dest = 0LL;
}
if ( v6 )
*v6 = 0;
}
return dest;
}

简单来说就是利用xtea算法,将buf(上面的数组)加密了,密钥是账户名

sub_557615658C02

__int64 __fastcall sub_557615658C02(__int64 a1)
{
signed int v2; // [rsp+14h] [rbp-81Ch]
int v3; // [rsp+18h] [rbp-818h]
unsigned int v4; // [rsp+18h] [rbp-818h]
int i; // [rsp+1Ch] [rbp-814h]
char buf[2048]; // [rsp+20h] [rbp-810h]
char v7; // [rsp+820h] [rbp-10h]
unsigned __int64 v8; // [rsp+828h] [rbp-8h]

v8 = __readfsqword(0x28u);
memset(buf, 0, sizeof(buf));
v7 = 0;
print_notice();
read(0, buf, 0x400uLL);
v3 = strlen(buf);
if ( v3 & 1 || v3 > 1024 ) // 长度不超过1024,且必须为偶数
{
v4 = 0;
print_notice();
}
else
{
v2 = 1;
for ( i = 0; i < v3; ++i ) // 检测输入是否合规
{
if ( (buf[i] <= 47 || buf[i] > 57) && (buf[i] <= 96 || buf[i] > 102) && (buf[i] <= 64 || buf[i] > 70) )// 只准输入数字,大小写的字母a~f
{
print_notice();
v2 = 0;
break;
}
}
if ( v2 ) // 如果输入合规
v4 = sub_5576156587C6(buf, v3, (_QWORD *)a1);
else
v4 = 0;
}
return v4;
}

读入了偶数个字节的数据,并且长度不超过1024,数据只能在09 af A~F的范围

sub_557615658DD6

_BYTE *__fastcall sub_557615658DD6(const char *passwd, __int64 data, int chunk, _DWORD *a4)
{
_DWORD *v5; // [rsp+0h] [rbp-50h]
int v6; // [rsp+Ch] [rbp-44h]
signed int v7; // [rsp+2Ch] [rbp-24h]
signed int v8; // [rsp+30h] [rbp-20h]
int i; // [rsp+34h] [rbp-1Ch]
int v10; // [rsp+38h] [rbp-18h]
int v11; // [rsp+3Ch] [rbp-14h]
_BYTE *ptr; // [rsp+40h] [rbp-10h]

v6 = chunk;
v5 = a4;
v7 = 0;
ptr = 0LL;
if ( data )
{
if ( chunk > 0 )
{
if ( passwd )
{
v10 = strlen(passwd);
if ( v10 > 0 )
{
ptr = malloc(v10 + 1);
if ( ptr )
{
ptr[v10] = 0;
v8 = 1;
for ( i = 0; i < v10; ++i )
{
v11 = passwd[i] - (passwd[i] % 10 + passwd[i] / 10);// 由passwd生成了一个偏移地址
if ( v11 >= v6 )
{
v8 = 0;
break;
}
ptr[i] = *(_BYTE *)(v11 + data); // ptr[i]就等于这个偏移地址上面的数据
}
if ( v8 ) // 若正常退出
{
*v5 = v10;
v7 = 1;
}
}
}
}
}
}
if ( !v7 && ptr )
{
free(ptr);
ptr = 0LL;
}
return ptr;
}

用passwd可以算出来一个偏移,对应着data中的数据,生成了一个ptr数组

sub_557615658FB0

for ( j = 0; j < passwd_len; ++j )
*((_BYTE *)&dest + j) ^= 0xCCu; // ptr进行异或0xcc
::ptr = sub_5576156599B9(v6, v7, (__int64)&dest, 16, &dword_55761585B058);

sub_5576156599B9:

xtea((unsigned int *)dest, buf_len / -4, v7);// 这里执行解密的过程,解密encrypted,密钥为ptr_xor

公式

xtea(xtea(buf, accout, ENCRYPT) ,f(data,passwd) , DECRYPT)

f(data, passwd) == accout

passwd到data是一个很简单的映射,映射完之后要异或一个0xcc,异或结果应该刚好等于account

solve

script from dalao

https://balsn.tw/ctf_writeup/20190518-rctf2019/#babyre2

我自己还没用过pwntools。。。没想到要直接给程序传入非可见字符,所以就借用了一下国外大佬的脚本

from pwn import *

r=remote("139.180.215.222", 20000)
print r.recvuntil("account")
r.send("a"*16)
print r.recvuntil("password")
r.send("\x10"*16)
r.recvuntil("data")
r.send("010203040506070809ad0b0c0d0e0f") #ad=61^cc
r.shutdown("send")

r.interactive()

原文作者:mrh929

原文链接:https://mrh1s.top/posts/8b232dcf/

发表日期:May 21st 2019, 8:29:34 pm

更新日期:May 21st 2019, 8:51:36 pm

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

CATALOG
  1. 1. Rctf_babyre2
    1. 1.1. 思路
      1. 1.1.1. alarm
      2. 1.1.2. sub_55761565891A
      3. 1.1.3. sub_5576156589CC
      4. 1.1.4. sub_557615658ACE
      5. 1.1.5. sub_557615658C02
      6. 1.1.6. sub_557615658DD6
      7. 1.1.7. sub_557615658FB0
      8. 1.1.8. 公式
    2. 1.2. solve