彻底从零开始的CTF之路 2020/11/22

HECTF 2020前端

参加的一场比赛,全程划水。算法

有好几题有思路,可是没不出来,(有的找到了半截flag,有的找到了算法却提交错误)数组

 

记录下几道有点意思的题目app

 

题目:签到函数

描述:登陆就送flagblog

 

 

 首先估计能注入,可是有个忘记密码的超连接,不如先点进去看看。游戏

 

 

 额,我先输入本身的手机号码点击发送验证码。内存

等了半天屁短信都没有。字符串

估计是个前端假按钮了。input

 

看描述是4位验证码,很明显是爆破嘛。

因而就开始爆破,可是10000数字跑完也没爆破出来。

最后回到第一个界面的。查看审查元素才发现:

 

 

 原来人家是指定的一个手机号码,

继续burp爆破,获得验证码是0233:

 

 

 随便输入一个完事。

 

 

 

密码到手。

 

 

 

 

 

 

题目:game1

描述:1024分便可拿到flag

 

 

 

这是一个贪吃蛇游戏,吃一个豆子加一分。

1024分就是长达1024的一条蛇。。。。

可尼玛就400多个格子啊,因此想玩出1024纯属作梦。

直接ida反编译吧。

 

 

 首先找到main函数:

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax
  int v4[4]; // [esp+28h] [ebp-20h]
  int v5; // [esp+38h] [ebp-10h]
  int i; // [esp+3Ch] [ebp-Ch]

  __main();
  v3 = time(0);
  srand(v3);
  init(&apple);
  initsnake();
  while ( 1 )
  {
    snakemove();
    input();
    Sleep(abs((signed int)(200.0 - (long double)score * 0.5)));
    if ( **(_DWORD **)snake == apple && *(_DWORD *)(*(_DWORD *)snake + 4) == dword_4C7018 )
    {
      for ( i = 0; i <= 3; ++i )
        v4[i] = v4[i + 1];
      ++score;
      mess();
      v5 = score;
      if ( score == 1024 )
        flag(v4);
    }
    if ( !*(_DWORD *)(*(_DWORD *)snake + 4)
      || *(_DWORD *)(*(_DWORD *)snake + 4) == 21
      || !**(_DWORD **)snake
      || **(_DWORD **)snake == 21 )
    {
      failed();
    }
  }
}

  观察可知

 if ( score == 1024 )
        flag(v4);

  这句是获得flag的语句。

运行了一个叫flag()的函数,传入了一个叫v4的数组做为参数。

查看flag():

int __cdecl flag(int *a1)
{
  int result; // eax
  signed int v2; // [esp+14h] [ebp-14h]
  signed int i; // [esp+18h] [ebp-10h]
  int v4; // [esp+1Ch] [ebp-Ch]

  v2 = strlen(_data_start__);
  for ( i = 0; i < v2; ++i )
  {
    if ( _data_start__[i] <= 96 || _data_start__[i] > 122 )
    {
      if ( _data_start__[i] <= 64 || _data_start__[i] > 90 )
        LOBYTE(v4) = _data_start__[i];
      else
        v4 = (_data_start__[i] - 65 + a1[i % 5]) % 26 + 97;
    }
    else
    {
      v4 = (_data_start__[i] - 97 + a1[i % 5]) % 26 + 65;
    }
    _data_start__[i] = v4;
  }
  gotoxy(24, 5);
  color(20);
  if ( _data_start__[0] != 72
    || _data_start__[1] != 69
    || _data_start__[2] != 67
    || _data_start__[3] != 84
    || _data_start__[4] != 70 )
  {
    result = printf("No! you're cheating!");
  }
  else
  {
    result = printf("You win! The flag is %s ", _data_start__);
  }
  return result;
}

 稍微化简一下,写成C语言版本:

int  flag(int *a1)
{
  
  char  ans[len];//这就是要输出的答案的原数组,查看内存可知是bxukv{pW1SiFW_J0_jV}
  int temp;

  for ( int i = 0; i < len; ++i )
  {
     if (ans[i] >='a' && ans[i] <= 'z' )
           temp = (ans[i] - 97 + a1[i % 5]) % 26 + 65;
       if (ans[i] >= 'A' || ans[i] <= 'Z' )
          temp = (ans[i] - 65 + a1[i % 5]) % 26 + 97;
  }

  if ( ans[0]  == 72
    & ans[1] === 69
    & ans[2] == 67
    & ans3]  == 84
    & ans[4] == 70 )
 
printf("You win! The flag is %s ",ans);
  
}

首发现要处理的原数组ans[]的内容是:

   bxukv{pW1SiFW_J0_jV}

而最后要知足

if ( ans[0] == 72 & ans[1] === 69 & ans[2] == 67 & ans3] == 84 & ans[4] == 70 )
才能输出flag(即ans[])


发现条件就是:
ans[0-5]=HECTF
前面5个字符就是本次比赛的名字。

OK,接下来看中间的处理过程:

 for ( int i = 0; i < len; ++i )
  {
     if (ans[i] >='a' && ans[i] <= 'z' )
           ans[i] = (ans[i] - 97 + a1[i % 5]) % 26 + 65;
       if (ans[i] >= 'A' || ans[i] <= 'Z' )
          ans[i] = (ans[i] - 65 + a1[i % 5]) % 26 + 97;
  }
  

 就是从第一个字符开始若是是小写就执行:

ans[i] = (ans[i] - 97 + a1[i % 5]) % 26 + 65;

若是是大写就执行:

ans[i] = (ans[i] - 65 + a1[i % 5]) % 26 + 97;

不然,ans[i]不变。

 

这个很容易推导bxukv{pW1SiFW_J0_jV}转换后的字符串。

可是其中有一个变量a1不知道。

可是原字符串是bxukv{pW1SiFW_J0_jV},转换后要以HECTF开头,

根据这5个字符就能求出a1[0-5]的值。

 

好比ans[0]=b,执行ans[0] = (ans[0] - 97 + a1[0 % 5]) % 26 + 65='H'。

可知a1[0]=6。

同理可知a[0-5]=6,7,8,9,10。

 

再将bxukv{pW1SiFW_J0_jV}带入上列规则运算就能获得flag了。

相关文章
相关标签/搜索