Създаване на изображение, което е и валиден JavaScript файл

Оригиналът е на Sebastian Stamm

5
1437

Изображенията обикновено са двоични файлове, а JavaScript файловете са си обикновен текст. И двата типа файлове имат свой точно определен файлов формат, който по свой начин кодира данните. За да могат JavaScript файловете да се изпълняват, те трябва да спазват определен синтаксис. А аз се запитах, дали не може да се направи файл, който хем е изображение, хем има допустим за JavaScript синтаксис и може да се стартира?

Избор на подходящото изображение

За съжаление, изображенията съдържат голямо  количество двоични данни, които представени като JavaScript код ще дават грешки. Ето защо, първата идея, която ми хрумна бе просто да поместя всичките данни на изображението в един голям коментар – например, ето така:

/*ALL OF THE BINARY IMAGE DATA*/

Това разбира се, ще бъде един допустим и валиден JavaScript файл. Само че файловете с изображения трябва да започват с определена поредица от байтове, който са специфични за всеки графичен формат, Така например, PNG файловете винаги започват със следната последователност от байтове: 89 50 4E 47 0D 0A 1A 0A. Ако файлът с изображението започне със символите /*, то тази файл вече няма да бъде графичен и системата няма да го разпознае като изображение.

Това начало на графичния файл ме подсети, че ако използвам тази поредица от байтове като име на променлива, която да има същото значение, като по-горния коментар в сорс кода:

PNG=`ALL OF THE BINARY IMAGE DATA`;

Можем да използваме шаблонни низове вместо обикновени низове " или ', понеже бинарните данни може да имат прекъсвания и шаблонните низове се справят по-добре в подобни ситуации.

За съжаление, повечето поредици от байтове в началото на графичните файлове съдържат непечатими символи, които не могат да бъдат използвани като имена на променливи. Но има един единствен графичен формат, който можем да използваме и това е GIF форматът. Всички GIF файлове започват с байтовете  47 49 46 38 39 61,което в ASCII код изглежда като низа GIF89a, а това е едно съвсем допустимо и нормално име на променлива!

Избор на най-подходящите размери на изображението

Сега, когато избрахме какъв графичен формат да използваме, който започва с байтовеподходящи за име на променлива, остава да добавим символите на знака за равенство и на обратния апостроф (backtick). А това означава, че следващите четири байта трябва да бъдат 3D 09 60 04.

Първите байтове на изображението

В GIF формата, четирите байта след заглавните байтове задават размера на изображението. Ние трябва в тези четири байта да вмъкнем шестнадесетичното число 3D (знакът за равенство) и 60 (обратният апостроф за началото на коментара). В GIF се използва little endian кодирането, при което вторият и четвъртият байт оказват огромно влияние на размерите на изображението. Те трябва да бъдат възможно най-малки, за да не получим изображение с широчина и дължина от хиляди пиксели. Тоест числата 3D и 60 ще трябва да заемат байтовете, които не оказват голямо влияние на размера.

Вторият байт за ширината на изображението трябва да бъде някакъв допустим символ, който не се вижда на екрана (whitespace), и който ще играе ролята на спейс между знака за равенство и началото на низа GIF89a= `….. Да не забравяме, че това шестнадесетично число трябва да бъде колкото се може по-малко, защото иначе изображението ще стане огромно.

Най-подходящият символ в тези условия е 09 (хоризонтална табулация). По този начин ще получим ширина на изображението 3D 09, което в little endian кодирането е числото 2365. Малко по-широко, отколкото ми се искаше, но е приемливо.

За втория байт на височината е добре да изберем значение, даващо добро съотношение на страните. Аз избрах 04, което дава височина 60 04, или 1120 пиксела.

Да вмъкнем JavaScript код в графичния файл

Засега нашият изпълним GIF файл нищо не може да прави. Той просто присвоява на глобалната променлива GIF89a един дълъг низ. Но ние искаме нещо по-интересно, нали? Основната част от данните в един GIF файл се използва за кодиране на изображението и ако опитаме там да вмъкнем JavaScript код, то изображението силно ще се промени. Но поради някаква причина, GIF форматът съдържа нещо, което се нарича Comment Extension. Това място е заделено за записване на метаданните, които не се интерпретират от GIF декодера. Това е идеалното място за вмъкването на нашия JavaScript код.

Това място за коментари се намира веднага след таблицата за цветовете на GIF формата. И понеже там може да бъде записана всякаква информация, то ние можем да поместим там затварянето на низа GIF89a, после целия JavaScript код и след това да маркираме началото на дълъг коментар, за да не може останалата част на изображението да окаже влияние на JavaScript парсъра.

Накрая нашият файл може да изглежда по следния начин:

GIF89a= ` BINARY COLOR TABLE DATA ... COMMENT BLOCK:

`;alert("Javascript!");/*

REST OF THE IMAGE */

Но тук има едно неголямо ограничение. Въпреки че самият блок с коментари може да има произволен размер, той е съставен от подблокове с максимален размер от по 255 символа. Ето защо, за да можем да поберем в това място един голям скрипт, ще трябва да разделим този скрипт на малки фрагменти по следния начин:

alert('Javascript');/*0x4A*/console.log('another subblock');/*0x1F*/...

Шестнадесетичните кодове в коментарите всъщност са байтовете, определящи размера на следващия подблок. Това го няма в JavaScript, но е задължително за графичния формат GIF. За да не пречат на другия код, те трябва да бъдат оформени като коментари. Аз написах един неголям скрипт, който автоматизира тази работа.

Изчистваме бинарните данни

Сега, когато вече разполагаме с базовата структура, трябва да направим така, че бинарните данни на изображението да не оказват влияние на JavaScript кода. Както казахме по-горе, този файл се състои от три части – в първата се намира променливата с име GIF89a, която получава за значение един дълъг низ, втората част включва самия JavaScript код, а третият е коментар от няколко дълги низа.

Нека се вгледаме по-подробно в първата част, където се извършва присвояване на значението на променливата.

GIF89a= ` BINARY DATA `;

Ако бинарните данни включват символа ` или съчетанието от символи ${, то ще имаме проблем, понеже по този начин или ще бъде сложен край на първия низ, или ще се създаде недопустим израз. Това може лесно да се оправи – достатъчно е да се променят двоичните данни! Например, вместо символа ` с шестнадесетичен код 60, можем да използваме шестнадесетичното число 61. И тъй като тази част на файла съдържа палитрата на цветовете, то това ще доведе до незначителни промени в някои цветове. Например, вместо използването на цветовия отенък #286148 ще се получи отенъка #286048. Едва ли някой ще забележи подобна малка разлика.

Да се преборим с изкривяванията

В края на JavaScript кода ние започнахме многоредов коментар, за да не могат бинарните данни на изображението да влияят на парсинга на java script:

alert("Script done");/*BINARY IMAGE DATA ...

Ако някъде в изображението се срещне комбинацията */, то коментарът ще завърши преждевременно и JavaScript файлът ще стане недопустим. Тук също можем лесно да променим някой от тези два символа, за да не стане така, че те да завършат коментара. Само че този път се намираме в раздела с кодираното изображение, където дори и най-малката промяна поврежда това изображение. Ще се получи нещо подобно:

В най-неблагоприятния случай изображението изобщо няма да се покаже. Аз избрах да променя само един бит, който инвертирах и по този начин изкривяванията бяха сведени до минимум. За щастие, имаше само няколко места със съчетанието */. Но в крайното изображение все пак се виждат малки изкривявания, като например в долната част на реда Valid JavaScript File, но като цяло съм доволен от резултата.

Завършваме файла

Остана ни само едно – да завършим този файл, който едновременно е изображение и правилен JavaScript файл. Понеже това е краят на файла, той трябва да завърши с байтовете 00 3B, а това означава, че трябва да приключим дългия коментар по-рано. Но тук различните промени в този файл не са особено забележими и аз просто добавих коментар от един ред, който няма да предизвика проблеми с парсинга:

/* BINARY DATA*/// 00 3B

Да се справим с браузъра

Тук трябва да преодолеем следното препятствие: ако качим това изображение на сървър и опитаме да го използваме с тага script, то най-вероятно ще получим следната грешка:

Refused to execute script from ‘http://localhost:8080/image.gif’ because its MIME type (‘image/gif’) is not executable. [Отказ от изпълнение на скрипта от ‘http://localhost:8080/image.gif’, понеже неговият MIME тип не е изпълним].

Тоест браузърът съвсем правилно си казва, че ‘Това си е изображение и аз няма да го стартирам‘. И повечето случаи това е съвсем уместно. Но ние все пак искаме да го изпълним. Решението е в това, просто да не казваме на браузъра, че това е изображение. За тази цел написах един съвсем опростен сървър, който подава изображението без информация в заглавието.

Без информация за MIME типа в заглавието, браузърът няма как да знае, че това е изображение и и ще извърши именно това, което се подразбира в контекста – ще го покаже като изображение при използването на тага  <img> и ще го стартира като JavaScript файл при използването на тага <script>.

Но… за какво ние всичко това?

И сам не зная. Просто за удоволствие – да си докажа, че мога да се справя с една доста сложна задача. Това е просто едно упражнение за ума и ако измислите ситуация, в която всичко това може да бъде полезно, моля, обадете ми се, за да споделим и умножим познанието.


Авторът на този материал проявява излишна скромност. Във форумите активно се обсъжда, че описаният по-горе метод е перфектен за скриването на зловреден код и създаването на още едно семейство компютърни вируси за осъществяването на доста весели атаки срещу сървъри и браузъри. Преди години имаше нещо подобно – haskell код в GIF файл. Днес този вид атаки успешно се прихващат и отразяват от антивирусния софтуер. Но винаги се появява нещо ново… Има информация, че не всички антивирусни програми могат да се справят с подобно чудо. VirusTotal например, нищо не казва за този JavaScript код, който едновременно е и изображение. Може би защото няма интегриран вредоносен код.

Методът може да се използва и за скрито предаване на чувствителна информация и за още много други неща. А вие сещате ли се за какво още може да се използва този метод?

 

4 4 гласа
Оценете статията
Абонирай се
Извести ме за
guest
5 Коментара
стари
нови оценка
Отзиви
Всички коментари