BROP - Blind Return Oriented Programming
Last updated
Last updated
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Celem tego ataku jest wykorzystanie ROP za pomocą przepełnienia bufora bez jakichkolwiek informacji o podatnym binarnym. Atak oparty jest na następującym scenariuszu:
Wada stosu i wiedza o tym, jak ją wywołać.
Aplikacja serwerowa, która restartuje się po awarii.
Więcej informacji na temat tych procesów można znaleźć tutaj (BF Forked & Threaded Stack Canaries) oraz tutaj (BF Addresses in the Stack).
Ten gadget zasadniczo pozwala potwierdzić, że coś interesującego zostało wykonane przez gadget ROP, ponieważ wykonanie nie spowodowało awarii. Zazwyczaj ten gadget będzie czymś, co zatrzymuje wykonanie i jest umieszczone na końcu łańcucha ROP, gdy szuka się gadgetów ROP, aby potwierdzić, że konkretny gadget ROP został wykonany.
Ta technika wykorzystuje gadget ret2csu. I to dlatego, że jeśli uzyskasz dostęp do tego gadgetu w środku jakichś instrukcji, otrzymasz gadgety do kontrolowania rsi
i rdi
:
To byłyby gadgety:
pop rsi; pop r15; ret
pop rdi; ret
Zauważ, że za pomocą tych gadgetów można kontrolować 2 argumenty funkcji do wywołania.
Zauważ również, że gadget ret2csu ma bardzo unikalny podpis, ponieważ będzie wyciągał 6 rejestrów ze stosu. Więc wysyłając łańcuch taki jak:
'A' * offset + canary + rbp + ADDR + 0xdead * 6 + STOP
Jeśli STOP jest wykonany, oznacza to zasadniczo, że adres, który wyciąga 6 rejestrów ze stosu został użyty. Lub że użyty adres był również adresem STOP.
Aby usunąć tę ostatnią opcję, wykonuje się nowy łańcuch taki jak poniżej i nie powinien on wykonać gadgetu STOP, aby potwierdzić, że poprzedni wyciągnął 6 rejestrów:
'A' * offset + canary + rbp + ADDR
Znając adres gadgetu ret2csu, można wnioskować adres gadgetów do kontrolowania rsi
i rdi
.
Tabela PLT może być przeszukiwana od 0x400000 lub z wyciekłego adresu RIP ze stosu (jeśli PIE jest używane). Wpisy tabeli są oddzielone o 16B (0x10B), a gdy wywoływana jest jedna funkcja, serwer nie zawiesza się, nawet jeśli argumenty nie są poprawne. Ponadto, sprawdzanie adresu wpisu w PLT + 6B również nie powoduje awarii, ponieważ jest to pierwszy kod wykonywany.
Dlatego można znaleźć tabelę PLT, sprawdzając następujące zachowania:
'A' * offset + canary + rbp + ADDR + STOP
-> brak awarii
'A' * offset + canary + rbp + (ADDR + 0x6) + STOP
-> brak awarii
'A' * offset + canary + rbp + (ADDR + 0x10) + STOP
-> brak awarii
Funkcja strcmp
ustawia rejestr rdx
na długość porównywanego ciągu. Zauważ, że rdx
jest trzecim argumentem i musimy, aby był większy niż 0, aby później użyć write
, aby wyciekł program.
Można znaleźć lokalizację strcmp
w PLT na podstawie jej zachowania, wykorzystując fakt, że teraz możemy kontrolować 2 pierwsze argumenty funkcji:
strcmp(<adres nieczytany>, <adres nieczytany>) -> awaria
strcmp(<adres nieczytany>, <adres czytany>) -> awaria
strcmp(<adres czytany>, <adres nieczytany>) -> awaria
strcmp(<adres czytany>, <adres czytany>) -> brak awarii
Można to sprawdzić, wywołując każdy wpis tabeli PLT lub używając wolnej ścieżki PLT, która zasadniczo polega na wywołaniu wpisu w tabeli PLT + 0xb (co wywołuje dlresolve
) a następnie w stosie przez numer wpisu, który chce się zbadać (zaczynając od zera), aby przeskanować wszystkie wpisy PLT od pierwszego:
strcmp(<adres nieczytany>, <adres czytany>) -> awaria
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
-> Spowoduje awarię
strcmp(<adres czytany>, <adres nieczytany>) -> awaria
b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
strcmp(<adres czytany>, <adres czytany>) -> brak awarii
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
Pamiętaj, że:
BROP + 0x7 wskazuje na pop RSI; pop R15; ret;
BROP + 0x9 wskazuje na pop RDI; ret;
PLT + 0xb wskazuje na wywołanie dl_resolve.
Po znalezieniu strcmp
można ustawić rdx
na wartość większą niż 0.
Zauważ, że zazwyczaj rdx
będzie już miało wartość większą niż 0, więc ten krok może nie być konieczny.
Na koniec potrzebny jest gadget, który eksfiltruje dane, aby wyeksportować binarny. I w tym momencie można kontrolować 2 argumenty i ustawić rdx
większe niż 0.
Istnieją 3 powszechne funkcje, które można wykorzystać do tego:
puts(data)
dprintf(fd, data)
write(fd, data, len(data)
Jednak oryginalny dokument wspomina tylko o funkcji write
, więc porozmawiajmy o niej:
Obecnym problemem jest to, że nie wiemy gdzie funkcja write znajduje się w PLT i nie znamy numeru fd, aby wysłać dane do naszego gniazda.
Jednak wiemy gdzie znajduje się tabela PLT i można znaleźć write na podstawie jej zachowania. Możemy stworzyć wiele połączeń z serwerem i użyć wysokiego FD, mając nadzieję, że pasuje do niektórych naszych połączeń.
Podpisy zachowań do znalezienia tych funkcji:
'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Jeśli dane są drukowane, to znaczy, że znaleziono puts
'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Jeśli dane są drukowane, to znaczy, że znaleziono dprintf
'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP
-> Jeśli dane są drukowane, to znaczy, że znaleziono write
Oryginalny dokument: https://www.scs.stanford.edu/brop/bittau-brop.pdf
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)