Shellcode

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by EP025, 2 Mar 2016.

  1. EP025

    EP025 New Member

    Joined:
    1 Jan 2016
    Messages:
    25
    Likes Received:
    2
    Reputations:
    0
    X86, Ubuntu

    Здравствуйте!) Ребят, недавно столкнулся с проблемой, она связана с исполнением шеллкода в программе на Си.
    Я написал(если честно, то скопировал) простенькую программу на асме, получил шестнадцатеричный код objdump'ом(нуль-байтов нет) и вставил в программу на Си. По сути программа должна выводить на экран строку.
    Программа, написанная на асме полностью выполняет то, что предполагалось. Но Си-программа, которая должна выполнить шестнадцатеричный код asm-программы завершается с ошибкой "segmentation fault".
    ASM:
    ________________
    [SECTION .text]
    global _start
    _start:
    jmp short ender

    starter:

    xor eax, eax
    xor ebx, ebx
    xor edx, edx
    xor ecx, ecx

    mov al, 4
    mov bl, 1
    pop ecx
    mov dl, 18
    int 0x80
    xor eax, eax
    mov al, 1
    xor ebx, ebx
    int 0x80

    ender:
    call starter
    db 'This is string'
    _______________
    Cи:


    #include <stdio.h>

    char code[] = "\xeb\x19\x31\xc0\x31\xdb\x31\xd2\x31\xc9"
    "\xb0\x04\xb3\x01\x31\xc9\xb0\x04\xb3\x01"
    "\x59\xb2\x12\xcd\x80\x31\xc0\xb0\x01\x31\xdb"
    "\xcd\x80";

    int main(int argc, char *argv[])
    {
    int (*shell)();
    shell = (int (*) () ) code;
    (int)(*shell)();
    }
    _________________________-
    Помогите пожалуйста разобраться. Большое спасибо. :)
     
  2. GRRRL Power

    GRRRL Power Elder - Старейшина

    Joined:
    13 Jul 2010
    Messages:
    823
    Likes Received:
    185
    Reputations:
    84
    С первого взгляда могу предположить, что ты вызываешь шелл-код в Си как функцию, но на самом деле по адресу code находится не функция, а последовательность ассемблерных инструкций без пролога и эпилога. Во-вторых, ассемблерный код не должен быть базозависимым, а он такой и есть у тебя, потому что он осуществляет переходы по меткам (starter, ender), которые, скорее всего, компилируются в переходы по абсолютным адресам, а те, в свою очередь, в сишной программе уже недействительны.
     
    #2 GRRRL Power, 9 Mar 2016
    Last edited: 10 Mar 2016
    EP025, Kaimi and totenkopf like this.
  3. VY_CMa

    VY_CMa Green member

    Joined:
    6 Jan 2012
    Messages:
    917
    Likes Received:
    492
    Reputations:
    724
    Code:
    
    int main(int argc, char *argv[])
    {
    unsigned char code[] = "\xeb\x19\x31\xc0\x31\xdb\x31\xd2\x31\xc9"
    "\xb0\x04\xb3\x01\x31\xc9\xb0\x04\xb3\x01"
    "\x59\xb2\x12\xcd\x80\x31\xc0\xb0\x01\x31\xdb"
    "\xcd\x80";
        __asm {
            lea eax, code
            call eax
        }
    }
    
     
    _________________________
  4. GRRRL Power

    GRRRL Power Elder - Старейшина

    Joined:
    13 Jul 2010
    Messages:
    823
    Likes Received:
    185
    Reputations:
    84
    > Синтаксис инлайн-асма Intel для Visual Studio или MASM под Windows
    > Автор просит под Ubuntu (gcc поддерживает только синтаксис AT&T), и его шелл-код с линуксовыми прерываниями
    > Ответ, видимо, даже не проверен и даже не скомпилирован под *nix
     
  5. KIR@PRO

    KIR@PRO from Exception

    Joined:
    26 Dec 2007
    Messages:
    826
    Likes Received:
    291
    Reputations:
    359
    VY_CMa ты когда начал на ассемблере кодить? :)


    По теме:
    Компилируй NASM:
    nasm -f bin -o data.bin myfile.asm
    Только после [section .text] добавь строчку use32
    А строку call starter замени call near starter

    Потом его хекс редактором открываешь и копируешь в стиле С и вставляешь.

    Вызов в С правильный, но только в этом случае, т.к. ассемблекный код вызывает в конце exit, и обработка стека особо не важна.
    А по хорошему надо соблюдать stdcall

    А вообще поищи ссылочку на shellstorm
     
    _________________________
    #5 KIR@PRO, 11 Mar 2016
    Last edited: 11 Mar 2016
    EP025 likes this.
  6. Ins3t

    Ins3t Харьковчанин

    Joined:
    18 Jul 2009
    Messages:
    939
    Likes Received:
    429
    Reputations:
    139
    https://en.wikipedia.org/wiki/Executable_space_protection

    Помимо прочего секция данных скорее всего не исполняема. Нужно выделять кусок виртуальной памяти, ставить ей права на исполнение и передавать управление туда.
     
    EP025, GRRRL Power and KIR@PRO like this.
  7. EP025

    EP025 New Member

    Joined:
    1 Jan 2016
    Messages:
    25
    Likes Received:
    2
    Reputations:
    0
    Угу... то есть Си-программа пытается перейти по адресу, которого вообще нет? На shellstorm'e я посмотрел исходники - ты прав, ни сегментов, ни меток там действительно нет и код выполняется прекрасно. Все строки передаются через стек, а затем в регистры. В общих чертах я понял, спасибо. :)
     
  8. EP025

    EP025 New Member

    Joined:
    1 Jan 2016
    Messages:
    25
    Likes Received:
    2
    Reputations:
    0
    Спасибо всем за помощь!)
     
  9. EP025

    EP025 New Member

    Joined:
    1 Jan 2016
    Messages:
    25
    Likes Received:
    2
    Reputations:
    0
    Подскажи пожалуйста, как это осуществить?
     
  10. KIR@PRO

    KIR@PRO from Exception

    Joined:
    26 Dec 2007
    Messages:
    826
    Likes Received:
    291
    Reputations:
    359
    третий параметр отвечает за права участка памяти. Обьединяешь PROT_READ|PROT_WRITE|PROT_EXEC и можешь читать/писать/исполнять данные в этом участке памяти.

    p.s. в man 2 .... внизу есть пример
    p.p.s. первый параметр это не адрес твоей функции которую ты вызываешь, это начала участка памяти выделенного под данные, он выравнен на границу 4кб вроде, но точно не помню.
     
    _________________________
  11. EP025

    EP025 New Member

    Joined:
    1 Jan 2016
    Messages:
    25
    Likes Received:
    2
    Reputations:
    0
    Спасибо. :) Только теперь возникла другая проблема: не могу разместить код в выделенном участке памяти.
    Если размещаю до вызова mprotect, то сама функция mprotect не выполняется и выкидывает ошибку "invalid argument". Если размещаю после вызова mprotect, то данные выполняться не хотят.
    Проблема с "неисполняемостью" решилась заменой шеллкода(брал на shellstorm'е), но хочется понять, почему не работает mprotect. Почему в первом случае он сообщает о неверном аргументе, а во втором шеллкод просто не перводится в машинные команды? Флаги PROT_READ, PROT_WRITE и PROT_EXEC включены.
     
  12. KIR@PRO

    KIR@PRO from Exception

    Joined:
    26 Dec 2007
    Messages:
    826
    Likes Received:
    291
    Reputations:
    359
    На код бы глянуть =)
     
    _________________________
  13. EP025

    EP025 New Member

    Joined:
    1 Jan 2016
    Messages:
    25
    Likes Received:
    2
    Reputations:
    0
    Моя оплошность, каюсь. Собственно код:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/mman.h>
    #include <string.h>
    #include <errno.h>

    int main(int argc, char *argv[])
    {
    permissions();
    return 0;
    }

    int permissions(char *addr)
    {

    int page_size = getpagesize();
    addr -= (unsigned long)addr % page_size;
    addr = "\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3"
    "\x50\x53\x89\xe1\xb0\x0b\xcd\x80";

    if(mprotect(addr, page_size, PROT_READ|PROT_WRITE|PROT_EXEC) == -1)
    {
    perror("Can't to run mprotect\n");
    return -1;
    }

    int (*exeshell)();
    exeshell = (int (*) () )addr;
    (int)(*exeshell)();
    }

    Догадываюсь, что проблема может быть в неверном выравнивании.
     
  14. KIR@PRO

    KIR@PRO from Exception

    Joined:
    26 Dec 2007
    Messages:
    826
    Likes Received:
    291
    Reputations:
    359
    первое, что бросается в глаза:
    1) вызов без параметров permissions() - в функции addr = (char *)0x00
    2) присвоение указателя: addr = "\x32\xc0..." тут ты присвоил указателю addr новый адрес который указывает на статическую строку находящуюся по другому адресу, тогда предыдущее выравнивание было бесполезно.

    Вот так должно завестись:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/mman.h>
    #include <string.h>
    #include <errno.h>
    
    char *shellcode = "\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
    
    int main(int argc, char *argv[])
    {
      permissions(shellcode);
      return 0;
    }
    
    int permissions(char *addr){
      int page_size = getpagesize();
      char *addr_align = (char *)addr - ( (u_long)addr % page_size );
      if( -1 == mprotect( addr_align, page_size, PROT_WRITE|PROT_READ|PROT_EXEC ) ){
       perror("mprotect:");
        exit(0);
      }
      int (*shell)();
      shell = int (*)()addr;
      shell();
    }
    

    p.s.
    :D
     
    _________________________
    #14 KIR@PRO, 3 Apr 2016
    Last edited: 4 Apr 2016
    EP025 likes this.
  15. EP025

    EP025 New Member

    Joined:
    1 Jan 2016
    Messages:
    25
    Likes Received:
    2
    Reputations:
    0
    Спасибо большое дружище, завтра проверю на свежую голову и зароюсь в документации.
    А еще вопрос: если по умолчанию стек является неисполняемым, то данные в специально выделенной области памяти с флагом PROT_EXEC могут выполняться?
     
    #15 EP025, 4 Apr 2016
    Last edited: 6 Apr 2016