首页
社区
课程
招聘
[原创]2019看雪CTF 团队赛 第五题 青梅竹马WP
2019-3-21 16:39 3090

[原创]2019看雪CTF 团队赛 第五题 青梅竹马WP

2019-3-21 16:39
3090
打开程序,随便输入点什么,弹出'no,wrong sn!!!"。
在MessageBoxA下断,回溯到402652,用IDA分析,下面的代码我已经分析过并重命名函数了。
//省略
 GetDlgItemTextA(hDlg, 1000, &String, 3);      // String总是等于0x4F
  v38 = GetDlgItemTextA(hDlg, 1001, &input, 255);
  sub_402A90(0, &Text);
  v39 = 0;
  v41 = 0;
  if ( v38 < 12 )                               // len>12
    v41 = 1;
  if ( input == 'A' && v30 == 'A' )             // 1和2位不可以为A
    ++v41;
  if ( v31 != 'V' || v32 != 'V' )               // 6和12位等于V
    ++v41;
  for ( i = 0; i < v38; ++i )
  {
    if ( i != 5 && i != 11 )                    // 当前位置不是6和12时
...//这部分代码主要是组合input并且初始化变量,省略
 v5 = String;                                // v5=0x4F
    sub_402889(80, &v13, 0x40u);                // 生成base64table
    sub_402270(&v13);                           // 写到全局变量
    len = base64decode((unsigned __int8 *)&code, &out);// 解码输入字符串
    base64encode((unsigned __int8 *)&out, &encode, len);// 解码
    if ( out )
    {
     if ( !memcmp(&encode, &code, v39) )       // 比较编码后是否相同,相同则进入下一步
      {
        v7 = check(100, v5, (int)&out, len);
        GetText(v7, &Text);                     // base64解码后返回
      }
    }
  }
  MessageBoxA(hDlg, &Text, &Text, 0);
分析代码,可以知道flag是base64编码后在6和12位处插入'V',在解码时会将其删去。
可以在调试 base64decode或 base64encode时提取到码表ABCyVPGHTJKLMNOFQRSIUEWDYZgbc8sfah1jklmnopqret5v0xX9wi234u67dz
看下check的代码
int __cdecl check(int a1, int value, int code, int len)
{
  signed int v4; // ebx
  int *v5; // edi
  int prime_t; // [esp+Ch] [ebp-1C0h]
  int table[99]; // [esp+10h] [ebp-1BCh]
  char int2; // [esp+19Ch] [ebp-30h]
  char codeint; // [esp+1A8h] [ebp-24h]
  char result; // [esp+1B4h] [ebp-18h]
  char big_m; // [esp+1C0h] [ebp-Ch]
  int prime; // [esp+1D4h] [ebp+8h]
  int ret; // [esp+1E0h] [ebp+14h]

  prime_t = 0;
  memset(table, 0, sizeof(table));
  prime = createprime(a1, &prime_t);            // 生成质数数组
  big_init(&result);
  big_init(&int2);
  big_setvalue((int)&result, 0);
  big_setvalue((int)&codeint, 0);
  v4 = 1;
  big_setvalue((int)&big_m, 1);
  big_setvalue((int)&int2, prime_t);            // 第一位是2
  big_fromint((int)&codeint, code, len);        // 将code转换为bignum类型
  ret = big_toint((int)&result);
  if ( prime > 1 )
  {
    v5 = table;                                 // table的值是prime的指针+1
    while ( *v5 != value )                      // value=0x4F
    {
      if ( *v5 )
        sub_401B0A((int)&big_m, (int)&big_m, *v5);// 乘以table所有成员,结果在big_m,结果是0x41CD66ACC237B22681A18067
      ++v4;
      ++v5;
      if ( v4 >= prime )
        goto LABEL_9;
    }
    prime = table[v4];                          // 取得value所在位置后一位的成员,在这个程序里一直等于0x53
  }
LABEL_9:
  if ( big_cmp(&codeint, &int2) >= 0 && big_cmp(&codeint, &big_m) <= 0 )
  {
    codevaild((int)&result, (int)&codeint, prime, (int)&big_m);
    if ( big_cmp(&result, &int2) >= 0 && big_cmp(&result, &big_m) <= 0 )// result为2
      ret = big_toint((int)&result);
  }
  free((int)&result);
  free((int)&int2);
  return ret;
}
看过GetText就知道,这个函数的返回值必须为2
if ( a1 )
  {
    if ( a1 == 1 )
    {
      v2 = aBwzxzsb3b3jrth;                     // more work to do!
    }
    else if ( a1 == 2 )
    {
      v2 = aSwe9lcbjb3jxzw;                     // yes, correct sn!
    }
    else
    {
      v2 = aC24azgzlc24n8c;                     // sn doesn't work!
    }
  }
  else
  {
    v2 = aBmdeth8xb2unth;                       // no, wrong sn!!!!
  }
我们继续跟进codevaild
int __cdecl codevaild(int a1, int big_code, unsigned int a3, int big_m)
{
  unsigned int i; // ebx
  char ret; // [esp+4h] [ebp-24h]
  char mulret; // [esp+10h] [ebp-18h]
  char tmp; // [esp+1Ch] [ebp-Ch]

  big_init(&ret);
  mod(&tmp, big_code, (_DWORD *)big_m);         // 取模,结果给tmp
  big_setvalue((int)&ret, 1);
  for ( i = a3; i; i >>= 1 )                    // i=0x53
  {
    if ( i & 1 )
    {
      mul_0(&mulret, &ret, &tmp);               // 相乘
      mod(&ret, (int)&mulret, (_DWORD *)big_m);
    }
    mul_0(&mulret, &tmp, &tmp);
    mod(&tmp, (int)&mulret, (_DWORD *)big_m);
  }
  copy((_DWORD *)a1, (int)&ret);
  free((int)&ret);
  return 0;
}
将过程列成数学表达式
ret1=(code*ret)%0x41CD66ACC237B22681A18067 //code^1
code1=(code*code)%0x41CD66ACC237B22681A18067 //2
ret2=(code1*ret1)%0x41CD66ACC237B22681A18067 //3
code2=(code1*code1)%0x41CD66ACC237B22681A18067 //4
code3=(code2*code2)%0x41CD66ACC237B22681A18067 //8
code4=(code3*code3)%0x41CD66ACC237B22681A18067 //16
ret5=(code4*ret2)%0x41CD66ACC237B22681A18067 //19
code5=(code4*code4)%0x41CD66ACC237B22681A18067 //32
code6=(code5*code5)%0x41CD66ACC237B22681A18067 //64
ret7=(code6*ret5)%0x41CD66ACC237B22681A18067 //83
ret7=2
可以把表达式整理成(flag^83) = 2 mod  0x41CD66ACC237B22681A18067
求解该高次剩余问题就能得到flag,下面用P代称 0x41CD66ACC237B22681A18067
一般高次剩余问题中P是质数,但是这个地方不同,P是合数,P是check函数的质数数组{0x03,0x05,0x07,0x0B,0x0D,0x11,0x13,0x17,0x1D,0x1F,0x25,0x29,0x2B,0x2F,0x35,0x3B,0x3D,0x43,0x47,0x49}相乘得到的,所以需要先质数分解P,
求解flag1^83 = 2 mod 3,flag2^83 = 2 mod 5, flagn^83 = 2 mod Pn ,再用中国剩余定理合并flagn。
求解高次剩余的文章和代码网上有很多,后面我会附上链接,可以解得flagn={2,3,4,7,7,8,13,4,15,4,5,5,22,32,5,10,17,63,38,32}
接下来就是用中国剩余定理合并,网上找的代码大多数都是int64,没办法直接用在这里,所以用python改了改
def egcd(a, b):
##    扩展欧几里得
    if 0 == b:
        return 1, 0, a
    x, y, q = egcd(b, a % b)
    x, y = y, (x - a // b * y)
    return x, y, q
def CRT(r1,m1,r2,m2):
##     中国剩余定理
    z=m1*m2
    t=egcd(m1,m2)
    x=t[0]
    y=t[1]
    return ((x%z*(r2-r1)%z*m1+r1)%z+z)%z
def dfs(p,r,mo):
    k=[2,3,4,7,7,8,13,4,15,4,5,5,22,32,5,10,17,63,38,32]
    m=[0x03,0x05,0x07,0x0B,0x0D,0x11,0x13,0x17,0x1D,0x1F,0x25,0x29,0x2B,0x2F,0x35,0x3B,0x3D,0x43,0x47,0x49]
    if(p+1==len(k)):
        print('code :',hex(r),'mod P = 2')
        print('P:',hex(mo))
        return
    _mo=m[p+1]
    dfs(p+1,CRT(r,mo,k[p+1],_mo),mo*_mo)
if __name__=='__main__':
    dfs(0,2,3)#k[0],m[0]
输出:
('code :', '0x1555d30f38b0dbcaec83c0f9L', 'mod P = 2')
('P:', '0x41cd66acc237b22681a18067L')
0x1555d30f38b0dbcaec83c0f9 这个就是结果了,用提取的码表base64编码后得到"PEDIy9102dreadyu"。在6和12位加上'V',得到最终flag:
PEDIyV9102dVreadyu
搞定收工。
参考文章和代码:

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

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