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了。