看雪.安恒2020 KCTF春季赛 > 第二题 子鼠开天 WP
2020-04-19 06:47:06 Author: bbs.pediy.com(查看原文) 阅读量:383 收藏

本题是考密码学,用到了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,这样必然多解

[求职]想求职找工作,请来看雪招聘投递简历!


文章来源: https://bbs.pediy.com/thread-258922.htm
如有侵权请联系:admin#unsafe.sh