首页
论坛
课程
招聘
[原创] 看雪.安恒2020 KCTF春季赛 > 第二题 子鼠开天 WP
2020-4-17 00:59 2088

[原创] 看雪.安恒2020 KCTF春季赛 > 第二题 子鼠开天 WP

2020-4-17 00:59
2088
本题是考密码学,用到了AES,RSA,SHA512,MD5,不过算法还是很简单的,只要RSA的大数分解能完成,就很容易做出来,分析如下:

IDA中F5,主函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
  time_t v3; // eax
  unsigned int v4; // kr04_4
  int result; // eax
  char v6; // [esp+8h] [ebp-12Ch]
  char v7; // [esp+9h] [ebp-12Bh]
  __int16 v8; // [esp+69h] [ebp-CBh]
  char v9; // [esp+6Bh] [ebp-C9h]
  char v10; // [esp+6Ch] [ebp-C8h]
  char v11; // [esp+6Dh] [ebp-C7h]
  __int16 v12; // [esp+131h] [ebp-3h]
  char v13; // [esp+133h] [ebp-1h]

  v6 = 0;
  v10 = 0;
  memset(&v7, 0, 0x60u);
  v8 = 0;
  v9 = 0;
  memset(&v11, 0, 0xC4u);
  v12 = 0;
  v13 = 0;
  v3 = time(0);
  sub_411AD8(v3);
  sub_411A90(aEnterYourName);
  scanf(aS, &v6);
  v4 = strlen(&v6) + 1;
  if ( v4 - 1 < 3 || v4 - 1 > 0x14 ) //名字长度判断
  {
    sub_411A90(aBadName);
    result = -1;
  }
  else
  {
    sub_411A90(aEnterYourSn);
    scanf(aS, &v10);
    if ( strlen(&v10) == 64 ) //SN长度判断
    {
      sub_401380((int)&v6, v4 - 1, (int)&v10, 64); //验证
      result = 0;
    }
    else
    {
      sub_411A90(aBadSn);
      result = -1;
    }
  }
  return result;
}
可以看出输入的名字长度可以在3-20这间,SN长度为64,验证函数是sub_401380:
void __cdecl sub_401380(char *name, unsigned int lenName, char *sSn, int lenSN)
{
  char v4; // [esp+4h] [ebp-70h]
  BYTE sn2[32]; // [esp+14h] [ebp-60h]
  BYTE sn[32]; // [esp+34h] [ebp-40h]
  BYTE sn1[32]; // [esp+54h] [ebp-20h]

  if ( lenName >= 3 && lenName <= 0x14 && lenSN == 64 )
  {
    if ( sub_401000(sSn, 64, (int)sn) != 32     // 16进制串sSn转BYTE sn[32]
      || (AESCode(sn, 32, (int)sn1, keyAES, 128, 0), RSACode((int)sn1, 32, (int)sn2), sn2[0])// sn2[0]要等于0
      || sn2[1] != 2                            // sn2[1]要等于2
      || sn2[15] )                              // sn2[15]要等于0,这儿sn2前16字节只判断了三个字节,另13个字节可以为任意,就出现了多解
    {
      sub_411A90(aBadSn);
    }
    else
    {
      hashCode(name, lenName, (int)&v4);        // 计算name的hash值
      if ( !memcmp(&v4, &sn2[16], 0x10u) )      // 比较sn2后16字节要等于name的hash
        sub_411A90(aCongratulation);            // 成功
    }
  }
}

AESCode是AES算法,通过一个函数,由参数bEncode来指定完成加密或解密
int __cdecl AESCode(BYTE *bufOut, int lenOut, BYTE *bufIn, BYTE *keyAES, int lenBit, BOOL bEncode)
{
  BYTE *v6; // esi
  int v7; // ebx
  char v9; // [esp+4h] [ebp-F4h]

  if ( bEncode == 1 )                           // 同一个函数完成加密解密,由bEncode判断是加密还是解密
    sub_404FE0(keyAES, lenBit, (unsigned int *)&v9);
  else
    sub_4053A0(keyAES, lenBit, (unsigned int *)&v9);
  if ( lenOut / 16 <= 0 )
    return lenOut;
  v6 = bufOut;
  v7 = lenOut / 16;
  do
  {
    sub_404FB0(v6, &v6[bufIn - bufOut], &v9, bEncode);
    v6 += 16;
    --v7;
  }
  while ( v7 );
  return lenOut;
}

RSACode对输入完成RSA解密,返回值高位在前
signed int __cdecl RSACode(BYTE *bufIn, int lenIn, BYTE *bufOut)
{
  _DWORD *N; // esi
  _DWORD *e; // edi
  _DWORD *m; // ebx
  _DWORD *c; // ebp
  signed int v8; // eax
  _DWORD *lpMem; // [esp+0h] [ebp-24h]
  BYTE bN[32]; // [esp+4h] [ebp-20h]
  BYTE *lenIna; // [esp+2Ch] [ebp+8h]

  bN[0] = 105; //高位在前 N = 0x69823028577465AB3991DF045146F91D556DEE8870845D8EE1CD3CF77E4A0C39
  bN[8] = 57;
  bN[31] = 57;
  bN[1] = -126;
  bN[2] = 48;
  bN[3] = 40;
  bN[4] = 87;
  bN[5] = 116;
  bN[6] = 101;
  bN[7] = -85;
  bN[9] = -111;
  bN[10] = -33;
  bN[11] = 4;
  bN[12] = 81;
  bN[13] = 70;
  bN[14] = -7;
  bN[15] = 29;
  bN[16] = 85;
  bN[17] = 109;
  bN[18] = -18;
  bN[19] = -120;
  bN[20] = 112;
  bN[21] = -124;
  bN[22] = 93;
  bN[23] = -114;
  bN[24] = -31;
  bN[25] = -51;
  bN[26] = 60;
  bN[27] = -9;
  bN[28] = 126;
  bN[29] = 74;
  bN[30] = 12;
  if ( lenIn != 32 )
    return 0;
  lpMem = sub_406170();
  N = sub_406650();
  e = sub_406650();
  m = sub_406650();
  c = sub_406650();
  BigIntFromBytes(bufIn, 32, c);
  BigIntFromInt(e, 65537);
  BigIntFromBytes(bN, 32, N);
  BigIntPowMod(m, c, e, N, lpMem);              // 大数计算 m = c ^ e mod N
  v8 = 0;
  lenIna = (BYTE *)(*m + 31);
  do                                            // 返回 m 到 bufOut, 高位在前
    bufOut[++v8 - 1] = *lenIna--;
  while ( v8 < 32 );
  sub_4065F0(N);
  sub_4065F0(e);
  sub_4065F0(m);
  sub_4065F0(c);
  sub_4061D0(lpMem);
  return 32;
}

再来看hashCode:
signed int __cdecl hashCode(const void *bufIn, unsigned int lenIn, PBYTE bufOut)
{
  BYTE bufTmp[20]; // [esp+8h] [ebp-DCh]
  char v5[200]; // [esp+1Ch] [ebp-C8h]

  qmemcpy(v5, bufIn, lenIn);
  *(_DWORD *)&bufTmp[lenIn + 20] = dword_41D038;// bufIn尾部加四字节 DE ED BE EF
  Sha512MD5((PBYTE)v5, lenIn + 4, bufTmp);      // 计算Sha512 再 MD5
  *(_DWORD *)&bufTmp[16] = dword_41D030;        // 16字节中间结果后面再加四字节 B9 79 37 9E
  Sha512MD5(bufTmp, 20, bufOut);                // 再计算 Sha512 再 MD5
  return 16;
}

signed int __cdecl Sha512MD5(PBYTE bufIn, int lenIn, PBYTE bufOut)
{
  BYTE v4[64]; // [esp+0h] [ebp-40h]

  SHA512((int)bufIn, lenIn, v4);
  MD5((int)v4, 64, bufOut);
  return 16;
}

由以上分析可以看出本用了好几种加密算法,不过验证算法还是比较简单的:
设输入的sSN转字节数据为sn:
sn1 = AESDecode(sn)
sn2 = RSACode(sn1)
hashname = hashCode(name)
if(sn2[0] = 0 && sn2[1] == 2 && sn[15] == 0 && sn[16-31] == hashname)
{
//成功
}
所以只要sn2[0,1,15-31]满足条件就成功,而sn2[2-14]可以为任意值,这就会多解了

关于求解方法:
hashname 可以直接由程序中的 hashCode(name)直接得出,而AES运算也可直接用程序中的函数AESCode函数计算出来,只需将参数bEncode改成1,输入改成sn1就可以了,难一点的就是RSA算法:
N = 0x69823028577465AB3991DF045146F91D556DEE8870845D8EE1CD3CF77E4A0C39
E = 0x10001
在http://www.factordb.com/分解N得到:
p = 0x979BE0C9EECE7426C9FD28C2D6E7772B
q = 0xB22831D15714EB91CD83340B4837182B
再用RSATool计算出D:
D = 0x390A684CB713378FFD5CCE8C4000B5D6A2BB9F29B63D395E6BE6E9DD941527BD
有了N,D,就可用大数计算器由sn2算出sn1:

hashCode('KCTF') = sn2[16-31] = 14AF58AD4D76D59D8D2171FFB4CA2231
由于sn2[2-14]没有验证,可以为任意值,全取0有:
sn2 = 0x0002000000000000000000000000000014AF58AD4D76D59D8D2171FFB4CA2231
由N,D,用大数计算器计算得到sn1 = sn2 ^ D mod N
sn1 = 46FD7E72B31A3CB32B2DB098B3597825056A8AC4CF13CD127B95D2B22D9F2E45
OD调试程序,在 004013DF 下断,输入以上sn1,断下后改堆栈中第六个参数为1,步过CALL,第三个参数所指地址就能得到sn:
EF 58 9F 33 33 82 26 68 83 B1 3D 8D F4 C6 C4 C2 A7 86 C2 E7 D9 53 8E 4A 3D 98 E7 B6 CF CD DC E1
即 sSN 为 EF589F333382266883B13D8DF4C6C4C2A786C2E7D9538E4A3D98E7B6CFCDDCE1

本题中计算用到的算法还是有好几种,有AES,RSA,SHA512,MD5不过不明白为什么结果有13字节没有验证,明显的BUG,这样必然多解


恭喜ID[飞翔的猫咪]获看雪安卓应用安全能力认证高级安全工程师!!

收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回