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