Skip to content
Snippets Groups Projects
Commit 02808914 authored by Gelencsér Ákos's avatar Gelencsér Ákos :alien:
Browse files

add ctf-2023 cheatsheets

parent 79f0a877
No related branches found
No related tags found
1 merge request!19add ctf-2023 cheatsheets
---
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
---
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
---
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
{
"label": "CTF feladatok",
"position": 4
}
......@@ -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",
},
],
},
],
},
],
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment