HECTF 2020
參加的一場比賽,全程劃水,
有好幾題有思路,但是沒不出來,(有的找到了半截flag,有的找到了演算法卻提交錯誤)
記錄下幾道有點意思的題目
題目:簽到
描述:登錄就送flag

首先估計能注入,但是有個忘記密碼的超鏈接,不如先點進去看看,

額,我先輸入自己的手機號碼點擊發送驗證碼,
等了半天屁短信都沒有,
估計是個前端假按鈕了,
看描述是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了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/226466.html
標籤:其他
上一篇:【轉載&翻譯】如何在CentOS8/RHEL8上安裝Linux Nginx MariaDB PHP(LEMP)
下一篇:程式猿不得不知的購房那些事兒
