re | [羊城杯 2020]babyre

re | [羊城杯 2020]babyre

linux x64

main函数很单纯,藏了一个smc,直接动调dump。

signed __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  signed int i; // [rsp+0h] [rbp-140h]
  char v5; // [rsp+10h] [rbp-130h]
  char v6; // [rsp+90h] [rbp-B0h]
  char v7; // [rsp+A0h] [rbp-A0h]
  char s; // [rsp+B0h] [rbp-90h]
  char encrypted[72]; // [rsp+F0h] [rbp-50h]
  unsigned __int64 v10; // [rsp+138h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  smc();                                        // 这里调用了smc去自解密代码
  __isoc99_scanf("%39s", &s);
  if ( (unsigned int)strlen(&s) != 16 )
  {
    puts("Wrong!");
    exit(0);
  }
  DES_string_to_key((__int64)"this is my key", (__int64)&v6);
  if ( !(unsigned int)DES_set_key_checked((__int64)&v6, (__int64)&v5) )
  {
    memset(&v7, 0, 8uLL);
    DES_ncbc_encrypt(&s, encrypted, 60LL, &v5, &v7, 1LL);
    for ( i = 0; i <= 15; ++i )
    {
      if ( encrypted[i] != aim[i] )             // 16bytes对比
                                                // 
        puts("wrong!");
    }
    if ( (*(unsigned int (__fastcall **)(char *, char *))byte_40272D)(&s, encrypted) )// 这里是一个smc
      puts("Correct!");
    else
      puts("Wrong!");
    exit(0);
  }
  puts("convert to key_schedule failed.");
  return 0xFFFFFFFFLL;
}

接下来就是动调,顺带编译了一下openssl1.0.0,因为没有那个库(libcrypt.so)

image

本来想用gdb dump的,但是着实不够方便,只能一段一段dump,就用ida动调dump一下了:

__int64 __fastcall __noreturn sub_40272D(__int64 a1)
{
  signed int i; // [rsp+18h] [rbp-128h]
  int j; // [rsp+1Ch] [rbp-124h]
  unsigned int v4; // [rsp+20h] [rbp-120h]
  signed int k; // [rsp+24h] [rbp-11Ch]
  signed int l; // [rsp+28h] [rbp-118h]
  char v7; // [rsp+30h] [rbp-110h]
  char s[31]; // [rsp+F0h] [rbp-50h]
  char v9; // [rsp+10Fh] [rbp-31h]
  unsigned __int64 v10; // [rsp+138h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  __isoc99_scanf("%40s", s);
  if ( (unsigned int)strlen(s) != 32 )          // 输入32字节
  {
    puts("Wrong!");
    exit(0);
  }
  sub_400C91((__int64)&v7, a1);                 // 一眼AES,不用看了。
  sub_401B8E(&v7, s);
  sub_401B8E(&v7, &s[16]);
  for ( i = 0; i <= 31; ++i )
  {
    for ( j = 0; i / 4 > j; ++j )               // 做一个xor
      s[i] ^= s[j];
  }
  v4 = 1;
  for ( k = 1; k <= 31; ++k )
    convert[k - 1] = (2 * (s[k - 1] ^ 0x13) + 7) ^ ((unsigned __int8)s[k - 1] % 9u + s[k] + 2);
  if ( v9 == 0xC4u )
  {
    for ( l = 0; l <= 30; ++l )
    {
      if ( convert[l] != byte_604100[l] )       // 最终对比
        v4 = 0;
    }
  }
  return v4;
}

由于上次手撕过一次AES,这次不用看了,一眼AES。
整理一下流程就是解des得到aes的key,然后位运算,最后对比。

反推的时候发现有的推不出来,看了一眼这个师傅的文章(https://blog.csdn.net/A951860555/article/details/120195840),才明白原来每个位可能是多解:

image

重新改写程序递归深度优先搜索答案:
image

exp:

from Crypto.Cipher import AES, DES

key = b"\xAD\x52\xF2\x4C\xE3\x2C\x20\xD6"
text = b"\x0A\xF4\xEE\xC8\x42\x8A\x9B\xDB\xA2\x26\x6F\xEE\xEE\xE0\xD8\xA2"
iv = b"\x00"*8
aes_key = DES.new(key, DES.MODE_CBC, iv).decrypt(text)
print(f'key: {aes_key}')


v9=0xc4
aim = [0xBD, 0xAD, 0xB4, 0x84, 0x10, 0x63, 0xB3, 0xE1, 0xC6, 0x84, 
  0x2D, 0x6F, 0xBA, 0x88, 0x74, 0xC4, 0x90, 0x32, 0xEA, 0x2E, 
  0xC6, 0x28, 0x65, 0x70, 0xC9, 0x75, 0x78, 0xA0, 0x0B, 0x9F, 
  0xA6, 0x00]
print(len(aim))


tmp = [0 for i in range(32)]
tmp[31] = v9

possible = []


def test_flag(i, tmp):
	if i == -1:
		possible.append(tmp[:])
		print(tmp)
		return
	_tmp = tmp[:]
	for j in range(256):
		if ((((2 * (j ^0x13)) + 7) ^ ((j % 9) + _tmp[i+1] + 2))&0xff) == aim[i]:
			_tmp[i] = j
			test_flag(i-1,_tmp)
		if j == 255 and ((((2 * (j ^0x13)) + 7) ^ ((j % 9) + _tmp[i+1] + 2))&0xff) != aim[i]:
			return
	

test_flag(30, tmp)

print(possible)
print(len(possible))

for x in possible:
	for i in range(31, -1, -1):
		for j in range(int(i/4)):
			x[i] ^= x[j]
	tmp = AES.new(aes_key, AES.MODE_ECB).decrypt(bytes(x))
	print(tmp)
posted @ 2023-01-09 14:43  Mz1  阅读(335)  评论(0编辑  收藏  举报