Blog

Sicheres Fuzzing und Parsen

Jeremy Mill
May 19, 2021

Hier bei FloQast analysieren wir ziemlich viel, insbesondere in der Sprache der Buchhalter, bekannt als xlsx. Im Rahmen unseres normalen Secure Software Development Lifecycle (SSDLC) und unseres Programms zur Anwendungssicherheit (AppSec) im Allgemeinen testen wir die Komponenten, die Teil unseres Produkts werden. Die Tests umfassen nicht nur die normalen OWASP-Top-10-Probleme, sondern auch fortgeschrittenere Angriffstechniken wie Fuzzing.Fuzzing gibt einem Programm wiederholt zufällige oder pseudozufällige Eingaben mit dem Ziel, unbehandelte Fehler oder unbeabsichtigtes Verhalten zu entdecken. Oft werden Abstürze, Denial-of-Service-Bedingungen (wie Endlosschleifen) oder andere potenziell gefährliche Szenarien aufgedeckt.

Der Fuzzer

Ein großer Teil unseres Tech-Stacks ist eingeschrieben Node.js, also muss unser Fuzzer JavaScript als erstklassiges Ziel unterstützen. Zum Glück für uns hat GitLab im Juni 2020 das hervorragende Sicherheitsunternehmen Peach Tech gekauft. Peach Tech ist das Unternehmen hinter exzellenten Tools wie:

Pfirsich-Fuzz

  • Python Fuzz
  • jsfuzz
  • und eine Reihe anderer großartiger Sicherheitstools

Das war ein Glück für uns, denn GitLab hat weitergemacht jsfuzz Open Source. Es ist hier zu finden: jsfuzz.

jsfuzz ist ein „Coverage-Guided Fuzzer“, was bedeutet, dass anstatt rein zufällige Eingaben für eine Anwendung zu verwenden, versucht er, die Eingabe zu modifizieren, um Testfälle zu generieren, die so viele Zweige des Quellcodes wie möglich testen. Das heißt, wir sollte erhalten Sie genauere Ergebnisse und ein höheres Maß an Sicherheit, dass unser Fuzzing die zu testende Software angemessen getestet hat. Andere deckungsgesteuerte Fuzzer sind AFL und Lib Fuzzer.

Einer der besten Teile von jsfuzz ist, dass das Setup einfach ist. Am schwierigsten beim Fuzzing ist es meistens, alles einzurichten. Im Vergleich zu anderen Fuzzing-Frameworks war dies ein Kinderspiel und die Betreuer bieten eine vergleichsweise große Anzahl von Beispiele.

Das Ziel und der Korpus

Unser Ziel war diesmal eine Bibliothek namens Blatt js was auch bekannt ist als xlsx und js-xlsx auf npm: https://www.npmjs.com/package/xlsx. Die Bibliothek ist mit etwas mehr als 1.000.000 Downloads/Woche relativ beliebt. Das xlsx Die Bibliothek hat eine Pro-Version mit erweiterten Funktionen sowie die Open-Source-Version. Auf einer hohen Ebene ist diese Bibliothek eine:

Parser und Writer für verschiedene Tabellenkalkulationsformate. Pure-JS-Reinraum-Implementierung anhand offizieller Spezifikationen, zugehöriger Dokumente und Testdateien. Der Schwerpunkt liegt auf der Robustheit beim Parsen und Schreiben, der formatübergreifenden Funktionskompatibilität mit einer einheitlichen JS-Darstellung und der ES3/ES5-Browserkompatibilität zurück zu IE6.

-EntNOMMEN aus der Projektbeschreibung auf npm

Die hier beschriebenen Tests wurden mit der Open-Source-Version durchgeführt, wobei der neueste Commit für den Haupt Zweig ihrer GitHub-Repository.

Auf einer hohen Ebene war unser Plan:

  • Geben Sie eine „bekannt“ gute XLSX-Datei als Korpus an
  • lass jsfuzz laufen, bis wir einen Absturz bekommen
  • für jeden Absturz:
    • Prüfen Sie, ob es sich um einen Fehler handelt, den wir überspringen sollten
      • Falls ja, füge es unserer „Ignorierliste“ hinzu
      • Wenn nein, füge es zu unseren Ergebnissen hinzu
  • Für jeden Absturz in unseren Ergebnissen:
    • Bestätigen Sie den Fehler anhand eines Beispiels aus der „realen Welt“
    • Bewerten Sie die Auswirkungen des Fehlers

Ein „Korpus“ ist eine zweifelsfrei funktionierende Datei oder ein Satz von Dateien. Wenn Sie diese Dateien einem Coverage-gesteuerten Fuzzer übergeben, kann dies die Geschwindigkeit und Effizienz des Fuzzers erheblich erhöhen, verglichen mit dem Starten mit zufälligen Bytes. In diesem Fall war unser Korpus ein einziger xlsx Datei benannt cat.xlsx das war eine so einfache Excel-Datei wie möglich: abc2cat11.15

Obwohl ich mit einer einzigen Datei angefangen habe, sollten Sie idealerweise mehrere Dateien bereitstellen, um unsere Wahrscheinlichkeit zu erhöhen, mehr (nicht erkundete/unscharfe) Codepfade zu erreichen. Wir haben gespeichert cat.xlsx in einem Ordner mit dem Namen Korpus zusammen mit unserem Fuzzer fuzz.js, die wir im nächsten Abschnitt erstellen werden.Lass uns anfangen zu fuzzing!

Lassen Sie uns zuerst jsfuzz installiert.

npm install -g jsfuzz

Jetzt erstellen wir unser Fuzzer-Ziel. Das ist unsere Startdatei, die nicht weiß, wie man damit umgeht. irgendein Fehler. Ich habe meins benannt fuzz.js und sah so aus:

const xlsx = require('xlsx');

async function fuzz (bytes) {
  try {
    await xlsx.read(bytes)
  } catch (error) {
    if (!acceptable(error)) throw error
}

function acceptable (error) {
  return !!expected
    .find(message => error.message.startsWith(message))
}

const expected = [
]

exports.fuzz = fuzz

Diese Datei:

  • Importiert unsere Bibliothek im Test
  • Definiert eine 'Fuzz'-Funktion, die jsfuzz wird anrufen
  • Richtet unsere Fehlerbehandlung für erwartete Fehler ein
  • Vergiss nicht, die „Fuzz“ -Funktion zu exportieren, was einen Ingenieur dazu bringen würde, seine Berufswahl in Frage zu stellen

Es wird fast immer Fehler geben, die wir beim Fuzzing ignorieren wollen. Stell dir ein jpeg Parser und ein Fehler, der besagt ungültiges Format. Das ist ein Fehler, der uns beim Fuzzing nicht interessiert, wir werden wahrscheinlich Tausende davon erstellen! Wenn wir unsere Tests ausführen und Fehler entdecken, die wir ignorieren möchten, können wir mit der Eingabe von Daten beginnen erwartet Array.Mal sehen, wie wir unseren Fuzzer mit unserem Korpus starten:

jsfuzz ./fuzz.js corpus

Nach ein paar Durchläufen sah mein Satz erwarteter Fehler so aus:

const expected = [
  'Unrecognized LOTUS BOF',
  'Unexpected BIFF Ver',
  'Bad Gutters',
  "Cannot set property 'ImData'",
  "Cannot read property",
  'MulBlank read error',
  'Bad SerAr',
  'Corrupted zip',
  'End of data reached',
  'Header Signature',
  'CFB file size',
  'Cannot find file',
  'Unsupported ELFs',
  'RangeError',
  'String record expects Formula'
]

Umgang mit Fehlern ohne Meldungen

Beim Testen ist ein Problem mit meinem Error-Handler aufgetreten. Ich bekam unbehandelte Ausnahmen von Knoten und nicht xlsx das hatte kein Botschaft mit ihnen verbunden. Um diese Fehler zu beheben, habe ich einige Änderungen an meinem vorgenommen akzeptabel funktion:

function acceptable (error) {
  if (error instanceof RangeError && error.code === 'ERR_BUFFER_OUT_OF_BOUNDS') {
    console.log('---skipping RangeError')
    return true
  }else if (error instanceof RangeError) {
    console.log('---skipping unknown RangeError')
    return true
  }
  return !!expected
    .find(message => error.message.startsWith(message))
}

Ich hätte das definitiv etwas eleganter handhaben können, aber in diesem Fall hat es gut genug funktioniert. Ich wollte diese überspringen Bereichsfehler weil sie es nicht wirklich getan haben Absturz irgendwas und ich wollte ein paar saftigere Bugs finden. Die Ergebnisse

Innerhalb von etwa einer Stunde wurden drei potenzielle Fehler mit hohem Schweregrad entdeckt. Zwei waren nicht genügend Arbeitsspeicher Ausnahmen, die abgestürzt sind Node.js was zu einer Denial-of-Service-Bedingung führte, und eine davon war eine Bedingung, die dazu führte, dass die Anwendung hängen blieb und 100% der CPU verbrauchte. Diesen Problemen wurden die folgenden CVEs zugewiesen:

  • CVE-2021-32012
  • CVE-2021-3-2013
  • CVE-2021-3-2014

Die Fehler wurden dem Anbieter innerhalb des folgenden Zeitplans gemeldet und von ihm gepatcht:

  • 04.05.21 - Erster Lieferantenkontakt
  • 04.05.21 - Diskussion mit dem Anbieter über die Ursache
  • 13.05.21 — Anbieter veröffentlicht Patch
  • 19.05.21 - Dieser Blogbeitrag

Während des Offenlegungsprozesses wurde festgestellt, dass CVE-2021-32013 und CVE-2021-32012 eine gemeinsame Ursache hatten und CVE-2021-32014 nichts damit zu tun hatte.

Ich möchte den Entwicklern auf https://sheetjs.com/ ein großes Dankeschön aussprechen, mit denen es während des Enthüllungsprozesses großartig war, zusammenzuarbeiten. Einen Bug zu bekommen, ihn zu untersuchen und einen Patch in ~9 Tagen zu veröffentlichen ist großartig und bringt frischen Wind. Aktionen, wenn Sie ein Xlsx-Benutzer sind Wenn Sie verwenden xlsx Von sheetjs sollten Sie auf Version 0.17.0 oder die neueste Version der Pro-Version aktualisieren.