RCTF_2019 write up

发布于 2019-05-21  29 次阅读


Reverse

babyre1

rctf_babyre1

思路

以下是与输入相关的指令

main

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  unsigned __int64 *in; // rdx
  int v4; // ecx
  unsigned int v5; // eax
  unsigned __int64 v6; // rdx
  int v7; // eax
  int *v8; // rax
  char *v9; // rbx
  __int64 i; // rax
  int v12; // [rsp+4h] [rbp-124h]
  void *ptr; // [rsp+8h] [rbp-120h]
  unsigned __int64 str; // [rsp+10h] [rbp-118h]
  unsigned __int64 v15; // [rsp+118h] [rbp-10h]

  v15 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  ptr = 0LL;
  v12 = 0;
  memset(&str, 0, 0x100uLL);
  __printf_chk(1LL, "Input right flag you can got 'Bingo!' :");
  __isoc99_scanf("%31s", &str);                 // 输入为16位
  in = &str;
  do
  {
    v4 = *(_DWORD *)in;
    in = (unsigned __int64 *)((char *)in + 4);
    v5 = ~v4 & (v4 - 0x1010101) & 0x80808080;
  }
  while ( !v5 );
  if ( !((unsigned __int16)~(_WORD)v4 & (unsigned __int16)(v4 - 0x101) & 0x8080) )
    v5 >>= 16;
  if ( !((unsigned __int16)~(_WORD)v4 & (unsigned __int16)(v4 - 0x101) & 0x8080) )
    in = (unsigned __int64 *)((char *)in + 2);
  v6 = (char *)in - __CFADD__((_BYTE)v5, (_BYTE)v5) - 3 - (char *)&str;
  if ( v6 > 0x10 )
  {
    puts("input is too long!");
  }
  else if ( v6 == 16 )
  {
    v7 = change_into_number((unsigned __int64)&str, 16, &ptr);// 这个程序就是把输入的内容(十六进制)转换成了程序可读的数字
    if ( v7
      && (v8 = xtea_keychanged(ptr, v7, (__int64)&unk_55883B7B0010, 16, &v12), (v9 = (char *)v8) != 0LL)
      && v12 > 0
      && (unsigned __int16)crc16((__int64)v8, v12) == 0x69E2 )
    {
      for ( i = 0LL; v12 > (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!");
  }
  return 0LL;
}

可以看到输入后的字符串进行了一大堆看不懂的操作,但是能推测出来输入的长度为16位

用ida调试可知,change_into_number这个函数把输入的字符串进行了转换

完成了16进制字符串 -> 16进制整数的过程

xtea_keychanged中的一个子函数

signed __int64 __fastcall sub_55883B5AECE0(int *a1, signed int a2, __int64 a3)
{
  __int64 v3; // rbp
  signed int v4; // ebx
  unsigned int v5; // ecx
  int v6; // er12
  int *v7; // r13
  unsigned int v8; // er14
  unsigned int v9; // ecx
  unsigned int v10; // er15
  unsigned int v11; // ebx
  unsigned int v12; // er10
  int *v13; // r9
  int v14; // er8
  unsigned int v15; // esi
  int v16; // eax
  unsigned __int8 v17; // dl
  unsigned int v18; // eax
  int *v19; // r10
  unsigned int v20; // eax
  char v21; // dl
  unsigned int v22; // eax
  signed __int64 result; // rax
  int v24; // esi
  unsigned int v25; // er11
  int *v26; // r13
  int v27; // er14
  unsigned int v28; // er12
  unsigned int v29; // ebx
  int *v30; // r8
  int v31; // esi
  unsigned int v32; // er9
  unsigned int v33; // er10
  unsigned int v34; // eax
  int v35; // ecx
  unsigned __int8 v36; // dl
  unsigned int v37; // eax
  int v38; // er15
  int *v39; // r8
  unsigned int v40; // er10
  int v41; // eax
  bool v42; // zf
  signed int v43; // [rsp+0h] [rbp-40h]
  int *v44; // [rsp+0h] [rbp-40h]
  unsigned int v45; // [rsp+Ch] [rbp-34h]

  v3 = a3;
  v4 = a2;
  v5 = *a1;
  v43 = a2;
  if ( a2 > 1 )
  {
    v6 = a2 - 1;
    v7 = &a1[a2 - 1];
    v8 = 0;
    v9 = *v7;
    v10 = ((a2 - 4) & 0xFFFFFFFE) + 2;
    v45 = 0x9E3779B9 * (52 / a2) - 0x4AB325AA;
    do
    {
      v8 -= 0x61C88647;
      v11 = v8 >> 2;
      if ( v43 <= 3 )
      {
        v14 = 0;
      }
      else
      {
        v12 = *a1;
        v13 = a1;
        v14 = 0;
        do
        {
          v15 = v13[1];
          v13 += 2;
          v16 = (v9 ^ *(_DWORD *)(v3 + 4LL * (((unsigned __int8)v14 ^ (unsigned __int8)v11) & 3))) + (v15 ^ v8);
          v17 = v14 + 1;
          v14 += 2;
          v18 = v12 + ((((v9 >> 5) ^ 4 * v15) + ((v15 >> 3) ^ 16 * v9)) ^ v16);
          v12 = *v13;
          *(v13 - 2) = v18;
          v9 = v15
             + (((4 * v12 ^ (v18 >> 5)) + (16 * v18 ^ (v12 >> 3))) ^ ((v12 ^ v8)
                                                                    + (v18 ^ *(_DWORD *)(v3
                                                                                       + 4LL
                                                                                       * (((unsigned __int8)v11 ^ v17) & 3)))));
          *(v13 - 1) = v9;
        }
        while ( v10 != v14 );
      }
      v19 = &a1[v14];
      do
      {
        v20 = v19[1];
        v21 = v11 ^ v14++;
        ++v19;
        v22 = *(v19 - 1)
            + (((v9 ^ *(_DWORD *)(v3 + 4LL * (v21 & 3))) + (v20 ^ v8)) ^ ((16 * v9 ^ (v20 >> 3)) + ((v9 >> 5) ^ 4 * v20)));
        *(v19 - 1) = v22;
        v9 = v22;
      }
      while ( v6 > v14 );
      v9 = *v7
         + (((v22 ^ *(_DWORD *)(v3 + 4LL * (((unsigned __int8)v6 ^ (unsigned __int8)v11) & 3))) + (*a1 ^ v8)) ^ ((4 * *a1 ^ (v22 >> 5)) + (16 * v22 ^ ((unsigned int)*a1 >> 3))));
      *v7 = v9;
    }
    while ( v8 != v45 );
    return 0LL;
  }
  result = 1LL;
  if ( a2 < -1 )
  {
    v24 = -a2;
    v25 = -1640531527 * (52 / v24 + 6);
    if ( v25 )
    {
      v26 = &a1[v24 - 1];
      v27 = ~v4;
      v44 = &a1[~v4];
      v28 = ~v4 - 2 - ((~v4 - 3) & 0xFFFFFFFE);
      do
      {
        v29 = v25 >> 2;
        if ( v27 <= 2 )
        {
          v31 = v27;
        }
        else
        {
          v30 = v44;
          v31 = v27;
          v32 = *v44;
          do
          {
            v33 = *(v30 - 1);
            v30 -= 2;
            v34 = v32;
            v32 = *v30;
            v35 = ((v5 ^ v25) + (v33 ^ *(_DWORD *)(v3 + 4LL * (((unsigned __int8)v31 ^ (unsigned __int8)v29) & 3)))) ^ ((4 * v5 ^ (v33 >> 5)) + ((v5 >> 3) ^ 16 * v33));
            v36 = v31 - 1;
            v31 -= 2;
            v37 = v34 - v35;
            v38 = *v30;
            v30[2] = v37;
            v5 = v33
               - (((16 * v38 ^ (v37 >> 3)) + ((v32 >> 5) ^ 4 * v37)) ^ ((v32 ^ *(_DWORD *)(v3
                                                                                         + 4LL
                                                                                         * (((unsigned __int8)v29 ^ v36) & 3)))
                                                                      + (v25 ^ v37)));
            v30[1] = v5;
          }
          while ( v28 != v31 );
        }
        v39 = &a1[v31];
        do
        {
          v40 = *(v39 - 1);
          --v39;
          v5 = v39[1]
             - (((v5 ^ v25) + (v40 ^ *(_DWORD *)(v3 + 4LL * (((unsigned __int8)v29 ^ (unsigned __int8)v31) & 3)))) ^ (((v5 >> 3) ^ 16 * v40) + ((v40 >> 5) ^ 4 * v5)));
          v39[1] = v5;
          --v31;
        }
        while ( v31 );
        v41 = *a1
            - (((((unsigned int)*v26 >> 5) ^ 4 * v5) + (16 * *v26 ^ (v5 >> 3))) ^ ((*(_DWORD *)(v3 + 4LL * (v29 & 3)) ^ *v26)
                                                                                 + (v25 ^ v5)));
        v42 = v25 == -1640531527;
        v25 += 1640531527;
        v5 = v41;
        *a1 = v41;
      }
      while ( !v42 );
    }
    return 0LL;
  }
  return result;
}

由0x9E3779B9可知,这是一个xtea加密(解密)算法

然后加解密所使用的常数有所改变,是内存中的16字节的数组

xtea(v8, -(v10 >> 2), constant)

这个 -(v10 >> 2)明显是一个负数,而xtea函数为负时,就是解密的过程

在调试时xtea_keychanged这个函数执行完后就直接报wrong

经高人指点,这个是xtea解密失败,程序判断这个密码解不出来,就直接报错了

xtea_keychanged

int *__fastcall xtea_keychanged(void *src, int const_8, __int64 a3, int a4, int *a5)
{
  int *v5; // rbp
  int v6; // er12
  __int64 constant; // r14
  int *string; // rbx
  int *v9; // rax
  int v10; // esi
  signed int v11; // eax
  int v13; // er12

  v5 = a5;
  if ( !src || (v6 = const_8, const_8 <= 0) || (constant = a3) == 0 || a4 != 16 )
  {
    if ( !a5 )
      goto LABEL_14;
    goto LABEL_13;
  }
  string = 0LL;
  if ( a5 )
  {
    *a5 = 0;
    if ( !(const_8 & 3) )
    {
      v9 = (int *)malloc(const_8 + 1);
      string = v9;
      if ( v9 )
      {
        memcpy(v9, src, const_8);               // v9 存储了输入的16进制数
        v10 = const_8 + 3;
        if ( v6 >= 0 )
          v10 = v6;
        xtea(string, -(v10 >> 2), constant);    // xtea对输入的字符串进行解密
        v11 = *((unsigned __int8 *)string + v6 - 1);// 取第八个字符
        *((_BYTE *)string + v6) = 0;
        *v5 = v6;
        if ( v6 > v11 && v11 <= 4 )
        {
          v13 = v6 - v11;                       // 8 - v11
          *v5 = v13;
          *((_BYTE *)string + v13) = 0;         // 把某一位置零了
          return string;
        }
        free(string);
      }
    }
LABEL_13:
    *v5 = 0;
LABEL_14:
    string = 0LL;
  }
  return string;
}
   *((_BYTE *)string + v13) = 0;         // 把某一位置零了

这句话很关键,把某一位截断了,而这一位只能是倒数第二位

crc16

__int64 __fastcall sub_55883B5AF3D0(__int64 a1, int a2)
{
  __int64 v2; // r12
  __int64 v3; // rbp
  __int16 v4; // ax
  __int16 v5; // ax
  __int16 v6; // dx
  __int16 v7; // ax
  __int16 v8; // dx
  __int16 v9; // ax
  __int16 v10; // dx
  __int16 v11; // ax
  unsigned __int8 v13; // [rsp+5h] [rbp-23h]
  unsigned __int16 v14; // [rsp+6h] [rbp-22h]
  unsigned __int64 v15; // [rsp+8h] [rbp-20h]

  v15 = __readfsqword(0x28u);
  v13 = 0;
  v14 = 0;
  if ( a2 )
  {
    v2 = a1;
    v3 = a1 + (unsigned int)(a2 - 1) + 1;
    do
    {
      while ( 1 )
      {
        v13 = *(_BYTE *)(++v2 - 1);
        sub_55883B5AF270(&v13, &v13);
        v4 = v14 ^ (v13 << 8);
        if ( ((v14 ^ (v13 << 8)) & 0x8000u) != 0 )
          v5 = 2 * v4 ^ 0x1021;
        else
          v5 = 2 * v4;
        v6 = 2 * v5 ^ 0x1021;
        if ( v5 >= 0 )
          v6 = 2 * v5;
        v7 = 2 * v6 ^ 0x1021;
        if ( v6 >= 0 )
          v7 = 2 * v6;
        v8 = 2 * v7 ^ 0x1021;
        if ( v7 >= 0 )
          v8 = 2 * v7;
        v9 = 2 * v8 ^ 0x1021;
        if ( v8 >= 0 )
          v9 = 2 * v8;
        v10 = 2 * v9 ^ 0x1021;
        if ( v9 >= 0 )
          v10 = 2 * v9;
        v11 = 2 * v10 ^ 0x1021;
        if ( v10 >= 0 )
          v11 = 2 * v10;
        if ( v11 < 0 )
          break;
        v14 = 2 * v11;
        if ( v2 == v3 )
          goto LABEL_19;
      }
      v14 = 2 * v11 ^ 0x1021;
    }
    while ( v2 != v3 );
  }
LABEL_19:
  sub_55883B5AF2D0(&v14, &v14);
  return v14;
}

也是通过0x1021可知,这是一个crc16校验,校验码为0x1021

if ( v2 == v3 )
     goto LABEL_19;

由这句话可知,v2会从起始字节一直遍历到v3处,那么a2就要构造得刚好把这些值覆盖,可知a2=6,所以解密后的那个数组的最后一个字节就是2

调试tips:

有效的加密字符串 4290cd6dc6ae54cc

解密后得0123456789abcd02

大致思路

  1. 程序读入16位的十六进制数,并转化为8字节
  2. 将数据进行xtea解密

同时这个自带加密函数,只需要把输入的参数取反,就是逆过程了

  1. 将解密后的数据进行crc16校验(校验码为0x1021),得到结果0x69E2
  2. 这个解密后的数组去异或0x17能得到"Bingo!" 字符串

solve

def crc16(data: bytes, poly=0x1021):
    '''
    CRC-16-CCITT Algorithm
    '''
    data = bytearray(data)
    crc = 0xFFFF
    for b in data:
        cur_byte = 0xFF & b
        for _ in range(0, 8):
            if (crc & 0x0001) ^ (cur_byte & 0x0001):
                crc = (crc >> 1) ^ poly
            else:
                crc >>= 1
            cur_byte >>= 1
    crc = (~crc & 0xFFFF)
    crc = (crc << 8) | ((crc >> 8) & 0xFF)

    return crc & 0xFFFF

enc = "Bingo!"
dec = []

for i in enc:
    dec.append(hex(ord(i) ^ 0x17))
dec.append(hex(6))
dec.append(hex(2))

print(dec)

557e797078360602

然后再拿这个字符串去加密一遍,就得到flag了

rctf{2a2e71aab6168fb6}

[11] Accepting connection from 192.168.133.1...
Input right flag you can got 'Bingo!' :2a2e71aab6168fb6

    Bingo!

后续:据说倒数第二字节是个padding

有空再填坑 https://blog.csdn.net/shift_wwx/article/details/84256774

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,数据只能在0~9 a~f 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()

CTFer|NOIPer|CSGO|摸鱼|菜鸡