Non-alfanumerický JavaScript

Zdroj: SOOM.cz [ISSN 1804-7270]
Autor: .cCuMiNn.
Datum: 16.11.2011
Hodnocení/Hlasovalo: 1.44/25

Pro obfuskaci (znepřehlednění) kódu existuje mnoho metod. My se v tomto článku zaměříme na jednu z nich, která mi připadne nejexotičtější a nejzajímavější. Touto metodou je zápis kódu JavaScriptu bez použití jakéhokoliv alfanumerického znaku. V celém kódu si tak vystačíme pouze s několika speciálními znaky ! { } [ ] ( ) + / '

Pokud se ptáte, jak je možné pomocí těchto pár znaků zapsat kód, kterému bude interpret JavaScriptu rozumět, pak věřte, že jde o skutečně logickou záležitost, která má svá jasně stanovená pravidla.

Výklad začnu uvedením skutečnosti, že je možné se na jakýkoliv znak v řetězci odvolávat pomocí jeho pozice v poli jeho znaků. Například písmeno "c" ze slova "hacking" je možné získat tímto zápisem: "hacking"[2].

Dále se naučíme vyjádřit jakékoliv číslo bez použití alfanumerických znaků. Na úvod nám poslouží sekvence dvou hranatých závorek [], o které si povíme, že představuje prázdné pole. Zkuste si jej vypsat pomocí příkazu alert([]); a uvidíte, že na vás vyskočí prázdné okno. Nyní si řekneme, že pokud před tento řetězec vložíme znak vykřičníku !, provedeme tím konverzi na logickou hodnotu, v tomto případě tedy zápis ![] bude představovat logickou hodnotu false. Opět si to můžete vyzkoušet pomocí výstupu funkce alert(). Pro získání logické hodnoty true, stačí, když celý výraz znegujeme. K tomu nám opět poslouží znak vykřičníku !. Logickou hodnotu true lze tedy zapsat jako sekvenci !![].

Víme, že logické hodnoty je možné vyjádřit také pomocí čísel, kdy false = 0 a true = 1. Pro konverzi logické hodnoty na číslo použijeme aritmetickou operaci přičítání, tedy znak +, který umístíme před zápis logické hodnoty. Pro získání číslice nula tedy použijeme zápis +![] a pro číslici jedna adekvátní zápis +!![]. Získání dalších číslic v řadě bychom pak mohli docílit neustálým přičítáním jedničky, jak ukazuje tabulka níže.

Protože by získání vyšších čísel znamenalo příliš dlouhé řetězce, je vhodnější u víceciferných čísel nejprve převést první číslici na řetězec přičtením prázdného pole [] a spojovat pak jednotlivé číslice za sebe jako textové řetězce. Výsledný řetězec uzavřeme do závorek a přidáním znaku + před tuto závorku, jej převedeme zpět na číslo. V uvedené tabulce je tímto způsobem vyjádřena hodnota 123.

0:+![]
1:+!![]
2:+!![]+!![]
3:+!![]+!![]+!![]
4:+!![]+!![]+!![]+!![]
5:+!![]+!![]+!![]+!![]+!![]
6:+!![]+!![]+!![]+!![]+!![]+!![]
7:+!![]+!![]+!![]+!![]+!![]+!![]+!![]
8:+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]
9:+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]
123:+((+!![])+[]+(+!![]+!![])+(+!![]+!![]+!![]))

V tuto chvíli už dokážete bez použití alfanumerických znaků zapsat jakékoliv číslo. Vrátím se tedy k získání písmene "c" ze slova "hacking" a upravím zápis pomocí nově nabytých vědomostí. Písmeno "c" získáme zápisem "hacking"[+!![]+!![]]. Nyní už zbývá jen říci si o získávání jednotlivých písmen, pomocí kterých budeme schopni poskládat bez použití alfanumerických znaků celé textové řetězce.

Pro získání téměř jakéhokoli znaku použijeme stejné metody, jako při získávání písmene "c" ze slova "hacking", tedy extrakci znaku z textového řetězce určením jeho pozice. Nemáme-li ovšem možnost zapsat zdrojové řetězce pomocí konkrétních znaků, musíme sáhnout k takovým řetězcům, které jsou již předdefinovány v samotném JavaScriptu. Se dvěma řetězci, které nám JavaScript poskytl, jste se již setkali. Jednalo se o řetězce true a false. Některé z dalších dostupných řetězců jsou společně s non-alfanumerickým zápisem, pomocí kterého je lze získat, uvedeny v následující tabulce.

[![]]+[]"false[object Object]"
[!![]]+[]"true[object Object]"
+[][+[]]"NaN[object Object]"
[][+[]]+[]"undefined"
!!{}/[]+[]"Infinity"
{}+''"[object Object]"

Přičtením prázdného pole tyto objekty nejprve převedeme na řetězec a z nich pak již můžeme extrahovat jednotlivá písmena určením jejich pozice. Pro získání písmene "f" bychom mohli použít zápisu (![]+[])[+[]], který vznikne vyvoláním objektu false a následným určením jeho nultého (prvního) prvku. Získání několika dalších písmen abecedy zobrazuje další tabulka.

a:(![]+[])[+!![]+[]]
b:(([],[][(![]+[])[+!![]+!![]+!![]]+([][([][[]]+[])[+!![]+!![]+!![]+!![]+[]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]]+(!![]+[])[+[]]+(![]+[])[+!![]+!![]+!![]+!![]+[]]+(!![]+[])[+!![]]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]])()+[])[+!![]+!![]+[]]
c:([]+[][(![]+[])[+!![]+!![]+!![]]+([][([][[]]+[])[+!![]+!![]+!![]+!![]+[]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]]+(!![]+[])[+[]]+(![]+[])[+!![]+!![]+!![]+!![]+[]]+(!![]+[])[+!![]]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]])[(+!![]+!![]+!![])+(+[])]
d:([]+[][(![]+[])[+!![]+!![]+!![]]+([][([][[]]+[])[+!![]+!![]+!![]+!![]+[]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]]+(!![]+[])[+[]]+(![]+[])[+!![]+!![]+!![]+!![]+[]]+(!![]+[])[+!![]]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]])[(+!![]+!![]+!![])+(+!![]+!![]+[])]
e:(!![]+[])[+!![]+!![]+!![]+[]]
f:(![]+[])[+[]]
i:([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]
j:(([],[][(![]+[])[+!![]+!![]+!![]]+([][([][[]]+[])[+!![]+!![]+!![]+!![]+[]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]]+(!![]+[])[+[]]+(![]+[])[+!![]+!![]+!![]+!![]+[]]+(!![]+[])[+!![]]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]])()+[])[+!![]+!![]+!![]+[]]
l:(![]+[])[+!![]+!![]+[]]
m:(([][(![]+[])[+[]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]+[]]+(!![]+[])[+[]]+(!![]+[])[+!![]+!![]+!![]+[]]+(!![]+[])[+!![]+[]]][([]+[][(![]+[])[+!![]+!![]+!![]]+([][([][[]]+[])[+!![]+!![]+!![]+!![]+[]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]]+(!![]+[])[+[]]+(![]+[])[+!![]+!![]+!![]+!![]+[]]+(!![]+[])[+!![]]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]])[(+!![]+!![]+!![])+(+[])]+([][([][[]]+[])[+!![]+!![]+!![]+!![]+[]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]]+(!![]+[])[+[]]+(![]+[])[+!![]+!![]+!![]+!![]+[]]+(!![]+[])[+!![]]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]+[]]+(!![]+[])[+!![]+!![]+[]]+([]+[][(![]+[])[+!![]+!![]+!![]]+([][([][[]]+[])[+!![]+!![]+!![]+!![]+[]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]]+(!![]+[])[+[]]+(![]+[])[+!![]+!![]+!![]+!![]+[]]+(!![]+[])[+!![]]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]])[(+!![]+!![]+!![])+(+[])]+(!![]+[])[+[]]+([][([][[]]+[])[+!![]+!![]+!![]+!![]+[]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]]+(!![]+[])[+[]]+(![]+[])[+!![]+!![]+!![]+!![]+[]]+(!![]+[])[+!![]]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+!![]+[]]])()+[])[+!![]+(+!![]+(+!![])+(+!![])+(+!![])+[])]
n:([][[]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]
o:([][([][[]]+[])[+!![]+!![]+!![]+!![]+[]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]]+(!![]+[])[+[]]+(![]+[])[+!![]+!![]+!![]+!![]+[]]+(!![]+[])[+!![]]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]
r:(!![]+[])[+!![]+[]]
s:(![]+[])[+!![]+!![]+!![]]
t:(!![]+[])[+[]]
u:(!![]+[])[+!![]+!![]+[]]
v:([]+[][(![]+[])[+!![]+!![]+!![]]+([][([][[]]+[])[+!![]+!![]+!![]+!![]+[]]+([][[]]+[])[+!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]+!![]]+(!![]+[])[+[]]+(![]+[])[+!![]+!![]+!![]+!![]+[]]+(!![]+[])[+!![]]]+[])[+!![]+!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]])[(+!![]+!![])+(+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])]

Tak a to je o non-alfanumerickém zápisu JavaScriptu téměř vše. Naučili jste se, jak vytvořit jakékoliv číslo a dokonce i libovolný text pouze za použití několika speciálních znaků. Stejným způsobem můžete poskládat také kód JavaScriptu, který následně spustíte pomocí funkce eval(). Sami jistě uznáte, že pouhým pohledem na non-alfanumerický kód je jen těžko odhadnutelné, jaká je pravá funkce skriptu.

Před pár dny jsem k tomuto článku zakládal ve webfóru thread s odkazem na kalkulačku, která byla zranitelná na XSS, ale která neumožňovala zápis písmen abecedy. Mohli jste si na ní sami vyzkoušet, jestli se vám podaří injektovat kód JavaScriptu bez použití písmen abecedy. Jediný, komu se to podařilo, byl StorM Troop3r, který pro zápis a spuštění svého kódu použil Unicode zápis znaků a vystačil si tak pouze se zpětným lomítkem a s čísly. Druhým způsobem je právě použití non-alfanumerického JavaScriptu, který nám umožní zapsat kód alert(1) řetězcem:

((+[][+[]]+[])[++[[]][+[]]]+([![]]+[])[++[++[[]][+[]]][+[]]]+([!![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]]+([!![]]+[])[++[[]][+[]]]+([!![]]+[])[+[]])+'('+(+!![])+')'


V případě testovací kalkulačky je tedy funkční tento odkaz: http://www.hackingvpraxi.cz/articles/xssobfjs_praxe_main.php#((+[][+[]]+[])[++[[]][+[]]]+([![]]+[])[++[++[[]][+[]]][+[]]]+([!![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]]+([!![]]+[])[++[[]][+[]]]+([!![]]+[])[+[]])+'('+(+!![])+')'. Po načtení stránky bude možná nutné její znovunačtení klávesou F5, aby si kalkulačna načetla obsah z URL.


Odkazy k tématu: http://www.nethemba.com/bypassing-waf.pdf
Tag pro vyhledávače: Non-alphanumeric JavaScript