2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

imctf2021

Posted at

imctf 2021

Web

can you see cookie?

問題ページ

タイトル通り、cookieを確認します。。
flag:cnVuX3J1bl9ydW5fYXNfZmFzdF95b3VfY2FuIQ==

これをbase64でデコードするとflagが取れました。

for stranger of storage🤠

local storageを確認します。

woody says:54,47,56,30,49,47,31,6c,49,48,52,6c,62,47,77,67,65,57,39,31,49,47,46,69,62,33,56,30,49,48,52,6f,5a,53,42,7a,64,48,4a,68,62,6d,64,6c,49,48,52,6f,61,57,35,6e,63,79,42,68,63,6d,55,67,61,47,46,77,63,47,56,75,61,57,34,6e,49,48,52,76,49,47,31,6c,43,6c,4e,30,63,6d,46,75,5a,32,55,67,64,47,68,70,62,6d,64,7a,43,6d,6c,74,59,33,52,6d,65,33,4e,30,63,6d,46,75,5a,32,56,66,64,47,68,70,62,6d,64,7a,58,32,46,79,5a,56,39,6f,59,58,42,77,5a,57,35,70,62,69,64,66,64,47,39,66,62,57,56,39,43,6b,46,70,62,69,64,30,49,47,35,76,49,47,52,76,64,57,4a,30,49,47,46,69,62,33,56,30,49,47,6c,30

怪しい文字列を見つけました。

cyber chefでデコードしてみます。

解けなかった問題

DoScript🐣

配布コード

import os
import subprocess
import urllib.parse
from flask import Flask, request

app = Flask(__name__)

def js_sanitize(text):
    #I've heard that this is enough to sanitize special characters in JavaScript.
    return "".join([c for c in text if ord(c) > 0x1f]).replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'").replace("/", "\\/")

@app.route("/")
def doscript():
    name = request.args.get("name")
    if not name:
        name = 'Nagoya-Hacker'
    html = f"""<!DOCTYPE html>
<html lang="ja">
<head>
<title>DoScript</title>
<ScRiPt>
//Hack! Hack! Hack!
alert("Hacked by {js_sanitize(name)}!!");
alert("Is there any way to turn off the alerts?");
alert("Hehehehehe");
</ScRiPt>
</head>
<body>
<h1>DoScript</h1>
This site is a perfectly safe site that will not be hacked.<br>
It cannot be tampered with by a hacker!!!<br>
👻<br>
<a href="/tamperchecker">Tamper Checker 🧐</a><br>
</body>
</html>
"""
    return html


@app.route("/tamperchecker", methods=["GET", "POST"])
def tamperchecker():
    mysite = "http://118.27.104.46:31416/"
    if request.method == "GET":
        return f"""<!DOCTYPE html>
<html lang="ja">
<head>
<title>Tamper Checker 🧐</title>
</head>
<body>
<h1>Tamper Checker 🧐</h1>
<form action="/tamperchecker" method="post">
<label for="url">URL: </label>
<input type="text" style="width:350px;" id="url" name="url" placeholder="{mysite}?name=Nagoya-Hacker">
<button type="submit">Submit</button>
</form>
</body>
</html>
"""
    else:
        url = request.form["url"]
        if f"{mysite}?name=" != url[0:len(mysite + "?name=")]:
            return "Not my site!"
        if len(url) > 100:
            return "URL is too long!"
        url = f"{mysite}?name=" + urllib.parse.quote(url.replace(f"{mysite}?name=", ""))
        try:
            cmd = f'chromium-browser --no-sandbox --headless --disable-gpu "{url}"'
            subprocess.run(cmd, shell=True, timeout=3)
        except:
            return 'This site has been hacked 👾'#Alerts are detected.
        #Flag呼び出し
        return f'This site has not been hacked 👻\nFlag:{os.getenv("FLAG")}'#No alerts are detected.

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=23142)

問題ページ

問題ページにアクセスすると、ポップアップが表示されます。
配布コードをみるに、ポップアップを表示させなければFlagが獲得できそうです。

js_sanitizeをbypassし、window.alert=function(){};などで、alert関数を上書きできればFlagが獲得できると考えました。

nameに与えた値は最初のalertに出力されます。

sanitizeされる文字列を再度確認します。

"".join([c for c in text if ord(c) > 0x1f]).replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'").replace("/", "\\/")

,',",/と制御文字はサニタイズされるようです。
しかし、、いくら試してもbypass出来ません。
ここでタイムアップでした。

以下writeupを見ての復習です。

XSSではなく、DoSを起こすのが正解だそうです。。
完全にXSSだと思い込んでいました。。
柔軟な思考ができなかったです。。悔しい。

以下のpayloadを送るとDoSになるとのことです。

<!--<script> 

https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements
上記のリンクによると、これはスクリプトブロックには、<!-- のあとにスクリプトの開始タグを含められないことになっているそうです。
仮に含んだ場合、2つ目のスクリプトの終了タグが現れるまでスクリプトブロックが継続します。

実際にやってみましょう。

記載通り、scriptタグが無効になりalert関数が実行されません。
このURLをコピーし、tampercheckerにPOSTすればFlagです。

<ScRiPt>
//Hack! Hack! Hack!
alert("Hacked by {js_sanitize(name)}!!");
alert("Is there any way to turn off the alerts?");
alert("Hehehehehe");
</ScRiPt>

Num-restaurant🍷

配布コード

import os
import random
import subprocess
import urllib.parse
from flask import Flask, request, Response

app = Flask(__name__)

def sanitize_price(price):
    if not price.isnumeric():
        return "10000000"
    return price

def sanitize_meal(meal):
    if not meal.isascii():
        return "Kasumi"
    #Is it possible to block XSS?
    return "".join([c for c in meal if ord(c) > 0x1f]).replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'").replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("http", "num")

@app.route("/")
def num_restaurant():
    niku = random.randint(1, 1000000)
    sushi = random.randint(1, 1000000)
    yasai = random.randint(1, 1000000)
    price = request.args.get("price")
    meal = request.args.get("meal")
    if (not price) or (not meal):
        script = ''
    else:
        script = f'''<script>
var text = "¥{sanitize_price(price)}{sanitize_meal(meal)}😋Yummy!!";
alert(text);
</script>
'''
    html = f"""<!DOCTYPE html>
<html lang="ja">
<head>
<title>Num-restaurant</title>
{script}
</head>
<body>
<h1>Welcome to Num-restaurant 🔥</h1>
<a href="?price={niku}&meal=Niku"><p style="font-size:200%;">¥{niku} / Niku🍖</p></a>
<a href="?price={sushi}&meal=Sushi"><p style="font-size:200%;">¥{sushi} / Sushi🍣</p></a>
<a href="?price={yasai}&meal=Yasai"><p style="font-size:200%;">¥{yasai} / Yasai🥗</p></a>
</body>
</html>
"""
    if request.args.get("encoding") == "shift_jis":
        return Response(html, content_type='text/html; charset=shift_jis')
        #No one uses it anymore, but I implemented it because I was born in 1997 and missed it.
    else:
        return Response(html, content_type='text/html; charset=utf-8')


@app.route("/admin", methods=["GET", "POST"])
def admin():
    if request.method == "GET":
        return """
<!DOCTYPE html>
<html lang="ja">
<head>
<title>Give the food to Admin !!</title>
</head>
<body>
<h1>Give the food to Admin !!</h1>
<form action="/admin" method="post">
Query: 
<input type="text" style="width:100px;" id="price" name="price" placeholder="10000">
<input type="text" style="width:100px;" id="meal" name="meal" placeholder="Niku">
<input type="hidden" id="encoding" name="encoding" value="utf-8">
<button type="submit">Submit</button>
</form>
</body>
</html>
"""
# POST
    else:
        url = f'http://160.251.83.96:31415?price={urllib.parse.quote(request.form["price"])}\
&meal={urllib.parse.quote(request.form["meal"])}\
&encoding={urllib.parse.quote(request.form["encoding"])}'
        try:
            flag = os.getenv("FLAG")
            cmd = f'chromium-browser --no-sandbox --headless --disable-gpu "{url}&flag={flag}"'
            subprocess.run(cmd, shell=True, timeout=3)
        except:
            return "Admin🥰Yummy!!"
        return "Admin🤤Yucky!!"

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=23141)

前問同様、これもXSSの問題だと思い、どうにかしてサニタイズをBypassしようと考えました。

encoding指定ができるため、shift_jisを指定し、shift_jisに起因する問題を使い、bypassするのかと考えましたが、ascii以外はサニタイズされてしまうため、上手くいきません。

とすると次はisnumeric関数に問題があるのかと思い、調べてみました。

isnumericについて1

isnumericについて2

isnumericは数値であればunicodeでもローマ数字でもTrueが返されるようです。

これを悪用できないか考えていたところでタイムアップでした。

以下は復習です。

var text = "¥{sanitize_price(price)}{sanitize_meal(meal)}😋Yummy!!";

入力した値の出力箇所が上記のようになっていることから、priceに5c問題を起こすような文字を入力すればサニタイズを突破し、xssを起こせるとのことです。

isnumericについて3

5C問題について

シフトJIS(Shift_JIS)は漢字などを2バイトで表現するが、一部の文字で、2バイト目に\すなわち0x5Cが使用されている。

上記の二つにリンクから"十"はisnumericでTrueかつ5C問題に使えることがわかります。

早速試してみます。

入力したダブルクォートがエスケープされていませんので成功したと考えます。

alertを実行することができました。

あとはこれを加工して自前のサーバにリクエストを飛ばすだけです。

感想

良問が多く、5C問題やDoSなど非常に勉強になりました。
また、本Writeupには記載していませんが、printtextには痺れました。
ああいう閃きが出来るよう、引き続き精進します。

公式Write up

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?