Memory layout and buffer overflow(8)

G·2022년 11월 6일
1

2-2 System programming

목록 보기
6/15
post-custom-banner

Memory layout and buffer overflow

메모리 영역의 구조와 스택상의 버퍼 오버플로우를 알아보자.

Memory layout

메모리 구조를 살펴보자. 메모리 구조중 스택을 중점적으로 살펴볼 것이다.

  • Stack: runtime stack이다. 8MB 제한에 지역변수들이 저장된다.
  • Heap: 동적할당된 변수들이 저장된다. malloc(), calloc(), new() 연산자로 할당한다.
  • Data: 정적으로 할당된 변수들이 저장된다.
  • Text / Shared Libraries: 읽기전용 공간이다. 함수의 이름(주소)가 저장된다.
    Memory layout

malloc() Examples

Memory Referencing Bug Example


a의 배열 크기는 2이다. 함수의 인자로 0, 1을 전달하는 경우엔 문제가 생기지 않는다. 하지만 2 이상일 경우에 문제가 생긴다. 이는 구조체가 원소를 인접한 메모리에 저장하는 경우를 생각해보면 된다. 메모리의 스택은 주소가 낮아지며 쌓인다. 이 경우에, a의 인덱스가 증가할수록 주소가 증가하기 때문에 double d 변수까지 영향을 주는 것이다.
이런 경우를 stack buffer overflow라고 한다.
방금 위의 경우가 배열의 크기를 넘어선 인덱싱이다. 다른 경우를 확인해보자

  • 무분별한 길이의 문자열 입력
  • scanf(), strcpy(), strcat() 등의 함수가 있다.
    특히 char 타입의 배열의 크기를 넘어 저장될 경우 이를 stack smashing이라 부른다.

Buffer Overflow Example


위의 코드를 보면 getchar 함수를 사용하여 문자를 입력받고 이를 인자로 받은 char 타입 배열에 저장한다. eof나 널문자를 받기 전까지 입력을 받고 배열을 리턴한다.

크기 4인 char 타입 배열을 선언하고 위 함수를 호출하면 결과는 이렇다.

  • 24개의 문자는 정상적으로 잘 입력되었다.
  • 25개의 문자는 Segmentation Falut 결과를 낳는다.


문자값을 입력받기 이전의 스택 상태이다. 20 bytes의 입력받지 않는 공간이 있다.
어셈블리어를 보면 24 bytes만큼 공간을 할당하고 있다.

23개의 값을 입력받는다면 널문자까지 포함하여 잘 저장되기 때문에 오류가 나지 않는다.

25개를 입력받는다면 오류가 난다. return 주소가 수정되었기 때문에 오류가 난 것이다.
만약 return 주소가 수정되었어도 주소가 유효하다면 return 주소로 jmp하게 된다.

Code Injection Attacks


B의 주소는 리턴 주소이다. 이를 유도한 주소로 바꾸어 정보를 빼내거나 할 수 있게 된다.
이러한 해킹을 막기위한 방법을 알아보자.

Avoid Overflow in Code


첫 번째로 크기 제한이 있는 함수를 사용하는 것이다. gets() 대신 fgets()를 사용하고,
strcpy 대신 strncpy를 사용하면 된다.

Systemp-Level Protections can help

Randomized stack offsets

  • 프로그램이 시작될 때, 임의의 공간을 스택에 할당한다.
  • 스택의 주소를 프로그램이 시작될 때마다 교체한다.

이 두 가지 부분을 활용하여 해커가 코드의 특정 주소를 모르게 만들 수 있다.

Stack Canaries

stack canaries는 읽기만 가능한 임의의 값을 사용한 것이다.
임의의 값을 buffer 위에 할당한다.
이 값이 바뀌었으면 stack overflow attack이 일어난 것이다.

위의 사진처럼 리턴 주소와 버퍼 사이에 canary 값을 할당한다면 버퍼 크기 이상의 값이 할당되었을 때 canary값이 바뀌게 된다. 이를 함수의 호출 전후로 확인하여 값이 바뀌었다면 stack smashing을 확인하고 실행을 종료한다.

어셈블리어를 확인해보자.

%rax 레지스터에 canary 값을 저장하고 stack에 저장한다.
함수가 끝난 이후 canary 값을 삭제한다.

canary 값을 stack에서 가져온다 이후 canary값을 이전의 값과 비교한다.
만약 값이 다르다면 stack_chk_fail을 호출한다.

profile
열심히 안 사는 사람
post-custom-banner

0개의 댓글