Skip to main content
  1. Writeups/

Let's go - Autorské řešení úlohy

·408 words·2 mins
hackrrr
Challenge author
Hackrrr
hackrrr
Writeup author
Hackrrr
Table of Contents

Solution #

Hned jednoduchou základní analýzou lze zjistit, že program by nejspíše napsán v jazyce Go. Tohle nám trochu znepříjemní život, jelikož dekompilace Go programů většinou nedopadá pěkně. Naneštěstí jiné možnosti moc nejsou… Na druhou stranu se alespoň nejedná o stripped program, takže máme alespoň nějaké informace o symbolech.

Po dekompilaci lze poměrně jednoduše najít, že se v programu vyskytuje pole čísel označované jako main.expected a delka našeho vstupu se musí rovnat délce toho pole (= 22). Dále zde máme funkci main.measure(), která bere jako vstup kanál (= Go konstrukt pro předávání hodnot), index a char (a název těchto dvou proměnných je poměrně vypovídající). Tato funkce je poměrně jednoduchá - nejdříve zjistí čas, pak se uspí na dobu závislou na char, zjistí čas znovu a výsledek je rozdíl naměřených časů vrácených přes vstupní kanál (zabalených ve struktuře main.measureOutput).

Nyní zbývá už “jen” pořádně zanalyzovat main.main(). Když se pokusíme odignorovat balast kolem a pokusíme se na to koukat z nadhledem (např. přes nějaký “graph view” v dekompilačním programu), můžeme vidět 3 smyčky, jejichž účel lze tipnout z nějakých význačných funkcí:

  • První primárně volá “interní” Go funkci runtime.newproc(), která nejspíše má za následek volání main.measure() (nepřímo přes main.main.func1(), na kterou je ve smyčce reference)
  • Ve druhé smyčce se opakovaně volá runtime.chanrecv1(), můžeme předpokládat, že přijimá data vrácená/poslaná funkcí main.measure() a ukládá je očividně do nějakého bufferu
  • Poslední cyklus obsahuje nějaké porovnávání na základě kterého modifikuje proměnnou, která pak po ukončení cyklu určuje, která zpráva o správnosti vlajky/vstupu se zobrazí; Toto porovnávání porovnává pole main.expected s hodnotami pocházejícími (nejspíše) z předchozího cyklu

Když si celého toto shrneme, můžeme tušit, že náš vstup se postupně pošle do funkce main.measure() a následně se porovná s main.expected. Není tedy nic jednoduššího než zkusit vzít tyto hodnoty a zkusit z nich rekonstruovat správný vstup - všechny hodnoty vydělíme konstantou z main.measure() kterou se násobí vstupní proměnná char (= 4500000) a převedeme znaky… To se nám ale nepodaří, protože 4500000 je moc veliké číslo a po dělení je vše 0. Znovu kouknutím do main.measure() zjistíme, že se výsledné hodnoty dělí 1000, takže chceme ve skutečnosti dělit dělitelem 4500. S tím už uspějeme a dosteneme vlajku HCKR{n0_T1m3_T0_w4ST3}.

Exploit script #

Pro úplnost přikládám i velice primitivní Python skript který spočítá správnou vlajku z extrahovaných hodnot.

expected = [
    0x4F376,
    0x49AA9,
    0x5270C,
    0x5A314,
    0x87221,
    0x78E9D,
    0x34C98,
    0x68811,
    0x5C50F,
    0x35E28,
    0x77E27,
    0x381BA,
    0x68606,
    0x5C57A,
    0x34E55,
    0x6873C,
    0x82EE5,
    0x393C9,
    0x5B3BA,
    0x5C789,
    0x38254,
    0x897DF,
]

for x in expected:
    print(chr(x // 4500), end="")