SELinux - Heise Workshop Juni 2024
1 Agenda und Folien
1.1 SELinux Workshop Agenda
1.1.1 Tag 1
- 09:00 Begrüssung
- 09:20 SELinux und Linux Security Module
- 11:00 Linux Audit Subsystem
- 12:30 Mittagspause
- 13:30 SELinux Label und Context
- 15:00 SELinux entdecken
- 17:00 Ende Tag 1
1.1.2 Tag 2
- 09:00 SELinux Type Enforcement
- 11:30 SELinux Fehler erkennen und beheben
- 12:30 Mittagspause
- 13:30 SELinux Richtlinien entwickeln
- 16:30 SELinux - Offene Fragen / Q&A
- 17:00 Ende
2 Allgemeine Informationen
- Diese Anleitung und die Folien können online unter https://linux-sicherheit.org abgerufen werden
- Bitte ersetzen Sie die Platzhalterzeichen
NNN
in der Anleitung durch Ihre Teilnehmernummer. Sie finden Ihre Teilnehmernummer in der Tabelle auf dieser Seite - Wenn nicht anders angegeben beziehen sich alle Konfigurations- und Kommando-Beispiele auf ein Red Hat Linux kompatibles System
3 Hostnamen, Benutzer und Passwörter
Anmeldung über SSH (OpenSSH, Putty, Google Chrome mit SSH-App) oder mit Browser an Port 443 (HTTPS) auf dem Server (z.B. https://selinux001.linux-sicherheit.org). Wählen Sie das Terminal auf der linken Seite des Cockpit-Webadministrationstools (wir benutzen in diesem Kurs keine der anderen Funktionen der Cockpit Web UI).
Leider meldet die Cockpit Web-Console bei Red Hat EL 9 kompatiblen Systemen derzeit, das ein aktueller Firefox Browser "zu alt" wäre. Dies ist ein Fehler, welcher noch nicht in diesem System gefixed wurde ("upstream" ist der Fehler schon behoben). Wenn dieser Fehler auftritt, dann bitte einen alternativen Browser oder den direkten SSH-Zugang benutzen.
- Benutzername für die VMs:
user
- Kennwort:
selinux2023
- Root-Shell mit
sudo -s
und dem Benutzerpasswort
Benutzernummer | Name | SSH-Zugriff | Web-Zugriff |
---|---|---|---|
001 | selinux001.linux-sicherheit.org | https://selinux001.linux-sicherheit.org | |
002 | selinux002.linux-sicherheit.org | https://selinux002.linux-sicherheit.org | |
003 | selinux003.linux-sicherheit.org | https://selinux003.linux-sicherheit.org | |
004 | selinux004.linux-sicherheit.org | https://selinux004.linux-sicherheit.org | |
005 | selinux005.linux-sicherheit.org | https://selinux005.linux-sicherheit.org | |
006 | selinux006.linux-sicherheit.org | https://selinux006.linux-sicherheit.org | |
007 | selinux007.linux-sicherheit.org | https://selinux007.linux-sicherheit.org | |
008 | selinux008.linux-sicherheit.org | https://selinux008.linux-sicherheit.org | |
009 | selinux009.linux-sicherheit.org | https://selinux009.linux-sicherheit.org | |
010 | selinux010.linux-sicherheit.org | https://selinux010.linux-sicherheit.org | |
011 | selinux011.linux-sicherheit.org | https://selinux011.linux-sicherheit.org | |
012 | selinux012.linux-sicherheit.org | https://selinux012.linux-sicherheit.org | |
013 | selinux013.linux-sicherheit.org | https://selinux013.linux-sicherheit.org |
4 Linux Security Modules und SELinux
5 Audit Subsystem
5.1 Audit Log-Daemon Installation
- Wir arbeiten auf den virtuellen Maschinen im Internet
- Audit-Subsystem Programme installieren. Das Paket
audit
ist in der Regel schon vorinstalliert. Das Paketaudispd-plugins
enthält die Plugins des Audit-Dispatchers
dnf install audit audispd-plugins
5.2 Konfiguration
- Passe die Audit-Daemon Konfiguration an. Benutze hierfür die Informationen aus den Folien
$EDITOR /etc/audit/auditd.conf
- Audit-Dämon anschalten (so dass er bei einem Restart neu gestartet wird) und prüfen das der Prozess läuft
systemctl enable --now auditd systemctl status auditd journalctl -u auditd
- Dem Audit-Daemon ein "Hangup (HUP)" Signal senden um die Konfiguration neu zu laden
pkill -HUP auditd
- Audit-Daemon starten und stoppen
service auditd stop service auditd start
- Red Hat Bug-Report: Unable to restart/stop auditd service using systemctl command in RHEL7: https://access.redhat.com/solutions/2664811
5.3 Ad-Hoc Trace von Programmen
- Starte einen Audit-Subsystem Trace eines Prozesses (hier 4 x ICMP
Echo per
ping
)
autrace /bin/ping -c 4 8.8.8.8
- Audit Log für einen speziellen
autrace
Aufruf anschauen. Die genaue Kommandozeile wurde vomautrace
Programm ausgegeben.
ausearch -i -p <audit-id>
- Die Log-Ausgaben können über eine Shell-Pipeline ausgewertet
werden. In diesem Beispiel werden alle System-Calls des
autrace
Aufruf aufgelistet und nach Häufigkeit sortiert ausgeben:
ausearch -i -p <audit-id> | grep type=SYSCALL | cut -f 6 -d ' ' | sort | uniq -c | sort -n
- Wurde der Hostname als Metadaten bei den Audit-Meldungen mit
ausgegeben Konfiguration des Audit-Daemon), so muss per
cut
Befehl das Feld #7 ausgewertet werden (...| cut -f 7 -d ' ' |...
)
5.4 Richtlinien-Dateien
- Die vorinstallierte (leere) Richtlinien-Datei löschen
rm /etc/audit/rules.d/audit.rules
- Audit-Richtlinien Beispiele befinden sich unter
/usr/share/audit/sample-rules
- Richtliniendateien aus den Beispielen in das Verzeichnis für die Audit-Regeln kopieren
cp /usr/share/audit/sample-rules/10-base-config.rules /etc/audit/rules.d/ cp /usr/share/audit/sample-rules/11-loginuid.rules /etc/audit/rules.d/ cp /usr/share/audit/sample-rules/30-stig.rules /etc/audit/rules.d/ cp /usr/share/audit/sample-rules/32-power-abuse.rules /etc/audit/rules.d/ cp /usr/share/audit/sample-rules/42-injection.rules /etc/audit/rules.d/ cp /usr/share/audit/sample-rules/43-module-load.rules /etc/audit/rules.d/ cp /usr/share/audit/sample-rules/70-einval.rules /etc/audit/rules.d/ cp /usr/share/audit/sample-rules/71-networking.rules /etc/audit/rules.d/ cp /usr/share/audit/sample-rules/99-finalize.rules /etc/audit/rules.d/
- Richtlinien für priviligierte Programme erstellen
cd /etc/audit/rules.d/ find /bin -type f -perm -04000 2>/dev/null | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $1 }' > 31-privileged.rules find /sbin -type f -perm -04000 2>/dev/null | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $1 }' >> 31-privileged.rules find /usr/bin -type f -perm -04000 2>/dev/null | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $1 }' >> 31-privileged.rules find /usr/sbin -type f -perm -04000 2>/dev/null | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $1 }' >> 31-privileged.rules filecap /bin 2>/dev/null | sed '1d' | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $2 }' >> 31-privileged.rules filecap /sbin 2>/dev/null | sed '1d' | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $2 }' >> 31-privileged.rules filecap /usr/bin 2>/dev/null | sed '1d' | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $2 }' >> 31-privileged.rules filecap /usr/sbin 2>/dev/null | sed '1d' | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $2 }' >> 31-privileged.rules
- Die ursprüngliche Audit-Richtlinien-Datei anschauen
less /etc/audit/audit.rules
- Die Richtlinie kompilieren und prüfen
augenrules --check
- Die neue Policydatei in den Kernel laden
augenrules --load
- Die kombinierte Audit-Richtlinien-Datei anschauen
less /etc/audit/audit.rules
- Aktive Audit-Regeln auflisten
auditctl -l
- Status des Audit-Subsystems anzeigen
# auditctl -s enabled 2 failure 1 pid 12901 rate_limit 0 backlog_limit 8192 lost 0 backlog 0 backlog_wait_time 0 loginuid_immutable 0 unlocked
5.5 Beispiele für Audit-Abfragen
- Alle Audit-Einträge zum Thema "sudo" zeigen
ausearch -i -x sudo
- Report über fehlgeschlagende Anmeldeversuche
ausearch -m USER_AUTH,USER_ACCT --success no
- Alle Audit-Meldungen für Benutzer UID 1000
ausearch -ua 1000 -i
- Fehlgeschlagende Syscalls seit gestern
ausearch --start yesterday --end now -m SYSCALL -sv no -i
5.6 Aufgabe:
- Das Audit-Subsystem um neue Regel(n) zu Systemd erweitern:
- Alle Systemd-Konfigurationsdateien mit der Endung
*.conf
, welche direkt unter/etc/systemd
(nicht in den Unterverzeichnissen) gespeichert sind, sollen auf Schreibzugriffe überwacht werden - Die Audit-Events sollen mit dem Key
systemd
markiert werden - Die neue Richtlinie kompilieren und aktivieren
- Die neue Richtlinie testen, in dem manuell an einer der überwachten Dateien eine Änderung vorgenommen wird
- Die Audit-Meldungen per
ausearch
anzeigen und analysieren (suchen nach Events mit dem Keysystemd
)
- Alle Systemd-Konfigurationsdateien mit der Endung
5.7 Beispiel-Lösung
- Erweiterung der Audit-Regeln
[...] # Systemd Konfiguration -w /etc/systemd/journald.conf -p wa -k systemd -w /etc/systemd/logind.conf -p wa -k systemd -w /etc/systemd/networkd.conf -p wa -k systemd -w /etc/systemd/resolved.conf -p wa -k systemd -w /etc/systemd/sleep.conf -p wa -k systemd -w /etc/systemd/system.conf -p wa -k systemd -w /etc/systemd/timesyncd.conf -p wa -k systemd -w /etc/systemd/user.conf -p wa -k systemd [...]
- Suche nach Audit-Events mit dem Schlüssel
systemd
ausearch -i -k systemd
6 SELinux
6.1 SELinux erkunden
- SELinux Hilfspakete installieren
dnf install policycoreutils setools libselinux-utils selinux-policy-doc setools-console dnf install policycoreutils-python3 selinux-policy-devel policycoreutils-newrole
6.1.1 SELinux Label auf Dateien
- SELinux Label (Context) auf Dateien anzeigen
ls -lZ <pfad>
- Welche Dateien/Verzeichnisse unter
/etc
sind vom SELinux Typsystem_conf_t
? - Welchen SELinux Type haben neue Dateien im Heimverzeichnis des
Benutzers
user
(ggf. eine Datei neu anlegen)? - Erstelle eine sortierte Liste der SELinux Datei-Typen (3tes Feld
im SELinux Label user:role:type) im Verzeichnis
/usr/sbin
.
- Lösungen
- Frage 1:
# ls -lZ /etc | grep system_conf_t -rw-r--r--. 1 root root system_u:object_r:system_conf_t:s0 449 Jan 23 20:06 sysctl.conf drwxr-xr-x. 2 root root system_u:object_r:system_conf_t:s0 124 Dec 13 06:15 yum.repos.d
- Frage 2:
[root@selinux016 user]# touch /home/user/test [root@selinux016 user]# ls -lZ /home/user/test -rw-r--r--. 1 root root unconfined_u:object_r:user_home_t:s0 0 Jan 31 07:39 /home/user/test
- Frage 3:
ls -lZ /usr/sbin/ | cut -f 3 -d ':' | sort | uniq -c | sort -n
6.1.2 SELinux Label auf Prozessen
- SELinux Label auf Prozessen anzeigen
ps -auxZ
6.1.3 SELinux Label auf dem aktuellen Benutzer anzeigen
# id -Z
6.1.4 SELinux Status abfragen
- Allgemeinen SELinux Status abfragen
sestatus
- Detaillierten SELinux Status abfragen
sestatus -v
- SELinux "enforcement" Status anzeigen
getenforce
- Verfügbare SELinux Module auflisten
semodule -l | less
- Die kompilierte (binäre) Richtlinien (Policy) Datei
ls -lh /etc/selinux/targeted/policy/
- Statistiken über den Access-Vector-Cache (AVC)
# avcstat lookups hits misses allocs reclaims frees 797921406 797847473 73933 73933 71040 73429
6.2 SELinux Policy Konfiguration anpassen
6.3 SELinux Funktions-Beispiel
- SELinux in den enforcing Modus schalten
setenforce 1
- Apache Webserver installieren und starten
dnf install httpd systemctl enable --now httpd
- Kleine Webseite mit einem Editor anlegen ($EDITOR durch
vi
,vim
,nano
,emacs
etc ersetzen)
$EDITOR /var/www/html/index.html
- Inhalt der HTML-Datei
/var/www/html/index.html
(Vorschlag)
<html> <body> <h1>Apache Webserver</h1> </body> </html>
- SELinux Security Context auf der Datei anzeigen
ls -lZ /var/www/html/index.html
- Port 80 in der Firewall erlauben
# firewall-cmd --zone=public --add-service=http --permanent # firewall-cmd --reload
- Die Webseite sollte nun unter Port 80 (http, nicht https) mittels eines Webbrowsers abrufbar sein
6.3.1 Ein SELinux-Problem für den Apache Webserver erzeugen
- Eine Datei
index.html
Datei im Heim-Verzeichnis des Benutzersroot
erstellen und in das Apache-WWW-Verzeichnis verschieben (nicht kopieren):rm /var/www/html/index.html $EDITOR /root/index.html mv /root/index.html /var/www/html/index.html ls -lZ /var/www/html/index.html
- Apache sollte nun nicht mehr in der Lage sein, die HTML-Datei auszuliefern (Default-Apache 2 Webseite erscheint)
- SELinux LSM-Meldungen im Audit-Log zum Prozess
httpd
anzeigen (Modulavc
= Access Vector Cache)ausearch -m avc -ts recent -c httpd -i
- SELinux Sicherheits-Kontext der Datei prüfen
matchpathcon -V /var/www/html/index.html
- Es wird angezeigt das der SELinux Sicherheits-Kontext der Datei nicht korrekt ist. SELinux blockiert daher die Zugriffe vom Apache Webserver auf diese Datei
6.3.2 Das SELinux Problem mit dem Apache Webserver lösen
- SELinux Security Context für Apache-Dateien anzeigen
sesearch --allow --source httpd_t --target httpd_sys_content_t --class file
- SELinux Sicherheits-Kontext anpassen (manuell mit dem Befehl
chcon
(Change Context)chcon --type httpd_sys_content_t /var/www/html/index.html
- (Alternativ) SELinux Sicherheits-Kontext aus der SELinux Policy
angleichen
restorecon -v /var/www/html/index.html
6.4 Aufgabe: BIND 9 Zonendatei in ungewöhnlichem Verzeichnis
- In dieser Aufgabe wollen wir die BIND 9 Zonendateien aus operativen
Gründen unterhalb des
/srv/
Filesystem-Pfad speichern - In dieser Aufgabe arbeiten wir mit dem BIND 9 DNS
Server. Installation des BIND 9 Servers
# dnf install bind
- Starte den BIND 9 DNS Server
# systemctl enable --now named
- Erstellen Sie das Verzeichnis
/srv/bind/zones/primary/
% mkdir -p /srv/bind/zones/primary
- Erstellen Sie eine Zonendatei mit einem Editor (z.B.
vi
) für die Zoneexample.net
in diesem neuen Verzeichnis$ttl 3600 @ IN SOA selinuxNNN.linux-sicherheit.org. . 1001 2h 1h 40d 1h IN NS selinuxNNN.linux-sicherheit.org. IN A 1.2.3.4
- Erstellen Sie einen neuen Zonen-Block ür die neue primäre Zone in
der BIND 9 Konfigurationsdatei
/etc/named.conf
zone "example.net" { type master; file "/srv/bind/zones/primary/example.net"; };
- Prüfe die Konfiguration (
named-checkconf
), lade die Konfiguration in den BIND 9 DNS Server (rndc reload
) und prüfe den Zonen-Status der Zoneexample.net
% named-checkconf -z % rndc reload % rndc zonestatus example.net
- Die Zone wurde nicht geladen
- Suchen Sie mittels
journalctl
undausearch
nach den Gründen% journalctl -eu named % ausearch -m avc -x /usr/sbin/named -i
- Wie kann dieses SELinux Problem gelöst werden?
6.4.1 Lösung
- SELinux Security Context Type-Enforcement Definitionen für BIND 9-Dateien anzeigen
sesearch --allow --source named_t --class file
- Aus der Liste schaut
named_zone_t
vielversprechend aus - Ändern Sie die SELinux Policy für den BIND 9 Server und übernehmen
Sie das neue Verzeichnis als ein Verzeichnis für BIND 9 Zonendateien
% semanage fcontext -a -t named_zone_t --ftype f "/srv/bind/zones(/.*)?" % semanage fcontext -a -t named_zone_t --ftype d "/srv/bind/zones(/.*)?"
- Wenden Sie die neuen SELinux Datei-Label auf die existierenden
Zonendateien an
% restorecon -rv /srv/bind/zones/ Relabeled /srv/bind/zones from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:named_zone_t:s0 Relabeled /srv/bind/zones/primary from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:named_zone_t:s0 Relabeled /srv/bind/zones/primary/example.net from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:named_zone_t:s0
- Danach den BIND 9 DNS Server neu starten (oder neu laden)
systemctl restart named
- Die Zone sollte nun korrekt geladen sein
rndc zonestatus example.net
6.5 SELinux Richtlinien über Schalter konfigurieren am Beispiel BIND 9
- In dieser Aufgabe arbeiten wir mit dem BIND 9 DNS
Server. Installation des BIND 9 Servers
# dnf install bind
- Starte den BIND 9 DNS Server
# systemctl enable --now named
- Der BIND 9 DNS kann interne Statistiken über das HTTP Protokoll ausliefern. Der Standardport für diese Funktion ist 8053.
- In der Datei
/etc/named.conf
füge die folgende Definition des Statistik-Channels hinzu (am Beginn der Konfigurationsdatei)statistics-channels { inet * port 8053 allow { any; }; };
- Für Produktions-Installationen sollte statt
any
der Zugriff auf diesen Dienst per IP-Adressen ACL beschränkt werden - Die Konfiguration prüfen und den BIND 9 DNS Server neu starten
# named-checkconf # systemctl restart named
- Im Journal-Log des BIND 9 DNS Servers sollte eine Meldung
auftauchen, das der Port 8053 nicht geöffnet werden konnte
# journalctl -u named [...] Oct 17 18:08:45 selinux22 named[34204]: /etc/named.conf:11: couldn't allocate statistics channel 0.0.0.0#8053: permission denied
- Der Fehler im Audit-Log
# ausearch -m avc -ts recent -i ---- type=PROCTITLE msg=audit(09/17/2021 04:06:05.836:2278) : proctitle=/usr/sbin/named -u named -c /etc/nam type=SYSCALL msg=audit(09/17/2021 04:06:05.836:2278) : arch=x86_64 syscall=bind success=no exit=EACCES(Permission denied) a0=0x15 a1=0x7f5183d24660 a2=0x10 a3=0x7f5183d244fc items=0 ppid=111613 pid=111615 auid=unset uid=named gid=named euid=named suid=named fsuid=named egid=named sgid=named fsgid=named tty=(none) ses=unset comm=isc-worker0000 exe=/usr/sbin/named subj=system_u:system_r:named_t:s0 key=(null) type=AVC msg=audit(09/17/2021 04:06:05.836:2278) : avc: denied { name_bind } for pid=111615 comm=isc-worker0000 src=8053 scontext=system_u:system_r:named_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=0
6.5.1 Setzen des Boolean, um BIND 9 den Zugriff auf die http-Ports zu erlauben
- Suche mit
sesearch
nach Berechtigungen von Prozessen mit Labelnamed_t
:# sesearch --allow --source named_t --class tcp_socket
- Das Umschalten des SELinux-Boleans
named_tcp_bind_http_port
erlaubt den Zugriff auf Ports, die für den Typhttp_port_t
definiert sind# setsebool named_tcp_bind_http_port=on
- Aber Port 8053 ist nicht unter diesen Ports 😕
# semanage port -l | grep http_port_t http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
- Lösung 1: Benutze einen Port für den Statistik-Channel, welcher in
der SELinux Policy für
http_port_t
erlaubt ist. Beispiel: In dernamed.conf
port8008
:statistics-channels { inet * port 8008 allow { any; }; };
- Lösung 2: Füge den BIND 9 Statistik-Port
8053
der Liste der Port fürhttp_port_t
hinzu:# semanage port -a -t http_port_t -p tcp 8053 # semanage port -l | grep http_port_t http_port_t tcp 8053, 80, 81, 443, 488, 8008, 8009, 8443, 9000
- Danach den BIND 9 DNS Server neu laden
systemctl restart named
- Die BIND 9 Statistik-Webseite sollte nun unter dem Namen der
virtuellen Maschine
selinuxNNN.linux-sicherheit.org
und der Angabe des korrekten Ports in der URL (8008
oder8053
, je nach Lösung) mit einem Web-Browser abrufbar sein (ggf. muss noch in der Firewall der Port freigeschaltet werden – siehe Beispiel oben)
6.6 Aufgabe: Eine dynamische DNS Zone
- Erstelle auf der VM eine neue DNS Zonendatei für den DNS Server
unter
/var/named/example.org
. Der Inhalt dieser Datei (NNN
durch die eigene Teilnehmer-Nummer ersetzen):
$ttl 3600 @ IN SOA selinuxNNN.linux-sicherheit.org. . 1001 2h 1h 40d 1h IN NS selinuxNNN.linux-sicherheit.org. IN A 1.2.3.4
- Prüfe den SELinux Sicherheit Kontext auf der neuen Zonendatei:
% ls -Z /var/named/example.org unconfined_u:object_r:named_zone_t:s0 /var/named/example.org
- Erstelle eine neue Zonen-Definition in der BIND 9
Konfigurations-Datei
/etc/named.conf
zone "example.org" { type master; file "example.org"; };
- Prüfe die neue Konfiguration und lade die Konfiguration in den BIND 9 DNS Server
% named-checkconf -z % rndc reconfig % rndc zonestatus example.org
- Editiere die BIND 9 Konfigurationsdatei
/etc/named.conf
und verwandle die Zoneexample.com
in eine dynamische Zone (eine Zone welche nicht mehr per Editor, sondern über das DNS Protokoll aus dem Netzwerk verändert wird):
zone "example.org" { type master; file "example.org"; update-policy local; };
- Prüfe die Konfiguration mit
named-checkconf -z
, lade die Konfiguration und prüfe den Status der Zone mittelsrndc zonestatus
. Die Zone sollte nun als eine dynamsiche Zone angezeigt werden. - Erzeuge einen neuen IPv6-Adressen-Eintrag in der Zone (mittels
nsupdate
Programm):
% nsupdate -l > ttl 3600 > add example.org in aaaa 2001:db8::1 > send > quit
- Die Änderungen der Datei werden erst mit einer Verzögerung
(max 15. Minuten) vom BIND 9 DNS Server in das Dateisystem
geschrieben. Mit dem Befehl
rndc sync
kann das Schreiben der Änderungen erzwungen werden:
# rndc sync
- Das dynamische Update schlägt fehl. Warum?
- Prüfe das BIND 9 Log:
% journalctl -eu named
- Prüfe das Audit-Log:
% ausearch -m avc -x /usr/sbin/named -i
- Wie können wir dieses Problem lösen?
6.6.1 Lösung 1
- Verschiebe die Zonen-Datei in das Verzeichnis
/var/named/dynamic
(in diesem Verzeichnis darf der BIND 9 Nameserver Dateien schreiben) und passe den SELinux Datei-Kontext (und die Unix-Berechntigungen) an:
% mv example.org dynamic/ % chown named: dynamic/example.org % restorecon -vr dynamic/ Relabeled /var/named/dynamic/example.org from unconfined_u:object_r:named_zone_t:s0 to unconfined_u:object_r:named_cache_t:s0
- Passe die BIND 9 Konfiguration an:
zone "example.org" { type master; file "dynamic/example.org"; update-policy local; };
6.6.2 Lösung 2
- Setze den SELinux Boolean Schalter
named_write_master_zones
aufon
% setsebool named_write_master_zones=on
6.7 Aufgabe: Apache auf einem anderen Port als 80 oder 443
- In dieser Aufgabe soll ein Apache 2 Webserver installiert werden. Aus operativen Gründen muss dieser Webserver auf Port 1235 auf Anfragen von Browser-Clients horchen
- Den Apache 2 Webserver installieren und starten
# dnf install httpd # systemctl enable --now httpd # systemctl status httpd
- Ändere die Apache Konfiguration unter
/etc/httpd/conf/httpd.conf
so das der Apache auf Port 1235/TCP auf HTTP-Anfragen horcht (KonfigurationsparameterListen: 1235
) - Versuche den Apache Webserver mittels systemd neu zu starten
(
systemctl restart httpd
). Dies wird fehlschlagen, dahttpd
sich nicht auf Port 1235 binden kann. - Schaue in per
ausearch
in die Audit-Logdatei nach SELinux Fehlern des Prozesseshttpd
ausearch -i -m avc -c httpd
- Erlaubte Ports für den Apache HTTP auflisten
semanage port -l | grep http_port_t
- Benutze den Befehl
semanage port
um den Port1235
für den Prozesshttpd
im SELinux freizuschalten und starte den Apache Webserver neu. - Port
1235
in der Firewall freischalten# firewall-cmd --zone=public --add-port=1235/tcp --permanent # firewall-cmd --reload
- Teste, ob der Firefox-Browser unter der URL http://selinuxNNN.linux-sicherheit.org:1235/ die Webseite des Webservers sehen kann.
- (Alternativ): Teste, ob die Webseite mit
curl
von der VM aus abrufbar ist:curl http://selinuxNNN.linux-sicherheit.org:1235/
- Beispiellösung
- Apache Konfiguration bearbeiten
$EDITOR /etc/httpd/conf/httpd.conf
- Apache Prozess neu starten. Dies sollte fehlschlagen, da SELinux die
Benutzung von Port 1235 für den Prozess
httpd
verbietet - SELinux Fehler für
httpd
im Audit-Log suchen
ausearch -m avc -c httpd -ts recent -i
- Port 1235 für Apache in der SELinux Richtlinie hinzufügen
semanage port -a -t http_port_t -p tcp 1235
- Neue Portdefinition prüfen
semanage port -l | grep http_port_t
- Apache Webserver neu starten
systemctl restart httpd
- Port 1235 in der Firewall freischalten
firewall-cmd --zone=public --add-port=1235/tcp --permanent firewall-cmd --reload
- Änderungen mit
semanage
sind persistent, die Port-Änderung ist unter/var/lib/selinux/targeted/active/ports.local
abgespeichert:
# This file is auto-generated by libsemanage # Do not edit directly. portcon tcp 1235 system_u:object_r:http_port_t:s0
6.8 SELinux und Benutzer
6.8.1 SELinux Benutzer zuweisen
- Derzeitige Zuweisungen von SELinux Benutzern anzeigen
# semanage login -l
- Verfügbare SELinux Benutzer und Rollen anzeigen
# semanage user -l
- Benutzer
user
in die Benutzerklasseuser_u
einfügen.semanage login -a -s user_u user
- Neu als Benutzer
user
anmelden (z.B. über SSH oder Cockpit) und ein Terminal öffnen - Die Befehle
su
odersudo
zur Ausweitung von Rechten sollten nun für den Benutzeruser
nicht mehr möglich sein - Die Benutzerzuweisungen wurden in
/etc/selinux/targeted/seusers
gesichert:# cat /etc/selinux/targeted/seusers
- Ein Shellscript
hello.sh
im Heimverzeichnis des Benutzersuser
erstellen und ausführbar machen$EDITOR /home/user/hello.sh chmod +x /home/user/hello.sh
- Inhalt des Shell-Scripts
#!/bin/sh echo "Hallo!"
- Testen, das dieses Shell-Skript direkt durch Aufruf der Datei
ausgeführt werden kann
# /home/cas/hello.sh Hallo!
- In einer neuen Sitzung als
root
den Benutzeruser
in die SELinux-Benutzer Gruppeguest_u
aufnehmen (Gäste haben im SELinux System weniger Rechte als reguläre Benutzer) und die direkte Ausführung von Skripten unterbinden (über einen SELinux Schalter (Boolean) ausschalten)semanage login -m -s guest_u user getsebool allow_guest_exec_content setsebool allow_guest_exec_content off
- Versuchen als Benutzer
user
das Shell-Skritp auszuführen. Shell-Skripte sind nun nicht mehr direkt ausführbar - Die SELinux Meldungen (avc) im Audit Log anschauen
ausearch -m avc -ts recent
- Welche Möglichkeit(en) gibt es für Benutzer
user
, trotz der SELinux Beschränkung Shell-Skripte auszuführen?
6.9 SELinux - Policy Development
- Für dieses Kapitel arbeiten wir auf dem VM Maschinen
- Andere Web-Server (Apache/NGINX etc) auf der VM stoppen
- Für die Dauer dieser Übung die Firewall auf der VM deaktivieren
systemctl stop firewalld systemctl stop httpd
6.9.1 SELinux Policy erstellen
- In diesem Kapitel werden wir eine SELinux Richtlinie (Policy) für
einen Dienst entwickeln, welcher bisher noch nicht durch SELinux
geschützt ist
- Dies kann in der Praxis für Software notwendig werden, welche von externen Entwicklern geliefert wurde oder im eigenen Hause entwickelt wird
- Unsere Beispiel-Anwendung ist ein sehr einfacher Webserver
- Um die Beispiel-Anwendung aus dem Quellcode übersetzen zu können installieren wir den GCC C-Compiler
dnf install gcc
- Für die Entwicklung neuer SELinux Richtlinien benötigen wir die Pakete für die SELinux Policy-Entwicklung (diese sind im Kurs ggf. schon installiert)
dnf install policycoreutils-python3 selinux-policy-devel
- Nach der Installation dieser Pakete befinden sie die SELinux Policy
Quelldateien (der von Red Hat und der Community erstellen
Richtlinien) im Verzeichnis
/usr/share/selinux/devel/
ls -l /usr/share/selinux/devel/ ls -l /usr/share/selinux/devel/include/contrib/
6.9.2 Ein einfacher Web-Server
- Hier ist der Quellcode (C Programmiersprache) eines (sehr) einfachen Web-Servers. Dieser Webserver liefert nur eine statische Webseite aus (diese Webseite ist fest im Quellcode des Servers eingebaut und wird nicht aus dem Dateisystem geladen):
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #include <err.h> char response[] = "HTTP/1.1 200 OK\r\n" "Content-Type: text/html; charset=UTF-8\r\n\r\n" "<!DOCTYPE html><html><head><title>Bye-bye baby bye-bye</title>" "<style>body { background-color: #111 }" "h1 { font-size:4cm; text-align: center; color: black;" " text-shadow: 0 0 2mm red}</style></head>" "<body><h1>Goodbye, world!</h1></body></html>\r\n"; int main() { int one = 1, client_fd; struct sockaddr_in svr_addr, cli_addr; socklen_t sin_len = sizeof(cli_addr); int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) err(1, "can't open socket"); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); int port = 8080; svr_addr.sin_family = AF_INET; svr_addr.sin_addr.s_addr = INADDR_ANY; svr_addr.sin_port = htons(port); if (bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) { close(sock); err(1, "Can't bind"); } listen(sock, 5); while (1) { client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len); printf("got connection\n"); if (client_fd == -1) { perror("Can't accept"); continue; } write(client_fd, response, sizeof(response) - 1); /*-1:'\0'*/ close(client_fd); } }
- Wir erstellen ein neues Verzeichnis für unser Project und erstellen die Datei mit dem Quellcode mit Hilfe eines Text-Editors (emacs, mg, nano, vim etc)
mkdir ~/src cd ~/src $EDITOR simple-server1.c
- Im nächsten Schritt wird der Quellcode in eine Programm-Datei
übersetzt (
simple-server
)
gcc -o simple-server simple-server1.c
- Die Programmdatei kopieren wir in das Verzeichnis
/usr/local/bin
cp simple-server /usr/local/bin
- Wir starten den Server im Hintergrund und testen die Funktion in dem wir uns mit einem Web-Browser an Port 8080 verbinden. Wir sollten dort eine "HelloHHHHHGoodbye World" Meldung sehen. Bei jeder Verbindung gibt der Server den Text Got connection aus.
simple-server &
- Wenn wir uns die SELinux Label des Dienstes anzeigen lassen sehen
wir das dieses Dienst
unconfined
ist, also nicht durch SELinux abgesichert
ps -eZ | grep simple ls -lZ /usr/local/bin/simple-server
6.9.3 Initiales SELinux Policy Modul
- Wir erstellen ein neues Verzeichnis fuer das neue SELinux Policy Modul
mkdir ~/selinux-src cd ~/selinux-src
- Der Befehl
sepolicy generate
erzeugt eine Vorlage für ein SELinux Policy Modul
sepolicy generate -n simple-server --init /usr/local/bin/simple-server
- Es werden drei SELinux Policy Quelldateien erstell
simple-server.te
- Type Enforcement Quellcode - auf welche Datei-Typen darf der Prozess zugreifensimple-server.fc
- File Context Quellcode - welche Datei-Typen werden benutzt (und in welchen Pfaden liegen diese)simple-server.if
- Interface Quellcode - Definiert die Übergänge zwischen den Dateisystem und Prozess Typen, und definiert die Regeln für die Richtliniensimple-server_selinux.spec
- Quelldatei für ein RPM Paketsimple-server.sh
- Shell Skript zum bauen des RPM Pakets des SELinux Policy Moduls
- Diese Dateien können wir uns anschauen
- Die Policy ist im
permissive
Modus!
less simple-server.te less simple-server.fc less simple-server.if
- Wir testen diese SELinux Policy indem wir diese übersetzen und ein
RPM-Paket erstellen. Das Paket
rpm-build
wird benötigt um ein RPM-Paket zu bauen
dnf -y install rpm-build sh simple-server.sh ls -l
- Die neue SELinux Policy befindet sich in der Datei
simple-server.pp
. Dieses neue SELinux Policy-Modul kann nun geladen und aktiviert werden
semodule -i simple-server.pp
- Die neue Policy wirkt sich nicht auf schon gestartete Prozesse
aus. Daher stoppen wir den vorher gestarteten
simple-server
Prozess
pkill simple-server
- Wir erstellen eine SystemD Service-Unit für den Server Dienst
$EDITOR /etc/systemd/system/simple-server.service
- Die Unit-Datei
[Unit] Description=a simple http server After=syslog.target network.target [Service] ExecStart=/usr/local/bin/simple-server [Install] WantedBy=multi-user.target
- Die neue Systemd-Service-Datei muss mit dem richtigen SELinux Label versehen werden
restorecon -R -v /etc/systemd/system/simple-server.service
- Systemd Service-Units neu laden und den Simple-Server starten
systemctl daemon-reload systemctl start simple-server systemctl enable simple-server systemctl status simple-server
- Nun den Dienst benutzen (Mit dem Web-Browser auf Port 8080 zugreifen). Das SELinux Modul ist noch im Permissive Mode, Verstösse gegen die Policy werden im Audit-Log protokolliert
ausearch -m avc -ts recent -c simple-server
- Auch das Systemd-Journal liefert Fehlermeldungen über
SELinux-Troubleshoot Modul
setroubleshoot
journalctl | grep setroubleshoot
- Erklärungen zu den Policy-Fehlermeldungen ausgeben
ausearch -m avc -ts today -c simple-server | audit2why | less
- Mittels des Programms
audit2allow
lassen sich Policy-Regeln aus den Audit-Meldungen erstellen. Diese Regeln sind selten 100% korrekt und müssen oft nachbearbeitet werden, helfen aber enorm bei der Erstellung eines Regelwerkes- Der Befehl
sepolgen-ifgen
erzeugt aus den SLinux Interface-Dateien des Systems Hilfdateien für die Erstellung der Policy-Dateien - Der Befehl
audit2allow -R
gibt die SELinux Policy-Regeln auf dem Terminal aus. Diese bauen wir per copy-n-paste in die Type-Enforcement-Quelldateisimple-server.te
ein. Dabei muss auf die korrekte Reihenfolge der Abschnitte geachtet werden (require
Block unter Deklarationen,allow
Ausdrücke darunter):
- Der Befehl
sepolgen-ifgen -v ausearch -m avc -ts today -c simple-server | audit2allow -R require { type simple-server_t; class tcp_socket { bind create setopt accept listen }; } #============= simple-server_t ============== allow simple-server_t self:tcp_socket { bind create setopt accept listen }; corenet_tcp_bind_generic_node(simple-server_t) corenet_tcp_bind_http_cache_port(simple-server_t)
- Neue Policy-Regeln in die Policy einfügen, Modul entfernen, neu kompilieren und dann neu laden
semodule -r simple-server sh ./simple-server.sh semodule -i simple-server.pp systemctl restart simple-server
- Die Anwendung benutzen und testen, danach wieder das Audit-Log auf
SELinux Fehler des
simple-server
Prozesses prüfen. GGf. neue Regeln erstellen und Modul und Programm neu laden - Diese Schritte wiederholen bis keine SELinux Meldungen mehr im
Audit-Log auftauchen
ausearch -m avc -ts recent -c httpd -i <no matches>
- Die Anwendung ggf. für eine gewisse Zeit in Produktion im permissive Modus betreiben und auf SELinux Fehler prüfen
- Treten keine Fehler mehr auf, dann die Zeile
permissive simple-server_t;
in der Type-Enforcement Datei auskommentieren und das Modul im enforcing Modus betreiben - Schalten wir nun das
simple-server
Modul in den enforcing Modus, werden wir feststellen das das Programm doch nicht wie gewünscht funktioniert - Ein Trace des laufenden Programms mittels
strace
oder eBPF oder bpftrace zeigt das die Syscallsshutdown
undwrite
fehlschlagen. Diese werden von SELinux unterbunden, aber nicht an das Audit-Subsystem gemeldet. - Aufgabe: Trage die Syscalls
shutdown
undwrite
in die Policy ein, übersetze die Policy und teste erneut
6.9.4 Die "DoNotAudit" Regeln ausschalten
- Entwickler von SELinux-Policies können bestimmte Regeln vom
Auditing ausschliessen
- Um übermässiges Logging im Audit zu vermeiden
- Verstösse gegen SELinux-Regeln, welche mit
donotaudit
markiert sind, werden nicht im Audit-Log vermerkt - Bei der Entwicklung von neuen SELinux Policies kann dies stören, denn hier möchte man im Audit-Log ein möglichst vollständiges Bild aller Verstösse bekommen
- Die
donotaudit
Regeln in der SELinux-Richtline ausschalten# semodule -DB
- Um die
donotaudit
Regel wieder zu aktivieren# semodule -B
6.10 Eine Änderung an einer SELinux Policy
- Unser "Simple-Server" lernt das Logging in eine Datei. In der
Quelldatei-Box sehen wir die Änderungen an dem Quellcode des
simple-server.c
Programms:
--- simple-server1.c 2022-10-25 09:22:06.684216579 +0000 +++ simple-server2.c 2022-10-26 08:53:33.319698944 +0000 @@ -19,6 +19,13 @@ int main() { int one = 1, client_fd; + FILE *f = fopen("/var/log/simple-server.log", "a"); + if (f == NULL) + { + printf("Error opening file!\n"); + exit(1); + } + struct sockaddr_in svr_addr, cli_addr; socklen_t sin_len = sizeof(cli_addr); @@ -40,7 +47,8 @@ listen(sock, 5); while (1) { client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len); - printf("got connection\n"); + fputs("got connection\n",f); + fflush(f); if (client_fd == -1) { perror("Can't accept");
- Das gepatchte Programm. Dieses Programm schreibt nun ein einfaches
Log in die Datei
/var/log/simple-server.log
. Den Nachfolgenden Quellcode in die Dateisimple-server2.c
speichern
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #include <err.h> char response[] = "HTTP/1.1 200 OK\r\n" "Content-Type: text/html; charset=UTF-8\r\n\r\n" "<!DOCTYPE html><html><head><title>Bye-bye baby bye-bye</title>" "<style>body { background-color: #111 }" "h1 { font-size:4cm; text-align: center; color: black;" " text-shadow: 0 0 2mm red}</style></head>" "<body><h1>Goodbye, world!</h1></body></html>\r\n"; int main() { int one = 1, client_fd; FILE *f = fopen("/var/log/simple-server.log", "a"); if (f == NULL) { printf("Error opening file!\n"); exit(1); } struct sockaddr_in svr_addr, cli_addr; socklen_t sin_len = sizeof(cli_addr); int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) err(1, "can't open socket"); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); int port = 8080; svr_addr.sin_family = AF_INET; svr_addr.sin_addr.s_addr = INADDR_ANY; svr_addr.sin_port = htons(port); if (bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) { close(sock); err(1, "Can't bind"); } listen(sock, 5); while (1) { client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len); fprintf(f,"got connection\n"); fflush(f); if (client_fd == -1) { perror("Can't accept"); continue; } write(client_fd, response, sizeof(response) - 1); /*-1:'\0'*/ close(client_fd); } }
- In der SELinux-Policy-Quelldatei
simple-server.fc
definieren wir den neuen Datei-Kontextvar_log_t
für die Log-Datei
/var/log/simple-server.log -- gen_context(system_u:object_r:var_log_t,s0)
- Die SELinux Type-Enforcment Datei für
simple-server
auf Permissive stellen und das SELinux-Modul neu übersetzen und laden - Den neuen Server-Dienst übersetzen, den alten
simple-server
Prozess stoppen, die neue Programm-Datei nach/usr/local/bin
kopieren, das SELinux Label anpassen und den Dienst neu starten
gcc -o simple-server simple-server2.c systemctl stop simple-server cp simple-server /usr/local/bin restorecon -R -v /usr/local/bin/simple-server systemctl start simple-server
- Per Web-Browser die Webseite auf
http://selinuxNNN.linux-sicherheit.org:8080/
aufrufen. - Neue SELinux Audit-Meldungen tauchen auf
# ausearch -m avc -ts recent -c simple-server ---- time->Wed Aug 24 21:17:34 2016 type=SYSCALL msg=audit(1472073454.717:1390): arch=c000003e syscall=2 success=yes exit=3 a0=400ae2 a1=441 a2=1b6 a3=21000 items=0 ppid=1 pid=7869 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="simple-server" exe="/usr/local/bin/simple-server" subj=system_u:system_r:simple-server_t:s0 key=(null) type=AVC msg=audit(1472073454.717:1390): avc: denied { open } for pid=7869 comm="simple-server" path="/var/log/simple-server.log" dev="vda1" ino=268256 scontext=system_u:system_r:simple-server_t:s0 tcontext=system_u:object_r:var_log_t:s0 tclass=file type=AVC msg=audit(1472073454.717:1390): avc: denied { create } for pid=7869 comm="simple-server" name="simple-server.log" scontext=system_u:system_r:simple-server_t:s0 tcontext=system_u:object_r:var_log_t:s0 tclass=file type=AVC msg=audit(1472073454.717:1390): avc: denied { add_name } for pid=7869 comm="simple-server" name="simple-server.log" scontext=system_u:system_r:simple-server_t:s0 tcontext=system_u:object_r:var_log_t:s0 tclass=dir type=AVC msg=audit(1472073454.717:1390): avc: denied { write } for pid=7869 comm="simple-server" name="log" dev="vda1" ino=258603 scontext=system_u:system_r:simple-server_t:s0 tcontext=system_u:object_r:var_log_t:s0 tclass=dir
- Die Erweiterungen zur SELinux Policy ausgeben, prüfen und in die
Type-Enforcement-Datei
simple-server.te
einfügen:
# ausearch -m avc -ts recent -c simple-server | audit2allow -R require { type simple-server_t; } #============= simple-server_t ============== auth_log_filetrans_login_records(simple-server_t) logging_manage_generic_logs(simple-server_t)
simple-server
SELinux Modul entfernen, Policy neu übersetzen, Modul neu laden, testen- Ggf. fehlen die Regeln um Log-Dateien anlegen und schreiben zu
dürfen. Wenn dies der Fall ist, die
donotaudit
Funktion in der SELinux-Policy deaktivieren- Ein Blick in die bestehenden SELinux Policy Quelldateien von ähnlichen Programmen kann (auch) helfen
- Wir fügen der Type-Enforcement-Quelldatei den Typ
var_log_t
und die Klassenfile
(Syscallscreate
,open
undwrite
) unddir
(Verzeichnis mit den Syscallswrite
undadd_name
) hinzu
require { type simple-server_t; type var_log_t; class tcp_socket { bind create setopt accept listen shutdown write }; class file { create open write }; class dir { write add_name }; }
- Wir fügen der Type-Enforcement-Quelldatei die Regeln für den
Zugriff auf Dateien und Verzeichnisse im
/var/log
Verzeichnisbaum hinzu:
allow simple-server_t var_log_t:file { create open write }; allow simple-server_t var_log_t:dir { write add_name };
- Das Makro
logging_rw_generic_log_dirs
erlaubt das Schreiben von Log-Dateienlogging_rw_generic_log_dirs(simple-server_t)
- Solange die Policy anpassen, bis keine Permission-Meldungen im Audit-Log erscheinen
- Prüfen, das die Log-Datei korrekt erstellt wird
6.10.1 Lösung - Finale Policy-Datei für "simple-server"
- Als finalen Schritt den Permissive Modus aus der Policy herausnehmen, Die Versionsnummer anpassen (Version 1.1.0), übersetzen und nochmals testen
policy_module(simple-server, 1.1.0) ######################################## # # Declarations # type simple-server_t; type simple-server_exec_t; init_daemon_domain(simple-server_t, simple-server_exec_t) require { type simple-server_t; type var_log_t; class tcp_socket { accept bind create listen setopt shutdown write }; class file { create open write }; class dir { write add_name }; } #permissive simple-server_t; ######################################## # # simple-server local policy # allow simple-server_t self:fifo_file rw_fifo_file_perms; allow simple-server_t self:unix_stream_socket create_stream_socket_perms; allow simple-server_t self:tcp_socket { accept bind create listen setopt shutdown write }; allow simple-server_t var_log_t:file { create open write }; allow simple-server_t var_log_t:dir { create write add_name }; corenet_tcp_bind_generic_node(simple-server_t) corenet_tcp_bind_http_cache_port(simple-server_t) domain_use_interactive_fds(simple-server_t) files_read_etc_files(simple-server_t) miscfiles_read_localization(simple-server_t) logging_manage_generic_logs(simple-server_t) logging_rw_generic_log_dirs(simple-server_t)
simple-server
SELinux Modul laden,simple-server
Prozess per Systemd neu starten, Programm testen
6.11 Hilfsmittel zur Erstellung von SELinux-Policy Regelwerken
strace
- Kann die benutzten Systemcalls eines laufenden Prozesses ausgebenautrace
- Kann die benutzten Systemcalls eines Prozessaufrufs ausgebennm
- kann die Aufrufe in die C-Bibliothek (GLIBC) ausgeben:
# nm /usr/local/bin/simple-server | grep @GLIBC U accept@GLIBC_2.2.5 U bind@GLIBC_2.2.5 U close@GLIBC_2.2.5 U err@GLIBC_2.2.5 U htons@GLIBC_2.2.5 U __libc_start_main@GLIBC_2.34 U listen@GLIBC_2.2.5 U perror@GLIBC_2.2.5 U puts@GLIBC_2.2.5 U setsockopt@GLIBC_2.2.5 U socket@GLIBC_2.2.5 U write@GLIBC_2.2.5
- eBPF und
bpftrace
sind sehr gute Hilfsmittel, um Prozesse und auch das Verhalten von Prozessen unter SELinux zu analysieren
7 Netzwerk-Logging im Audit-Daemon
- Der Audit-Daemon kann Audit-Loginformationen auch über eine Netzwerk-Verbindug entgegennehmen. Dies erlaubt das Zusammenführen von Audit-Log-Informationen auf einem zentralen Audit-Log-Host. Wichtige forensische Informationen werden ausserhalb des Zugriffs durch Angreifer gespeichert.
- Auf dem zentralen Log-System wird die Einstellung
tcp_listen_port
in der Konfigurations-Datei/etc/audit/auditd.conf
für den Audit-Daemon aktiviert
tcp_listen_port = 60 tcp_listen_queue = 5 tcp_max_per_addr = 1 tcp_client_ports = 1024-65535 tcp_client_max_idle = 0
- Nach dem (Neu-)Start des Audit-Daemons ist nun Port 60 für eingehende Audit-Meldungen geöffnet
# dnf install lsof # lsof -Poni :60 COMMAND PID USER FD TYPE DEVICE OFFSET NODE NAME auditd 60599 root 12u IPv6 417339 0t0 TCP *:60 (LISTEN)
- Auf der Maschine des Audit-Senders das Paket
audispd-plugins
installieren:
# dnf install audispd-plugins
- Das Remote-Logging wird über die Plugin-Konfiguration unter
/etc/audit/plugins.d/au-remote.conf
angeschaltet:
# This file controls the audispd data path to the # remote event logger. This plugin will send events to # a remote machine (Central Logger). active = yes direction = out path = /sbin/audisp-remote type = always #args = format = string
- Der Remote-Audit-Log Dienst hat eine eigene Konfigurationsdatei
unter
/etc/audit/audisp-remote.conf
# # This file controls the configuration of the audit remote # logging subsystem, audisp-remote. # remote_server = port = 60 ##local_port = transport = tcp queue_file = /var/spool/audit/remote.log mode = immediate queue_depth = 10240 format = managed network_retry_time = 1 max_tries_per_record = 3 max_time_per_record = 5 heartbeat_timeout = 0 network_failure_action = stop disk_low_action = ignore disk_full_action = warn_once disk_error_action = warn_once remote_ending_action = reconnect generic_error_action = syslog generic_warning_action = syslog queue_error_action = stop overflow_action = syslog startup_failure_action = warn_once_continue ##krb5_principal = ##krb5_client_name = auditd ##krb5_key_file = /etc/audisp/audisp-remote.key
8 SELinux Ressources
8.1 Audit Subsystem
- Audit Subsystem Homepage https://people.redhat.com/sgrubb/audit
- Linux Audit Mailing-Liste https://listman.redhat.com/mailman/listinfo/linux-audit
- Linux Audit Dokumentation https://github.com/linux-audit/audit-documentation/wiki
- Introduction to Linux Audit http://security-plus-data-science.blogspot.com/2017/02/introduction-to-linux-audit.html
- Audit log Normalization Part 1 http://security-plus-data-science.blogspot.com/2017/02/audit-log-normalization.html
- Audit log Normalization Part 2 - CSV format http://security-plus-data-science.blogspot.com/2017/02/audit-log-normalization-part-2-csv.html
- A Linux Auditd rule set mapped to MITRE's Attack Framework https://github.com/bfuzzy/auditd-attack
- Setup and configure linux auditd https://github.com/juju4/ansible-auditd
- Best Practice Auditd Configuration https://github.com/Neo23x0/auditd/
- Splunk App for Linux Auditd https://github.com/doksu/splunk_auditd
- All-Seeing Eye or Blind Man? Understanding the Linux Kernel Auditing System https://www.sans.org/white-papers/38605/
- SUSE "Understanding Linux Audit" https://documentation.suse.com/sles/12-SP4/html/SLES-all/cha-audit-comp.html
- Fedora/Red Hat: Changes/Deprecate TCP wrappers https://fedoraproject.org/wiki/Changes/Deprecate_TCP_wrappers
- HOWTO Fedora Enable Auditing https://github.com/linux-audit/audit-documentation/wiki/HOWTO-Fedora-Enable-Auditing
8.2 SELinux
- NSA SELinux https://web.archive.org/web/20201022103915/https://www.nsa.gov/what-we-do/research/selinux/
- Webseite https://selinuxproject.org/page/Main_Page
- SELinux FOR RED HAT DEVELOPERS https://www.redhat.com/en/files/resources/en-rhel-selinux-devlopers-120114.pdf
- SELinux Notebook https://github.com/SELinuxProject/selinux-notebook
- Fedora Security-Enhanced Linux User Guide https://docs.fedoraproject.org/en-US/Fedora/13/html/Security-Enhanced_Linux/index.html
- Fedora SELinux FAQ - Frequently-asked questions about Security Enhanced Linux https://docs.fedoraproject.org/en-US/Fedora/13/html/SELinux_FAQ/index.html
- A Brief Tour of Linux Security Modules https://www.starlab.io/blog/a-brief-tour-of-linux-security-modules/
- SELinux Struggles with BIND Startup https://www.isc.org/blogs/selinux-struggles-bind/
named_selinux
Manual page https://linux.die.net/man/8/named_selinux (the man page on your system is likely more up-to-date)- Policy sourcecode examples https://github.com/SELinuxProject/refpolicy
8.3 Books
- SELinux System Administration - Third Edition by Sven Vermeulen https://www.packtpub.com/product/selinux-system-administration-third-edition/9781800201477
9 eBPF (Bonus Inhalt)
9.1 Was ist eBPF, XDP und BCC?
- eBPF ist die extended Berkeley Packet Filter virtuelle Maschine im Linux Kernel
- XDP (eXpress Data Path) ist eine eBPF Schnittstelle zum Netzwerkstack
- BCC ist die BPF Compiler Collection, eine Sammlung von Tools und eBPF Programmen
- eBPF ist eine Weiterentwicklung der originalen Berkeley Packet Filter Technologie https://en.wikipedia.org/wiki/Berkeley_Packet_Filter
9.2 Die eBPF Idee
- eBPF erlaubt es dem Benutzer, Programme im Betriebssystem-Kern
innerhalb einer Sandbox auszuführen
- eBPF ermöglicht es, die Funktionen des Betriebssystem-Kerns sicher und effizient zu erweitern, ohne den Quell-Code des Kernels ändern oder Module laden zu müssen
- eBPF Programme können Netzwerk-Pakete (und andere Datenstrukturen) innerhalb des Linux-Kernels überwachen und verändern
- eBPF Programme sind keine Kernel Module, es ist nicht
notwendig ein Kernel-Entwickler zu sein um eBPF benutzen zu
können
- Wissen über Programmierung in der Sprache "C" ist jedoch von Vorteil
9.3 eBPF
9.4 eBPF Einsatzgebiete
- Einsatzgebiete für eBPF
- Netzwerk Sicherheit (erweiterte Firewall Funktionen)
- Host / Container Security
- Forensische Analyse laufender Prozesse
- Fehlerdiagnose
- Geschwindigkeitsmessungen
- Rootkits? Backdoors im Kernel oder in Netzwerkkarten?
9.5 eBPF Verfügbarkeit
- eBPF ist in modernen Linux Systemen verfügbar (ab Kernel 3.18+) und
wird derzeit von Microsoft auf das Windows Betriebssystem portiert
- Blog Post "Making eBPF work on Windows" https://cloudblogs.microsoft.com/opensource/2021/05/10/making-ebpf-work-on-windows/
9.6 Die Wurzeln von BPF
- Der originale BSD Packet Filter (BPF) wurde von Steven
McCanne und Van Jacobson am Lawrence Berkeley Laboratory entwickelt
(https://www.tcpdump.org/papers/bpf-usenix93.pdf)
- BPF wurde auf fast alle Unix/Linux Systeme und viele non-Unix Betriebssysteme portiert (z.B. Windows, BeOS/Haiku, OS/2 …)
- BPF ist die Basis-Technologie hinter bekannten Netzwerk-Sniffing
Werkzeugen wie
tcpdump
und Wireshark
9.7 BPF am Beispiel von tcpdump
- Ein BPF-Filter (z.B. für tcpdump) wird in einen Bytecode für
die BPF virtuelle Maschine im Linux-Kernel übersetzt und in den
Kernel geladen
- Das Betriebssystem ruft das BPF-Programm für jedes Netzwerk-Paket auf, welches den Netzwerk-Stack durchläuft
- Nur Pakete, welche auf den Filter-Ausdruck passen, werden an das
Programm im Userspace weitergeleitet (der
tcpdump
Prozess in dieses Beispiel) - BPF reduziert die Menge der Daten, welche zwischen dem Kernel und dem Userspace ausgetauscht werden müssen
9.8 BPF am Beispiel von tcpdump
tcpdump
kann angewiesen werden den BPF Quellcode des tcpdump
Filters auszugeben:
# tcpdump -d port 53 and host 1.1.1.1 Warning: assuming Ethernet (000) ldh [12] (001) jeq #0x86dd jt 19 jf 2 (002) jeq #0x800 jt 3 jf 19 (003) ldb [23] (004) jeq #0x84 jt 7 jf 5 (005) jeq #0x6 jt 7 jf 6 (006) jeq #0x11 jt 7 jf 19 (007) ldh [20] (008) jset #0x1fff jt 19 jf 9 (009) ldxb 4*([14]&0xf) (010) ldh [x + 14] (011) jeq #0x35 jt 14 jf 12 (012) ldh [x + 16] (013) jeq #0x35 jt 14 jf 19 (014) ld [26] (015) jeq #0x1010101 jt 18 jf 16 (016) ld [30] (017) jeq #0x1010101 jt 18 jf 19 (018) ret #262144 (019) ret #0
9.9 eBPF vs. BPF
- Während BPF (heute auch cBPF = classic BPF genannt) Netzwerk
Pakete im Betriebssystem-Kern filtert, kann eBPF auf weitere
Kernel-Datenstrukturen Filter anwenden und Programm-Code
ausführen:
- Kernel Systemcalls
- Kernel Tracepoints
- Kernel Funktionen
- Userspace Tracepoints
- Userspace Funktionen
9.10 eBPF und der Linux Kernel
- Die erste Version von eBPF wurde im Linux Kernel 3.18 eingeführt
- Neue Kernel Versionen bringen weitere, neue eBPF Funktionen
- Linux Distributionen (Red Hat/Canonical/Suse) haben zum Teil eBPF Funktionen auf ältere LTS/EL Kernel Versionen zurückportiert
- Eine Übersicht der eBPF Funktionen nach Linux Kernel Version aufgeschlüsselt: https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md
9.11 Die eBPF Architektur
9.11.1 Die eBPF virtuelle Maschine
- eBPF Programme werden für eine virtuelle CPU Architektur übersetzt
- Der Programmcode wird in den Linux Kernel geladen und dort geprüft
- Auf populären CPU Architekturen (amd64, AARCH64) wird der eBPF Bytecode in nativen Maschinencode re-compiliert (Just in Time Compiler = JIT)
9.11.2 XDP - Express Data Path
- Der express data path (XDP) innerhalb des Linux-Kernels ist
eine Infrastruktur, um auf unterster Ebene Kontrolle über
Netzwerk-Pakete auszuüben
- Der normale Datenfluss im Linux Netzwerk-Stack kann via XDP umgangen werden
- eBPF Programme können in den eXpress Data Path (XDP) geladen werden
9.11.3 XDP / eBPF Hardware Offloading
- XDP eBPF Programm können auf verschiedenen Ebenen in den Linux Kernel geladen werden
- Offload XDP: direkt in die Netzwerk-Hardware (ASIC/FPGA, benötigt Unterstützung für XDP in der Hardware, z.B. vorhanden in den Netronome Netzwerkadaptern)
- Native XDP: In den Linux Kernel Netzwerk-Treiber der Netzwerkschnittstelle (benötigt Unterstützung durch den Treiber)
- Generic XDP: In den Linux Kernel Netzwerk-Stack (weniger Performance, aber ohne besondere Unterstützung von Hardware oder Treibern möglich)
9.11.4 XDP / eBPF Ausführungs-Ebenen
9.11.5 XDP Funktionen
- XDP Programme können
- lesen: Netzwerk-Pakete und Statistiken sammeln
- verändern: Den Inhalt der Netzwerkpakete ändern
- verwerfen: Ausgewählte Netzwerk-Pakete können verworfen werden (Firewall)
- umleiten: Netzwerkpakete können auf die gleichen oder andere Netzwerkschnittstellen umgeleitet werden (Switching/Routing)
- durchlassen: Das Netzwerkpaket wird an den Linux TCP/IP Stack zur normalen Bearbeitung übergeben
9.11.6 XDP vs DDoS Angriffe
- XDP kann unerwünschten Netzwerk-Verkehr schon sehr früh im Netzwerk-Stack verwerfen (z.B. innerhalb der Netzwerk-Hardware). Dies kann zum Schutz gegen DDoS Angriffe eingesetzt werden
9.12 eBPF "Real-World" Anwendungen
9.12.1 eBPF/XDP Support in DNS Software
- Der Open-Source DNS Load-Balancer DNSdist von PowerDNS kann DNS Pakete via eBPF und XDP filtern oder per Rate-Limiting beschränken
- Der Knot Resolver (seit Version 5.2.0) kann mittels ePBF und XDP den Linux TCP/IP Stack für DNS Pakete umgehen und die DNS-Pakete direkt an den Knot DNS Resolver Prozess im Userspace weiterleiten (https://knot-resolver.readthedocs.io/en/stable/daemon-bindings-net_xdpsrv.html). Hierdurch wird eine enorme Geschwindigkeitssteigerung der DNS Antwortrate erziehlt.
9.12.2 eBPF Programme zur Analyse des Laufzeitverhalten von Linux-Systemen
9.12.3 Beispiel: eBPF Syscall Auswertung
- Die Syscalls eines BIND 9 Prozesses auswerten mit dem Programm
syscount
# syscount-bpfcc -p `pgrep named` -i 10 Tracing syscalls, printing top 10... Ctrl+C to quit. [07:34:19] SYSCALL COUNT futex 547 getpid 121 sendto 113 read 56 write 31 epoll_wait 31 openat 23 close 20 epoll_ctl 20 recvmsg 20
9.12.4 Beispiel: eBPF Prozess Capabilities
- Die Linux Capabilities von laufenden Prozessen anzeigen
# capable-bpfcc | grep named 07:36:17 0 29378 (named) 24 CAP_SYS_RESOURCE 1 07:36:17 0 29378 (named) 24 CAP_SYS_RESOURCE 1 07:36:17 0 29378 (named) 12 CAP_NET_ADMIN 1 07:36:17 0 29378 (named) 21 CAP_SYS_ADMIN 1 07:36:17 0 29378 named 6 CAP_SETGID 1 07:36:17 0 29378 named 6 CAP_SETGID 1 07:36:17 0 29378 named 7 CAP_SETUID 1 07:36:17 109 29378 named 24 CAP_SYS_RESOURCE 1
9.12.5 Beispiel: gethostlatency
- Das BCC Programm
gethostlatency
misst die Latenz der client-seitigen DNS Namensauflösung durch Systemaufrufe wiegetaddrinfo
odergethostbyname
# gethostlatency-bpfcc TIME PID COMM LATms HOST 10:21:58 19183 ping 143.22 example.org 10:22:18 19184 ssh 0.03 host.example.de 10:22:18 19184 ssh 60.59 host.example.de 10:22:35 19185 ping 23.44 isc.org 10:22:49 19186 ping 4459.72 yahoo.co.kr
9.12.6 Sicherheitsanwendungen
- Audit-Frameworks für Linux benutzen eBPF um Prozesse auf einem Host
oder im Container ganzheitlich zu überwachen:
- Dateisystemzugriffe
- Netzwerkverkehr
- Prozess-Execution
- Systemcalls
9.12.7 Sicherheitsanwendungen
- Dabei wird jedem Prozess eine execution-id zugewiesen und alle Log-Einträge können mittels der ID einem Prozess zugeordnet werden
- eBPF kann direkt Policy-Entscheidungen von Linux-Security-Modulen
(LSMs wie SELinux, AppArmor, Tomoya) anpassen
- eBPF kann direkt Dateizugriffe, Netzwerk-Kommunikation oder Syscalls unterbinden
9.12.8 Sicherheitsanwendungen: Tetragon
9.12.9 Sicherheitsanwendungen: Falco
9.12.10 Sicherheitsanwendungen: tracee
9.13 Quis custodiet ipsos custodes?
9.13.1 Backdoors
- Mittels eBPF lassen sich potente Backdoors im Betriebssystem-Kernel
verstecken
- Persistent?
- In der Netzwerk-Karte?
- Um eBPF Programme in den Kernel zu laden werden priviligierte
Rechte (CAPSYSADMIN) benötigt (in neueren Linux
Distributions-Versionen)
/proc/sys/kernel/unprivileged_bpf_disabled
9.13.2 boopkit
9.13.3 TripeCross
9.13.4 BPFdoor
9.13.5 Symbiote
9.13.6 Bvp47
9.14 eBPF einschränken
9.14.1 "Unprivilegded eBPF" ausschalten
- Bis 2022 hatten einige (populäre) Linux Distributionen
unpriviligiertes eBPF aktiviert
- Jeder Benutzer konnte eBPF Code in den Kernel laden!
- Ubuntu (und andere Linux-Distributionen) haben diese Lücke in Frühjahr 2022 geschlossen
- Prüfen, daß die Linux Kernel-Variable (sysctl)
kernel.unprivileged_bpf_disabled
> 0 ist
9.14.2 KPROBEOVERRIDE im Kernel
- Ist in der Kernel Konfiguration der Schalter
CONFIG_BPF_KPROBE_OVERRIDE
aktiv (zur Übersetzungszeit des Kernels), so können eBPF Programme die Rückgabewerte von (Kernel-)Funktionen überschreiben - Diese Konfiguration sollte in Kernel in Produktions-Umgebungen nicht gesetzt sein (Kernel "config" prüfen)
9.14.3 KProbes ausschalten
- Durch schreiben des Wertes
0
in die Pseudo-Datei/sys/kernel/debug/kprobes/enabled
werden die Kernel-Probes (KPROBES) ausgeschaltet - Achtung: ein Benutzer mit Schreibrechten auf diese Datei
(z.B.
root
) kann die KPROBES wieder anschalten
9.14.4 Kernel ohne eBPF
- In sicherheitskritischen Bereichen sollten ggf. Kernel ohne eBPF
Funktion (kprobes, XDP, eBPF TC Filter) eingesetzt werden
- Hierzu muss der Kernel neu übersetzt werden (dies ist oft nicht praktikabel)
- Es gibt (derzeit, 2023) keinen Kernel Commandline Schalter um eBPF auszuschalten
9.15 eBPF Literatur
9.15.1 Buch: Linux Observability with BPF
Von David Calavera, Lorenzo Fontana (November 2019)
9.15.2 Buch: Systems Performance (2nd ed.)
Von Brendan Gregg (Dezember 2020)
9.15.3 Buch: BPF Performance Tools
Von Brendan Gregg (Dezember 2019)
9.15.4 Report: What is eBPF? (O'Reilly)
9.15.5 Report: Security Observability with eBPF (O'Reilly)
9.16 eBPF Links
9.16.1 eBPF
- eBPF support and Linux kernel versions https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md
- Awesome BPF https://github.com/zoidbergwill/awesome-ebpf
- Using user-space tracepoints with BPF https://lwn.net/Articles/753601/
- Extending systemd Security Features with eBPF https://kinvolk.io/blog/2021/04/extending-systemd-security-features-with-ebpf/
- Absolute Beginner's Guide to BCC, XDP, and eBPF https://dev.to/satrobit/absolute-beginner-s-guide-to-bcc-xdp-and-ebpf-47oi
9.16.2 eBPF
- Linux Extended BPF (eBPF) Tracing Tools https://www.brendangregg.com/ebpf.html
- Performance Implications of Packet Filtering with Linux eBPF https://www.net.in.tum.de/fileadmin/bibtex/publications/papers/ITC30-Packet-Filtering-eBPF-XDP.pdf
- eBPF for perfomance analysis and networking
https://marioskogias.github.io/students/debeule.pdf
- BPF and XDP Reference Guide https://docs.cilium.io/en/v1.10/bpf/
9.16.3 BCC
- Intro to Kernel and Userspace Tracing Using BCC, Part 1 of 3 https://blogs.oracle.com/linux/post/intro-to-bcc-1
9.16.4 bpftrace
- bpftrace Reference Guide https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md
- Kernel analysis with bpftrace https://lwn.net/Articles/793749/
- The bpftrace One-Liner Tutorial https://github.com/iovisor/bpftrace/blob/master/docs/tutorial_one_liners.md
- Full-system dynamic tracing on Linux using eBPF and bpftrace https://www.joyfulbikeshedding.com/blog/2019-01-31-full-system-dynamic-tracing-on-linux-using-ebpf-and-bpftrace.html
- bpftrace Cheat Sheet https://www.brendangregg.com/BPF/bpftrace-cheat-sheet.html
9.16.5 Network Scripts
9.16.6 eBPF Prometheus exporter
- eBPF exporter https://blog.cloudflare.com/introducing-ebpf_exporter/
9.16.7 eXpress Data Path (XDP)
- Introduction to: XDP and BPF building blocks https://people.netfilter.org/hawk/presentations/ebplane2019/xdp-bpf-building-blocks.pdf
- A practical introduction to XDP https://www.linuxplumbersconf.org/event/2/contributions/71/attachments/17/9/presentation-lpc2018-xdp-tutorial.pdf
- eBPF/XDP https://www.slideshare.net/Netronome/ebpfxdp-sigcomm-2018
- XDP Packet filter and UDP https://fly.io/blog/bpf-xdp-packet-filters-and-udp/
- XDP Firewall https://github.com/gamemann/XDP-Firewall
9.16.8 eXpress Data Path (XDP)
- How to filter packets super fast: XDP & eBPF! https://jvns.ca/blog/2017/04/07/xdp-bpf-tutorial/
- Load XDP programs using the ip (iproute2) command https://medium.com/@fntlnz/load-xdp-programs-using-the-ip-iproute2-command-502043898263
- L4Drop: XDP DDoS Mitigations https://blog.cloudflare.com/l4drop-xdp-ebpf-based-ddos-mitigations/
- How to drop a packet in Linux in more ways than one https://codilime.com/blog/how-to-drop-a-packet-in-linux-in-more-ways-than-one/
- eBPFsnitch https://github.com/harporoeder/ebpfsnitch
- XDP minimal example https://ruderich.org/simon/notes/xdp-minimal-example
- Why is the kernel community replacing iptables with BPF? https://cilium.io/blog/2018/04/17/why-is-the-kernel-community-replacing-iptables
9.16.9 BPF Backdoor
- Linux eBPF backdoor over TCP https://github.com/kris-nova/boopkit
- A Linux eBPF rootkit with a backdoor, C2, library injection, execution hijacking, persistence and stealth capabilities https://github.com/h3xduck/TripleCross
- Offensive BPF: Using bpftrace to host backdoors https://embracethered.com/blog/posts/2021/offensive-bpf-bpftrace-message-based/
- Offensive BPF: Malicious bpftrace https://embracethered.com/blog/posts/2021/offensive-bpf-bpftrace/
9.16.10 BPF Malware/Angriffe
- BPFDoor: Chinese tool almost undetected for FIVE years is second BPF-based attack uncovered this year https://linuxsecurity.com/news/hackscracks/bpfdoor-chinese-tool-almost-undetected-for-five-years-is-second-bpf-based-attack-uncovered-this-year
- Anatomy of suspected top-tier decade-hidden NSA backdoor https://www.theregister.com/2022/02/23/chinese_nsa_linux/
9.16.11 BPF Malware/Angriffe
- Hackers using stealthy Linux backdoor Symbiote to steal credentials https://www.csoonline.com/article/3663510/hackers-using-stealthy-linux-backdoor-symbiote-to-steal-credentials.html
- Bvp47 - Top-tier Backdoor of US NSA Equation Group https://www.pangulab.cn/files/The_Bvp47_a_top-tier_backdoor_of_us_nsa_equation_group.en.pdf
9.16.12 eBPF Sicherheit
- Mapping It Out: Analyzing the Security of eBPF Maps https://www.crowdstrike.com/blog/analyzing-the-security-of-ebpf-maps/
- With Friends like eBPF, who needs enemies ? https://i.blackhat.com/USA21/Wednesday-Handouts/us-21-With-Friends-Like-EBPF-Who-Needs-Enemies.pdf
- Kernel Pwning with eBPF: a Love Story https://www.graplsecurity.com/post/kernel-pwning-with-ebpf-a-love-story
9.16.13 eBPF Sicherheit
- Ubuntu is releasing updated kernels that disable unprivileged eBPF by default https://wiki.ubuntu.com/SecurityTeam/KnowledgeBase/BHI
- Toward signed BPF programs https://lwn.net/Articles/853489/
- CONFIGBPFKPROBEOVERRIDE: Enable BPF programs to override a kprobed function https://cateee.net/lkddb/web-lkddb/BPF_KPROBE_OVERRIDE.html
- The kprobes debugfs interface https://www.kernel.org/doc/Documentation/kprobes.txt
9.16.14 eBPF Sicherheitsprobleme
- CVE-2021-33624 kernel: Linux kernel BPF protection against speculative execution attacks can be bypassed to read arbitrary kernel memory
9.16.15 eBPF Sicherheitswerkzeuge
- A flow-based IDS using Machine Learning in eBPF https://arxiv.org/abs/2102.09980
- ebpfkit-monitor is a tool that detects and protects against eBPF powered rootkits https://github.com/Gui774ume/ebpfkit-monitor
- MAC and Audit policy using eBPF (KRSI) https://lwn.net/Articles/813057/
- KRSI — the other BPF security module https://lwn.net/Articles/808048/
9.16.16 eBPF Audit-Tools
- bpfcontain-rs - https://github.com/willfindlay/bpfcontain-rs/
- Tracee - Linux Runtime Security and Forensics using eBPF https://github.com/aquasecurity/tracee
- falco - Cloud Native Runtime Security https://github.com/falcosecurity/falco
- Cilium Tetragon - eBPF-based Security Observability and Runtime Enforcement https://github.com/cilium/tetragon
9.17 eBPF Praxis
9.17.1 Discover eBPF programs with bpftool
- Installing
bpftool
:
% dnf install bpftool
- Which
eBPF
programms are loaded into the Linux Kernel?
% bpftool prog list 306: cgroup_device tag ca8e50a3c7fb034b gpl loaded_at 2023-02-09T21:16:16+0000 uid 0 xlated 496B jited 307B memlock 4096B pids systemd(1) 307: cgroup_skb tag 6deef7357e7b4530 gpl loaded_at 2023-02-09T21:16:16+0000 uid 0 xlated 64B jited 54B memlock 4096B pids systemd(1) 308: cgroup_skb tag 6deef7357e7b4530 gpl loaded_at 2023-02-09T21:16:16+0000 uid 0 [...]
- What are these? See
cgroup_skb
- Are eBPF programm statistics enabled?
% sysctl kernel.bpf_stats_enabled kernel.bpf_stats_enabled = 0
- Let's enable eBPF statistics
% sysctl -w kernel.bpf_stats_enabled=1
- Dump disassembled eBPF source code
% # bpftool prog dump xlated id 317 0: (bf) r6 = r1 1: (69) r7 = *(u16 *)(r6 +176) 2: (b4) w8 = 0 3: (44) w8 |= 2 4: (b7) r0 = 1 5: (55) if r8 != 0x2 goto pc+1 6: (b7) r0 = 0 7: (95) exit
- See the eBPF JIT compiler generated machine code disassembly
% bpftool prog dump jited tag tag 6deef7357e7b4530 317: cgroup_skb tag 6deef7357e7b4530 gpl [6/723] 0xffffffffc0af90d0: 0: nopl 0x0(%rax,%rax,1) 5: xchg %ax,%ax 7: push %rbp 8: mov %rsp,%rbp b: push %rbx c: push %r13 e: push %r14 10: mov %rdi,%rbx 13: movzwq 0xb0(%rbx),%r13 [...]
9.17.2 bpttrace tools and on-liners
- Install the BIND 9 DNS Server
% dnf install bind % systemctl enable --now named
- Install the bpftrace tools
% dnf install bpftrace
- Switch the operating system to use the BIND 9 DNS resolver
% echo "nameserver 127.0.0.1" > /etc/resolv.conf
- Example 1: Start
gethostlatency.bt
(found in/usr/share/bpftrace/tools
) in one terminal, execute some other network tools that require DNS name resolution (ping
,dnf
,traceroute
etc) to see the DNS name resolution latency - Example 2: Count UDP send/receives by on-CPU PID and process name
(Script is an excerpt From "BPF Performance Tools" by Brendan
Gregg)
- Execute some DNS queries against the BIND 9 resolver
- Exec the bpftrace script with CTRL+C to see the report
% bpftrace -e "k:udp*_sendmsg,k:udp*_recvmsg { @[func, pid, comm] = count(); }"
- Example 3: Show packets received over UDP by the
named
process as a histogram (execute some queries, then terminate the script with CTRL+C)
% bpftrace -e "kr:udp_recvmsg /pid == $(pgrep named)/ { @recv_bytes = hist(retval); }"
- Example 4: Histogram of UDP packets send by the BIND 9 process (execute some queries, then terminate the script with CTRL+C)
% bpftrace -e "kprobe:udp_sendmsg /pid == $(pgrep named)/ { @size = hist(arg2); }"
- Example 5: BIND 9 tracing - print whenever
rndc status
is executed
% bpftrace -e 'uprobe:named:named_server_status { print("rndc status executed") }'
- Example 6: How long does it take (in nanoseconds) to flush the cache with
rndc flush
? Do some queries against the BIND 9 DNS resolver to fill the cache. Then callrndc flush
to flush the cache. The printed value should get higher the more cache entries need to be cleaned.
% bpftrace -e "uprobe:/usr/sbin/named:named_server_flushcache / pid == $(pgrep named) /{ @start[tid] = nsecs; } uretprobe:/usr/sbin/named:named_server_flushcache /@start[tid]/ { print(nsecs - @start[tid]); delete(@start[tid]); }"
- Trace the cache related functions BIND 9 executes. Send some
queries to the BIND 9 DNS resolver, then use
rndc flush
,rndc flushname <domain-name>
andrndc flushtree <name>
and see which functions are executed inside BIND 9
% bpftrace -e 'uprobe:/usr/lib64/libdns-9.16.23-RH.so:dns_cache* { print(func) }'
9.17.3 Instrumenting BIND 9
- Configure BIND 9 to forward all request for the domain
isc.org
to Quad9 (9.9.9.9). In the file/etc/named.conf
add
zone "isc.org" { type forward; forwarders { 9.9.9.9; }; };
- Check the configuration with
named-checkconf
and restart the BIND 9 DNS server withsystemctl restart named
- Install
bpftrace
dnf install bpftrace
- Save the following source into the file
forward-trace.bt
#!/usr/bin/bpftrace struct dns_name { unsigned int magic; unsigned char *ndata; unsigned int length; unsigned int labels; unsigned int attributes; unsigned char *offsets; // isc_buffer_t *buffer; // ISC_LINK(dns_name_t) link; // ISC_LIST(dns_rdataset_t) list; }; BEGIN { print("Waiting for forward decision...\n"); } uprobe:/usr/lib64/libdns-9.16.23-RH.so:dns_fwdtable_find { @dns_name[tid] = ((struct dns_name *)arg1)->ndata } uretprobe:/usr/lib64/libdns-9.16.23-RH.so:dns_fwdtable_find { if (retval == 0) { printf("Forwarded domain name: %s\n", str(@dns_name[tid])); } delete(@dns_name[tid]); }
- Make the file executeable
% chmod +x forward-trace.bt
- Execute the
bpftrace
script
% ./forward-trace.bt
- Login to the lab machine from a different terminal (or use
tmux
). Flush the cache of the BIND 9 resolver withrndc flush
, then execute a DNS name resolution forisc.org
and for other domains. See that thebpftrace
script reports the forward decisions in BIND 9
% dig @localhost isc.org
- Our eBPF program in the
bpftool
listing
bpftool prog list | tail xlated 64B jited 54B memlock 4096B pids systemd(1) 383: kprobe name uretprobe__dns_ tag 30ca488d1d146cd7 gpl run_time_ns 297757 run_cnt 115 loaded_at 2023-02-09T21:46:24+0000 uid 0 xlated 536B jited 314B memlock 4096B map_ids 25,26 pids forward-trace.b(34273) 384: kprobe name uprobe__dns_fwd tag c30da15c9fd45317 gpl run_time_ns 116583 run_cnt 115 loaded_at 2023-02-09T21:46:24+0000 uid 0 xlated 176B jited 105B memlock 4096B map_ids 25 pids forward-trace.b(34273)
- Try to disassemble the eBPF programm (eBPF code and JIT)
9.17.4 Using XDP/eBPF to drop all UDP except DNS
- Install the Kernel headers for the running Linux Kernel. These
headers are needed to get the function names and entry points into
the Linux Kernel for the eBPF probes
% dnf install kernel-headers-$(uname -r)
- Create a text file with the name
drop-non-dns-udp.c
and enter the following C-Code. This eBPF program will …- … be executed for every incoming network packet
- … prints the text got a packet whenever an IP packet is received
- … extracts the IP- and UDP-header from the packet
- … if the packet neither has the UDP source port of 53 (DNS) nor
a destination port of 53 the return code of
XDP_DROP
will be returned, which will discard the packet. A message is printed to inform about the dropped packet - … all other network packets will be given with the return value of
XDP_PASS
into the Linux TCP/IP stack for further processing - This is an example XDP program. Production quality eBPF/XDP
programms should use the eBPF map structures to exchange
information with the User-Space program instead of using the
bpf_trace_printk
function.
#define KBUILD_MODNAME "filter" #include <linux/bpf.h> #include <linux/if_ether.h> #include <linux/ip.h> #include <linux/in.h> #include <linux/udp.h> int udpfilter(struct xdp_md *ctx) { bpf_trace_printk("got a packet\n"); void *data = (void *)(long)ctx->data; void *data_end = (void *)(long)ctx->data_end; struct ethhdr *eth = data; if ((void*)eth + sizeof(*eth) <= data_end) { struct iphdr *ip = data + sizeof(*eth); if ((void*)ip + sizeof(*ip) <= data_end) { if (ip->protocol == IPPROTO_UDP) { struct udphdr *udp = (void*)ip + sizeof(*ip); if ((void*)udp + sizeof(*udp) <= data_end) { if ((udp->dest != ntohs(53)) && (udp->source != ntohs(53))) { if (udp->dest != udp->source) { bpf_trace_printk("drop udp src/dest port %d/%d\n", ntohs(udp->source), ntohs(udp->dest)); return XDP_DROP; } } } } } } return XDP_PASS; }
- Also create a eBPF loader program (written in Python) in the file
drop-non-dns-udp.py
.- This loader will compile the C-Program above into eBPF byte code,
load the program into the Linux Kernel and will detach the eBPF
program with the loopback network interface (
lo
) - There will be a few warnings issued during compilation. This is not a problem for this exercise and we can ignore these for this lab session
- The
virtio
network driver of the virtual machine environment doas not allow eBPF programs on the regular network interfaces (eth0
andeth1
), therefore we test this function with the lookback interface.
- This loader will compile the C-Program above into eBPF byte code,
load the program into the Linux Kernel and will detach the eBPF
program with the loopback network interface (
#!/usr/bin/env python3 from bcc import BPF import time device = "lo" b = BPF(src_file="drop-non-dns-udp.c") fn = b.load_func("udpfilter", BPF.XDP) b.attach_xdp(device, fn, 0) try: b.trace_print() except KeyboardInterrupt: pass b.remove_xdp(device, 0)
- Make the loader executable
% chmod +x drop-non-dns-udp.py
- Execute the loader program (the warnings can be ingnored)
% ./drop-non-dns-udp.py
- Create a new connection to the lab machines (new browser tab,
another SSH connection or use
tmux
) - Create DNS queries towards the running BIND 9 DNS resolver over the loopback interface. These queries shoul not be blocked:
% dig @localhost isc.org
- Send UDP packets (DNS or other protocol) towards a port other than 53, the packets will be discarded before entering the regular Linux Kernel TCP/IP stack:
% dig -p 5353 @localhost isc.org