consider jmp case in amd64
This commit is contained in:
parent
b87178a9ab
commit
a42f01cfa1
|
@ -5,10 +5,14 @@ module OneGadget
|
|||
module ClassMethods
|
||||
LINUX_X86_32 = %w(eax ebx ecx edx edi esi ebp esp).freeze
|
||||
LINUX_X86_64 = LINUX_X86_32 + %w(rax rbx rcx rdx rdi rsi rbp rsp) + 7.upto(15).map { |i| "r#{i}" }
|
||||
# Registers' name in amd64.
|
||||
# @return [Array<String>] List of registers.
|
||||
def amd64
|
||||
LINUX_X86_64
|
||||
end
|
||||
|
||||
# Registers' name in i386.
|
||||
# @return [Array<String>] List of registers.
|
||||
def i386
|
||||
LINUX_X86_32
|
||||
end
|
||||
|
|
|
@ -2,7 +2,12 @@ require 'one_gadget/gadget'
|
|||
# Ubuntu GLIBC 2.23-0ubuntu5
|
||||
# ELF 64-bit LSB shared object, x86-64
|
||||
build_id = File.basename(__FILE__, '.rb').split('-').last
|
||||
OneGadget::Gadget.add(build_id, 0x4526a, constraints: ['[rsp+0x30] == NULL'])
|
||||
OneGadget::Gadget.add(build_id, 0xef6c4, constraints: ['[rsp+0x50] == NULL'])
|
||||
OneGadget::Gadget.add(build_id, 0xf0567, constraints: ['[rsp+0x70] == NULL'])
|
||||
OneGadget::Gadget.add(build_id, 0xf5b10, constraints: ['[rbp-0xf8] == NULL', 'rcx == NULL'])
|
||||
OneGadget::Gadget.add(build_id, 0x4526a, constraints: ['[rsp+0x30] == NULL'],
|
||||
effect: 'execve("/bin/sh", rsp+0x30, environ)')
|
||||
OneGadget::Gadget.add(build_id, 0xef6c4, constraints: ['[rsp+0x50] == NULL'],
|
||||
effect: 'execve("/bin/sh", rsp+0x50, environ)')
|
||||
OneGadget::Gadget.add(build_id, 0xf0567, constraints: ['[rsp+0x70] == NULL'],
|
||||
effect: 'execve("/bin/sh", rsp+0x70, environ)')
|
||||
OneGadget::Gadget.add(build_id, 0xf5b10, constraints: ['[rbp-0xf8] == NULL || [[rbp-0xf8]] == NULL',
|
||||
'rcx == NULL || [rcx] == NULL'],
|
||||
effect: 'execve("/bin/sh", rcx, [rbp-0xf8])')
|
||||
|
|
|
@ -27,6 +27,7 @@ module OneGadget
|
|||
def process(cmd)
|
||||
inst, args = parse(cmd)
|
||||
return registers[pc] = args[0] if inst.inst == 'call'
|
||||
return if inst.inst == 'jmp' # believe the fetcher has handled jmp.
|
||||
sym = "inst_#{inst.inst}".to_sym
|
||||
send(sym, *args)
|
||||
end
|
||||
|
@ -40,7 +41,8 @@ module OneGadget
|
|||
Instruction.new('add', 2),
|
||||
Instruction.new('sub', 2),
|
||||
Instruction.new('push', 1),
|
||||
Instruction.new('call', 1)
|
||||
Instruction.new('call', 1),
|
||||
Instruction.new('jmp', 1)
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -7,13 +7,7 @@ module OneGadget
|
|||
# Gadgets for amd64 glibc.
|
||||
# @return [Array<OneGadget::Gadget::Gadget>] Gadgets found.
|
||||
def find
|
||||
bin_sh_hex = str_offset('/bin/sh').to_s(16)
|
||||
cands = candidates do |candidate|
|
||||
next false unless candidate.include?(bin_sh_hex) # works in x86-64
|
||||
next false unless candidate.lines.last.include?('execve') # only care execve
|
||||
true
|
||||
end
|
||||
cands.map do |candidate|
|
||||
candidates.map do |candidate|
|
||||
processor = OneGadget::Emulators::Amd64.new
|
||||
candidate.lines.each { |l| processor.process(l) }
|
||||
offset = offset_of(candidate)
|
||||
|
@ -25,6 +19,31 @@ module OneGadget
|
|||
|
||||
private
|
||||
|
||||
def candidates
|
||||
bin_sh_hex = str_offset('/bin/sh').to_s(16)
|
||||
cands = super do |candidate|
|
||||
next false unless candidate.include?(bin_sh_hex) # works in x86-64
|
||||
next false unless candidate.lines.last.include?('execve') # only care execve
|
||||
true
|
||||
end
|
||||
# find gadgets in form:
|
||||
# lea rdi, '/bin/sh'
|
||||
# jmp xxx
|
||||
# xxx:
|
||||
# ...
|
||||
# call execve
|
||||
cands2 = `#{objdump_cmd}|egrep 'rdi.*# #{bin_sh_hex}' -A 1`.split('--').map do |cand|
|
||||
cand = cand.lines.map(&:strip).reject(&:empty?)
|
||||
next nil unless cand.last.include?('jmp')
|
||||
jmp_addr = cand.last.scan(/jmp\s+([\da-f]+)\s/)[0][0].to_i(16)
|
||||
dump = `#{objdump_cmd(start: jmp_addr, stop: jmp_addr + 100)}|egrep '[0-9a-f]+:'`
|
||||
remain = dump.lines.map(&:strip).reject(&:empty?)
|
||||
remain = remain[0..remain.index { |r| r.match(/call.*<execve[^+]*>/) }]
|
||||
[cand + remain].join("\n")
|
||||
end.compact
|
||||
cands + cands2
|
||||
end
|
||||
|
||||
def resolve(processor)
|
||||
# check rdi should always related to rip
|
||||
return unless processor.registers['rdi'].to_s.include?('rip')
|
||||
|
|
|
@ -22,7 +22,7 @@ module OneGadget
|
|||
# @return [Array<String>]
|
||||
# Each +String+ returned is multi-lines of assembly code.
|
||||
def candidates(&block)
|
||||
cands = `objdump -w -d -M intel "#{file}"|egrep 'call.*<exec[^+]*>$' -B 20`.split('--').map do |cand|
|
||||
cands = `#{objdump_cmd}|egrep 'call.*<exec[^+]*>$' -B 20`.split('--').map do |cand|
|
||||
cand.lines.map(&:strip).reject(&:empty?).join("\n")
|
||||
end
|
||||
# remove all calls, jmps
|
||||
|
@ -33,6 +33,13 @@ module OneGadget
|
|||
|
||||
private
|
||||
|
||||
def objdump_cmd(start: nil, stop: nil)
|
||||
cmd = %(objdump -w -d -M intel "#{file}")
|
||||
cmd.concat(" --start-address #{start}") if start
|
||||
cmd.concat(" --stop-address #{stop}") if stop
|
||||
cmd
|
||||
end
|
||||
|
||||
def slice_prefix(cands)
|
||||
cands.map do |cand|
|
||||
lines = cand.lines
|
||||
|
|
|
@ -55,16 +55,16 @@ module OneGadget
|
|||
arg1 = processor.stack[cur_top + 4]
|
||||
arg2 = processor.stack[cur_top + 8]
|
||||
options = if call.include?('execve')
|
||||
valid_execve(arg1, arg2, rw_base: rw_base)
|
||||
resolve_execve(arg1, arg2, rw_base: rw_base)
|
||||
elsif call.include?('execl')
|
||||
valid_execl(arg1, arg2, rw_base: rw_base, sh: bin_sh - 5)
|
||||
resolve_execl(arg1, arg2, rw_base: rw_base, sh: bin_sh - 5)
|
||||
end
|
||||
return nil if options.nil?
|
||||
options[:constraints].unshift("#{rw_base} is the address of `rw-p` area of libc")
|
||||
options
|
||||
end
|
||||
|
||||
def valid_execl(arg1, arg2, rw_base: nil, sh: 0)
|
||||
def resolve_execl(arg1, arg2, rw_base: nil, sh: 0)
|
||||
args = []
|
||||
arg = arg1.to_s
|
||||
if arg.include?(sh.to_s(16))
|
||||
|
@ -77,7 +77,7 @@ module OneGadget
|
|||
{ constraints: ["#{arg} == NULL"], effect: %(execl("/bin/sh", #{args.join(', ')})) }
|
||||
end
|
||||
|
||||
def valid_execve(arg1, arg2, rw_base: nil)
|
||||
def resolve_execve(arg1, arg2, rw_base: nil)
|
||||
# arg1 == NULL || [arg1] == NULL
|
||||
# arg2 == NULL or arg2 is environ
|
||||
cons = [should_null(arg1.to_s)]
|
||||
|
|
|
@ -132,8 +132,8 @@ module OneGadget
|
|||
# @return [String]
|
||||
# Only supports :amd64, :i386 now.
|
||||
def architecture(file)
|
||||
str = `file #{::Shellwords.escape(file)}`
|
||||
return :amd64 if str.include?('x86-64')
|
||||
str = `readelf -h #{::Shellwords.escape(file)}`
|
||||
return :amd64 if str.include?('X86-64')
|
||||
return :i386 if str.include?('Intel 80386')
|
||||
:unknown
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ describe 'one_gadget' do
|
|||
describe 'from file' do
|
||||
it 'libc-2.19' do
|
||||
path = @data_path['libc-2.19-cf699a15caae64f50311fc4655b86dc39a479789.so']
|
||||
expect(OneGadget.gadgets(file: path)).to eq [0x4647c, 0xe5765, 0xe66bd]
|
||||
expect(OneGadget.gadgets(file: path)).to eq [0x4647c, 0xc1ba3, 0xe4968, 0xe5765, 0xe66bd]
|
||||
end
|
||||
|
||||
it 'libc-2.24' do
|
||||
|
|
Loading…
Reference in New Issue