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 jakiejkolwiek informacji o podatnym binarnym. Atak oparty jest na następującym scenariuszu:
Wrażliwość na stosie 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) i 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 zostanie 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 wykonywać gadgetu STOP, aby potwierdzić, że poprzedni wyciągnął 6 rejestrów:
'A' * offset + canary + rbp + ADDR
Znając adres gadgetu ret2csu, można wnioskować adresy 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(<non read addr>, <non read addr>) -> awaria
strcmp(<non read addr>, <read addr>) -> awaria
strcmp(<read addr>, <non read addr>) -> awaria
strcmp(<read addr>, <read addr>) -> brak awarii
Można to sprawdzić, wywołując każdy wpis w 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(<non read addr>, <read addr>) -> awaria
b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
-> Spowoduje awarię
strcmp(<read addr>, <non read addr>) -> awaria
b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP
strcmp(<read addr>, <read addr>) -> 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.
Mając znaleziony 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. A 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:
Obecny problem polega na tym, ż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 z 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)