← Zurück zum Blog

Alternate Data Streams: das zweite $DATA-Attribut, das jeder vergisst

· 5 Min. Lesezeit

ADS ist das NTFS-Feature, das Junior-Analysten immer wieder überrascht. Eine Datei kann mehr als ein $DATA-Attribut haben. Das unbenannte ist das, dessen Größe dir dir anzeigt. Alles andere, alles mit einem Doppelpunkt im Bezeichner, ist für die Tools, zu denen die meisten greifen, unsichtbar. Angreifer wissen das. Microsoft weiß das. Der Prüfer, der deinen IR-Bericht durchsieht, sollte das auch wissen.

Der Mechanismus, kurz gefasst

Im MFT-Datensatz ist Attributtyp 0x80 $DATA. Der Header enthält einen optionalen Namen. Ein einzelner Datensatz kann viele $DATA-Attribute enthalten: ein unbenanntes (den primären Stream) und beliebig viele benannte. Aus dem Userland adressierst du sie mit filename:streamname.

echo payload > readme.txt:nope
type readme.txt:nope

readme.txt hat null Bytes nach allem, was Explorer zeigt. Der benannte nope-Stream enthält die Nutzlast. Derselbe MFT-Datensatz. Derselbe Indexeintrag im übergeordneten Verzeichnis. Zwei unabhängige Datenattribute.

Das ist kein Bug. Es ist eine Designentscheidung von 1993, die NTFS von HPFS geerbt hat, damit Services for Macintosh Ressourcen-Forks anhängen konnten. Fast niemand nutzt ADS heute noch dafür. Alle anderen, einschließlich Microsoft, nutzen es für Ad-hoc-Metadaten.

Wo Windows selbst ADS ablegt

Zone.Identifier ist der berühmte. Edge, Chrome, Firefox und Outlook fügen ihn allem hinzu, was eine Sicherheitsgrenze überquert hat. Der Inhalt ist ein INI-Fragment mit Quell-URL, Referrer und einer ZoneId=3-Zeile, die Mark-of-the-Web-Schutz in Office und SmartScreen auslöst. Wenn du eine MFT liest und eine Binärdatei in Downloads\ keinen Zone.Identifier hat, frage warum. Vielleicht hat jemand Unblock-File ausgeführt oder die Datei durch ein Archiv kopiert, das Streams verwirft.

Andere von Microsoft genutzte Streams, denen du begegnen wirst:

  • $KSP auf asiatischen Tastaturlayout-Dateien.
  • OECustomProperty und Verwandte auf Outlook-Anhängen, die auf der Platte gespeichert wurden.
  • Wof-Reparse-Daten auf komprimierten Dateien unter WIMBoot und CompactOS.
  • SmartScreen auf ausführbaren Dateien, die Defender erfasst hat.
  • encryptable auf ConfigMgr-Cache-Verzeichnissen.

Diese tauchen ständig auf. Lerne, wie deine Baseline aussieht, bevor du Streams als verdächtig markierst.

Was Angreifer tatsächlich mit ADS machen

Drei Muster wiederholen sich:

  1. Payload-Versteck, separater Launcher. Die sichtbare Datei ist harmlos (ein Word-Dokument, eine PDF, eine Lizenzdatei). Der benannte Stream enthält die eigentliche PE. WMI, wmic process call create oder rundll32 startet legit.docx:payload.exe direkt. Defender scannt heute benannte Streams, aber viele EDRs tun das nicht, wenn ein Stream von einem vertrauenswürdigen Prozess erstellt wird.
  2. Living-off-the-land-Versteck. PowerShell-Payloads in ads:script.ps1 geschrieben und dann mit Get-Content -Stream ausgeführt. Überraschend häufig in Red-Team-Retainern, weil es eine pfadbasierte IOC-Suche übersteht.
  3. Persistenz in alternativen Streams von Systemdateien. C:\Windows\System32\drivers\etc\hosts:backdoor ist das Lehrbuchbeispiel. Die Hosts-Datei sieht unangetastet aus, der Zeitstempel kann sogar original sein, und die meisten Datei-Integritäts-Tools schauen nie über den unbenannten Stream hinaus.

Die gemeinsame Eigenschaft: Du siehst sie nicht mit dir, du siehst sie nicht im Explorer, und du siehst sie nicht durch Hashen der sichtbaren Datei. Du siehst sie, indem du den MFT-Datensatz liest.

Auf einem Live-Host erkennen

dir /R zählt Streams in cmd auf. PowerShells Get-Item -Stream * macht dasselbe mit saubererer Ausgabe. streams.exe von Sysinternals durchläuft einen Baum. Alle drei funktionieren auf dem Volume, auf dem du arbeitest. Keines hilft, wenn das Volume offline ist oder du nicht weißt, welche Dateien zu prüfen sind.

dir /R C:\Users\bob\Documents\
Get-ChildItem -Recurse | ForEach-Object { Get-Item $_.FullName -Stream * } |
  Where-Object Stream -ne ':$DATA'

Dieser zweite Einzeiler enumeriert jeden Nicht-Default-Stream in einem Baum. Führe ihn auf einer frischen Windows-Installation aus und du hast schon Hunderte Treffer, meist Zone.Identifier. Filtere auf Streams größer als ein paar Hundert Bytes und das Rauschen sinkt schnell.

Warum die MFT die bessere Sichtweise ist

Die On-Host-Befehle zählen pro Datei auf. Sie übersehen, was du nicht zu prüfen gedacht hast. Die MFT tut das nicht. Jedes $DATA-Attribut, benannt oder nicht, wird in den Datensatz an Attributtyp 0x80 geschrieben. Durchlaufe die Tabelle, liste die $DATA-Attribute jedes Datensatzes mit Namen und Größen auf, und du hast in einem Durchgang ein erschöpfendes Inventar jedes Streams auf dem Volume.

Ein paar Dinge, die dir das gibt, die die dateibasierte Enumeration nicht gibt:

  • Streams in Verzeichnissen, in die du nie geschaut hättest, einschließlich C:\Windows\Temp\ und C:\$Recycle.Bin\.
  • Streams auf gelöschten Dateien. Der MFT-Datensatz überlebt das Löschen; das ADS ist immer noch im Attributstrom gelistet. fls und istat aus dem Sleuth Kit zeigen sie; ebenso MFTECmd und die omerbenamram/mft-CLI.
  • Residente Streams, deren Inhalt inline im MFT-Datensatz liegt. Kleine Payloads (PowerShell-Loader, Base64-Blobs, kodierte Konfigs) passen häufig in den Slack eines 1.024-Byte-Datensatzes. Du kannst sie direkt aus dem MFT-Extrakt lesen, ohne je den Datenbereich anzufassen. Siehe residente Daten für die Größenschwelle.
  • Stream-Namen. Die MFT bewahrt den Namen, den der Angreifer gewählt hat. :payload.exe sieht harmlos in einer Liste von Dateigrößen aus; er ist laut, wenn du einen Attribut-Dump nach exe grep'st.

Der Triage-Filter, den ich tatsächlich verwende

Nach dem Parsen der MFT mit MFTECmd oder mft_dump exportiere ich jeden Datensatz mit mehr als einem $DATA-Attribut. Dann verwerfe ich:

  • Alles, dessen zusätzlicher Stream-Name Zone.Identifier und unter 1 KB groß ist.
  • Alles in \Windows\WinSxS\ (der Manifest-Store, voll legitimer Streams).
  • Alles unter einem von einem Anbieter signierten Anwendungsverzeichnis, dessen Stream ein bekannter Thumbnail- oder Metadaten-Schlüssel ist.

Was übrig bleibt, ist das Arbeitsset. Auf einer sauberen Workstation sind das meist unter hundert Datensätze. Auf einem Host, auf dem etwas Interessantes passiert ist, zeigen sich die Deltas sofort.

Querverweise zu den Überlebenden mit dem USN-Journal, um zu sehen, wann jeder Stream erstellt oder zuletzt geändert wurde. Ein benanntes $DATA-Attribut, das um 02:14 erschien und seitdem nicht angefasst wurde, ist einen näheren Blick wert. Paare es mit Sysmon und Security-Ereignisprotokollen, falls vorhanden: Sysmon Event ID 15 (FileCreateStreamHash) ist die eine Event-ID, die ADS-Erstellung mit dem Hash des Stream-Inhalts erfasst.

Eine Anmerkung zu Defender

Modernes Defender scannt ADS standardmäßig. Er ist wirklich gut beim Brute-Force-Muster "lege eine bekannte schädliche PE in einen Stream". Er ist weniger gut bei kleinem, obfuskiertem PowerShell, das in Streams versteckt ist, und er fasst nicht immer Streams in ausgeschlossenen Pfaden an. Gehe nicht davon aus, dass Defenders Schweigen ein Beweis für Abwesenheit ist.

Weiterführende Literatur

  • Microsoft, File Streams. Die offizielle Mechanik, einschließlich der BackupRead/BackupWrite-API, die alle Streams kopiert.
  • Sleuth Kits istat und fls für die Offline-Enumeration, die ADS sichtbar macht.
  • Mark of the Web-Dokumentation von Microsoft dazu, was Zone.Identifier tatsächlich macht und wie Office es verwendet.

Externe Ressourcen