BOF에 취약한 함수
취약한 함수란취약한 함수란 컴파일되기 이전에 프로그래머로부터 작성된 코드 중 버퍼 오버 플로우나 포맷 스트링 공격 등에 노출될 수 있는 함수를 뜻한다. 이러한 함수의 사용은 오류를 발생시키거나 심할 경우 상위 권한까지 탈취될 수 있기에 주의하여야 한다. 따라서 이러한 함수의 어떠한 부분이 취약한지 등을 알고 제작할 때 해당 함수들의 사용을 자제하므로 추가적인 피해를 방지할 수 있다. 취약한 함수에는 대표적으로 gets, scanf 등과 같은 함수로 입력받는 문자열의 크기와 주어진 변수의 크기를 고려하지 않는다는 점이다. 이렇게 변수의 크기를 고려하지 않는 함수들은 입력받은 문자열 등이 변수 공간보다 클 경우, 결국 스택의 다른 곳까지 침범하게 된다. 스택의 다른 요소들이 침범될 경우 BOF 공격 등에 쉽..
2016.03.30
no image
FTZ Level12 //BOF,RTL,EGG,Backdoor
문제확인이번 문제도 level11과 비슷한 문제로 풀 수가 있다. 일단 tmp로 파일을 복사하여 디버깅을 해보자. 이전 문제와 같이 0x108 만큼의 버퍼를 채우고 RET에 새로운 주소값을 채우면 된다. EGG Shell을 이용한 방법Egg쉘을 이용한 방법의 경우 아래와 같이 쉽다.Shell Code = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"getaddr.c #include main(){char *p=getenv("EGG");printf("Address : %p\n",p);} RTL ( Return to Libc)을 이용한 방법RTL은 리턴 주소를 공유 라이브러..
2015.05.10
no image
FTZ Level11 //BOF,EGG,Format String
문제 확인 나중에 디버깅을 하는데 있어서 권한이 없으므로 cp 명령어를 통하여 tmp 폴더로 복사를 하여 분석을 한다.Disas을 통하여 보았을때 Dummy 8바이트가 추가로 붙은 것을 확인 할 수가 있으며, 스텍을 그림으로 나타내면 아래와 같은 모양이 된다. Buffer Over Flow를 통한 풀이 *RET에 넣을 주소를 구하여보자. 아래의 코드에서 25Byte의 Shell Code는 B로 대입을 하고 A는 NOP, C에는 나중에 넣을 RET의 크기 만큼 대입을 하여 x/256x $ESP를 통하여 RET에 넣을 주소를 구하는 과정이다. 여기에서 주의해야 할 사항으로는 ` , ' , " 의 사용에 유의하여야 한다는 것이다.여기서 우리는 0xbffffadc의 주소를 RET로 사용 할 것이다. 그리고 공격..
2015.05.09
취약한 함수란

취약한 함수란 컴파일되기 이전에 프로그래머로부터 작성된 코드 중 버퍼 오버 플로우나 포맷 스트링 공격 등에 노출될 수 있는 함수를 뜻한다. 이러한 함수의 사용은 오류를 발생시키거나 심할 경우 상위 권한까지 탈취될 수 있기에 주의하여야 한다. 따라서 이러한 함수의 어떠한 부분이 취약한지 등을 알고 제작할 때 해당 함수들의 사용을 자제하므로 추가적인 피해를 방지할 수 있다. 


취약한 함수에는 대표적으로 gets, scanf 등과 같은 함수로 입력받는 문자열의 크기와 주어진 변수의 크기를 고려하지 않는다는 점이다. 이렇게 변수의 크기를 고려하지 않는 함수들은 입력받은 문자열 등이 변수 공간보다 클 경우, 결국 스택의 다른 곳까지 침범하게 된다. 스택의 다른 요소들이 침범될 경우 BOF 공격 등에 쉽게 노출될 수가 있으며 이는 공격자가 해당 프로그램을 조작하거나 심지어 상위 권한을 획득할 수 있는 여건을 줄 수가 있다. 이제 이러한 함수들에 대하여 알아보자.


Gets


사용자로부터 문자열을 입력받는 gets 함수는 가장 대표적으로 취약한 함수 중 하나이다. 해당 함수는 문자열을 입력받지만 문자열을 담을 공간의 길이와 입력받은 문자열의 길이를 확인하지 않기 때문에 버퍼오버플로우에 취약하다. 우선 아래의 코드를 확인해보자.

#include <stdio.h>
int main()
{
    char buf[10];
    gets(buf);
    printf("%s\n",buf);
}

char형 배열 buf를 10만큼 선언한 뒤 여기에 사용자로부터 입력을 받고 해당 내용을 출력한다. 이를 어셈블리로 나타내면 위 그림과 같은데, 컴파일 과정에 있어서 CheckEsp와 CheckStackVars와 같이 별도의 함수가 추가되어 있는 것을 확인할 수가 있다. 이 두개는 모두 취약한 함수가 공격당했는가를 확인하는 부분이라 생각하면 된다.


어셈블리의 형태로 보면 사용자로부터 입력을 받을 공간 [ebp+buf]를 gets의 인자로 주기 위해 스택에 넣고 gets 함수를 호출한다. 호출된 함수를 통해 [ebp+buf]에는 사용자가 입력한 문자열이 위치하게 되며, 이는 다시 printf의 인자로 출력된다. 여기서 자세히 보아야 할 부분은 바로 gets를 호출하기 바로 전 변수 buf의 주소 [ebp+buf]이다. 해당 부분을 스택에서 확인하면 주어진 공간은 10이기 때문에 그 이후의 공간은 다른 내용(여기서 다른 내용이란 스택의 변조를 체크하기 위한 \xCCCCCCCC이다.)으로 채워져 있다. =


사용자로부터 입력받을 문자열의 길이를 확인하지 않기 때문에 만약 10보다 큰 내용의 문자열이 입력된다면 이러한 내용은 변조된다. 아래의 그림의 첫 번째 표와 같이 스택에 아홉 개의 "A"와 \x00이 채워지면 그 뒤의 내용은 변화되지 않는다. 하지만 만약 10 보다 큰 내용을 입력할 경우 두 번째 표와 같이 기존에 존재하던 내용들이 덮어 써지는 걸 확인할 수 있다.

이러한 경우 스택 변화를 방지하기 위한 \xCC까지 침범하여 결국 CheckStackVars를 호출하는 과정에 있어 오류가 나타나며 프로그램이 종료된다. 만약 CheckStackVars와 같이 스택의 변화를 방지하는 요소가 없다면 "A"라는 문자열은 스택에 저장되어 있는 다른 부분까지 침범이 가능해지고 이를 통해 RETN 값을 수정하는 등을 통해 BOF 공격을 취할 수가 있다.


Scanf


scanf 또한 사용자로부터 문자열을 입력받아 변수에 저장하는 용도로 사용된다. 하지만 역시 입력받은 문자열의 길이를 체크하지 않기 때문에 스택의 값이 변조될 수가 있다. 아래의 코드를 보자.

#include <stdio.h>
int main()
{
    char buf[10];
    scanf("%s",buf);
    printf("%s",buf);
}

전체적인 내용은 위 gets와 유사하며, 변수 buf의 주소 [ebp+buf]에 scanf를 통해 입력을 받지만 그 길이의 제한이 없기 때문에 이전에 스택에 push 된 다른 값들이 변조될 수 있다. 주어진 길이보다 클 경우 스택 체크 함수로 인해 오류를 나타내며 프로그램은 종료된다.


Strcat


이번엔 strcat 함수에 대하여 알아보자. strcat은 변수 a에 변수 b의 내용을 덧붙여주는 함수로, 이 또한 변수의 길이를 체크하지 않기 때문에 BOF 공격에 취약성을 가지고 있다. 아래의 코드와 어셈블리어를 보자.

#include <stdio.h>
int main()
{
    char buf[10]="AAAAAAAAA";
    char str[10]="BBBBBBBBB";
    strcat(buf,str);
    printf("%s\n",buf);
}

우선 [ebp+buf], [ebp+buf+4], [ebp+buf+8] 등을 이용해 해당 문자열을 저장하는 것을 확인할 수가 있다. 이렇게 저장된 문자열의 시작 주소 [ebp+buf], [ebp+str]은 스택에 넣어지는데 여기서 [ebp+str]이 변수 스택에 들어가고 그다음 [ebp+buf]가 스택에 들어가게 된다.


그 후 "BBB..."를 "AAA..."에 덧붙이게 되는데, 이 과정에서 변수 buf의 공간은 초기에 10만큼만 할당되었기에 변수 str의 크기를 확인한다면 이를 덧붙일 수 없는 것이 정상적이다. 하지만 strcat 함수의 경우 이러한 길이를 고려하지 않기 때문에 결국 [ebp+buf]의 뒷부분에 [ebp+str]의 내용이 덧붙여진다. 그러므로 결국 아래의 그림과 같이 뒷부분에 존재하고 있던 내용 "\x00\xCC..."가 사라지게 되고 변수 str의 내용인 "BBBB..."가 자리 잡게 된다. 이 또한 gets와 마찬가지로 스택의 내용을 변조시키므로  CheckStackVars 함수에서 오류를 나타내게 된다. 만약 이러한 스택 변화를 확인하는 함수가 없을 경우 쉽게 스택의 내용이 변조되어 공격자에게 이용당할 수 있다.


Strcpy


strcat이 버퍼에 있는 내용을 덧붙이는 것이라면, strcpy 함수는 해당 내용을 통째로 옮기는 용도로 사용된다. 전반적인 내용은 strcat과 유사하며, 우선 아래의 코드를 확인하자.

#include <stdio.h>
int main()
{
    char buf[10] = "AAAAAAAAA";
    char str[] = "BBBBBBBBBBBB";
    strcpy(buf,str);
    printf("%s\n", buf);
}

선언된 변수가 스택에 저장되는 과정은 위와 동일하며, strcpy를 호출하기 전에 [ebp+str]을 먼저 스택에 넣은 다음 [ebp+buf]를 인자로 넣어준다. 이러한 과정을 거쳐 결국 변수 buf에 있던 내용은 str의 내용으로 바뀌게 된다. 하지만 strcpy 함수 역시 크기를 체크하지 않기 때문에 변수 buf의 크기가 10 임에도 불구하고 10보다 큰 내용이 오게 되었다. 이를 표현하면 아래의 표와 같다.


Sprintf


sprintf는 printf와 비슷하게 출력 함수로 사용되지만, 다른 점이 있다면 printf가 모니터 화면에 출력되는 것이라면 sprintf는 버퍼로 사용될 변수로 출력이 된다는 점이다. 아래의 코드를 확인해보자.

#include <stdio.h>
int main()
{
    char buf[10];
    char str[] = "BBBBBBBBBBBB";
    sprintf(buf, "%s", str);
    printf("%s\n", buf);
}

[ebp+str]부터 [ebp+str+0xC]까지 해당 문자열을 넣은 다음, 해당 문자열의 시작 주소인 [ebp+str]을 인자로 넣어준 뒤 포맷 스트링, 그리고 해당 버퍼가 저장될 변수 [ebp+buf]가 차례대로 스택에 쌓이게 된다. 아래의 표와 같이 원래 해당 buf의 내용이 존재하지 않았으며 buf 뒤에는 다른 내용의 값들이 존재하고 있다. 하지만 sprintf 계열의 함수를 사용할 경우 하단의 표와 같이 다른 부분의 값을 덮어씌울 수가 있다.


Reference


http://j3nasis.tistory.com/entry/버퍼오버플로우-취약-함수별-대책

http://ljsk139.blog.me/30129428446

http://itguru.tistory.com/66

http://www.hackerschool.org/HS_Boards/data/Lib_system/sprintf.txt



'Reversing > Theory' 카테고리의 다른 글

윈도우 후킹 원리 (1) - User Mode  (3) 2016.04.23
System Call & SSDT Hooking  (0) 2016.04.10
윈도우 메모리구조와 메모리분석 기초  (3) 2016.03.29
CPU 레지스터  (0) 2016.03.26
C기본 문법 어셈블리 변환  (5) 2016.03.20

문제확인


이번 문제도 level11과 비슷한 문제로 풀 수가 있다. 일단 tmp로 파일을 복사하여 디버깅을 해보자.


이전 문제와 같이 0x108 만큼의 버퍼를 채우고 RET에 새로운 주소값을 채우면 된다.




EGG Shell을 이용한 방법


Egg쉘을 이용한 방법의 경우 아래와 같이 쉽다.

Shell Code = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"

getaddr.c

#include <stdio.h>

main(){

char *p=getenv("EGG");

printf("Address : %p\n",p);}




RTL ( Return to Libc)을 이용한 방법


RTL은 리턴 주소를 공유 라이브러리 함수의 주소로 적어 해당 함수를 실행시키는 방법이다. 스택의 구조에서 system()함수를 ret에 덮어 씀으로 인하여 프로세스가 종료되고 리턴할 때 system()함수로 가게 된다. 그리고 system()함수가 끝나게 되면 그 다음 4바이트가 리턴주소이기 떄문에 정상적으로 종료될 수 있게 하기 위하여 exit()를 붙여준다.

system()는 쉘을 실행할수가 있고 인자로 /bin/sh를 넣어주면 된다.

결국 ----str[256]-dummy[8]-SFP[4]-RET[4]    >>> ---A[256]-A[8]-A[4]-System()[4]-exit()[4]-"/bin/sh"[4]로 덮어준다고 볼 수 있다.

아래에는 system() 의 안에 있는 /bin/sh 문자열의 주소를 구하는 코드이다.




Cat을 붙이는 이유


Pipe( | ) 사용시 왼쪽 프로그램의 stdout이 오른쪽 프로그램의 stdin으로 들어가게 되는데 python 이 실행되고 종료되면서 pipe broken 오류로 인하여 종료가 일어난다 따라서 사용자의 입력을 출력해주는 cat이나 te를 이용하여 stdin을 유지 시켜야한다.

<분명히 캡쳐를 한 것인데, 출처를 잊어버렸..>


BackDoor 생성


위의 방법들을 통하여 쉘을 얻은 후에 추가적으로 입력을 해주면 아래와 같이 Backdoor를 생성 할 수가 있다.


참고

http://smleenull.tistory.com/276          //RTL

http://peonix120.tistory.com/3            //RTL


'Hacking > System Hacking' 카테고리의 다른 글

FTZ Level15 //Hard Coding, EGG  (2) 2015.05.21
FTZ Level14 //Distance  (0) 2015.05.17
FTZ Level13 //RTL, EGG  (0) 2015.05.13
FTZ Level11 //BOF,EGG,Format String  (0) 2015.05.09
#25 Bytes Shell Code  (0) 2015.05.07

문제 확인



나중에 디버깅을 하는데 있어서 권한이 없으므로 cp 명령어를 통하여 tmp 폴더로 복사를 하여 분석을 한다.

Disas을 통하여 보았을때 Dummy 8바이트가 추가로 붙은 것을 확인 할 수가 있으며, 스텍을 그림으로 나타내면 아래와 같은 모양이 된다.



Buffer Over Flow를 통한 풀이



*RET에 넣을 주소를 구하여보자. 아래의 코드에서 25Byte의 Shell Code는 B로 대입을 하고 A는 NOP, C에는 나중에 넣을 RET의 크기 만큼 대입을 하여 x/256x $ESP를 통하여 RET에 넣을 주소를 구하는 과정이다. 여기에서 주의해야 할 사항으로는 ` , ' , " 의 사용에 유의하여야 한다는 것이다.

여기서 우리는 0xbffffadc의 주소를 RET로 사용 할 것이다. 그리고 공격에 쓰일 25Byte의 쉘코드는 인터넷을 통하여 쉽게 구할 수가 있다.

"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"

*여기서 유의 해야할 사항으로는 `,',"을 헷갈리지 않고 사용하는 것과 \x90이 \xc2로 치환될 경우가 있는 UTF-8을 대신하여 en_US를 사용하여야 한다. 이를 위해서는 콘솔에 다음과 같이 입력을 하여야한다. "export LANG=en_US "




환경 변수를 이용한 풀이


이번에는 환경변수를 이용한 풀이를 하여보자. 우선 환경변수를 등록하여 준다.그 후 아래와 같이 하면 된다.



Format String Attack


포멧스트링에서 가장 대표적으로 언급되는 중요한 호출 시점이란 main() 함수의 소멸자이다. 소멸자란 main()함수가 종료되는 시점에 소멸자가 호출된다고 볼수 있는데, 이러한 소멸자 역할을 수행하는 함수를 셸코드로 흐름을 바꿀수 있다면 셸이 떨어진다는 의미이다. nm 명령어를 통하여 함수의 소멸자를 찾아보자.

여기서 중요한 소멸자 함수는 바로 0x08049610에 위치하여 있는 __DTOR_END__ 함수이다. 이제 공격에 앞서 위의 방법과 같이 환경변수를 등록하여 주고 이제 이 값의 위치를 셸코드가 들어 있는 환경변수의 위치로 바꾸어주면 된다.

우리가 공격해야 할 주소는 소멸자가 있는 __DTOR_END__(0x08049610) 이고 덮어써야할 셸 코드 주소는 0xbffffc69이다. 여기서 마지막에 있는 명령어에 대하여 설명을 하자면, 우선 __DTOR_END__의 주소와 Half Word(2Byte) 뒤의 주소를 두번씩 입력하고 있다. 이렇게 하면 char str[256] 배열에 이 주소값이 들어 갈 것이다.

그리고 %8x%8x%8x%64577c%n%50070c%n :  %8x 지정자를 이용해 8바이트 단위의 출력 포멧을 만들면서 포인터를 3자리 앞으로 옮겼다. 즉 %8x 지정자 3개로 인해 포인터가 str 배열 바로 앞으로 이동한다. 그리고 %c 지정자때문에 포인터가 한번 더 이동해서 str배열을 가리키게 된다.

여기서 64577의 값은 "0xFC69"를 10진수로 변환한 후에 다른 입력된 값(40byte)를 빼주는 것이다. 그래야 앞의 40Byte와 더해져서 0xFC69가 스트링에 입력이 될 수 있기 떄문이다. 그리고 50070은 0xBFFF를 입력하기 위한 값으로 0x1BFFF-0xFC69를 빼준 값이다.







'Hacking > System Hacking' 카테고리의 다른 글

FTZ Level15 //Hard Coding, EGG  (2) 2015.05.21
FTZ Level14 //Distance  (0) 2015.05.17
FTZ Level13 //RTL, EGG  (0) 2015.05.13
FTZ Level12 //BOF,RTL,EGG,Backdoor  (0) 2015.05.10
#25 Bytes Shell Code  (0) 2015.05.07