Немного запоздавший разбор задания Notes из дополнительного конкурса финала VolgaCTF 2020.
Задание представляет собой сервис для создания заметок с поддержкой HTML тегов и смайлов.
Заголовок заметки позволяет использовать только текст и смайлы, а сам контент фильтруется с использованием jQuery и DOMPurify.
<h3>Title <XSS> <img class="smile" src="/static/smiles/dance.gif" name="dance"></h3>
<p id="content"></p>
...
<script>
var note = "Content\x20\x3CXSS\x3E\x20\x3Cimg\x20class\x3D\x22smile\x22\x20src\x3D\x22\x2Fstatic\x2Fsmiles\x2Fdance.gif\x22\x20name\x3D\x22dance\x22\x3E\x20";
$(document).ready(function() {
try {
$.globalEval("note = DOMPurify.sanitize(note)");
} finally {
content.innerHTML = note;
}
});
Как видно из кода, для эксплуатации XSS необходимо, чтобы $.globalEval
вернул исключение. Однако, сделать это через содержимое заметки довольно сложно, поэтому участникам необходимо было изучить реализацию остальных функций приложения, а именно - смайлов. Каждый смайл добавлялся в виде тега <img>
с аттрибутом name
. Добавление именованных img
позволяет частично использовать технику DOM Clobbering.
То есть, создание заметки со смайлом :dance:
приведет к созданию HTML тега <img name=dance>
, который будет доступен в JavaScript через window.dance
. Но именованные свойства поддерживаются не только объектом window
, но и document
, более того, используя это можно переопределить такие значения как document.cookie
, document.documentElement
, document.body
и другие (https://html.spec.whatwg.org/multipage/dom.html#dom-document-namedItem-which).
Рассмотрим, как именно выполняется функция $.globalEval
function DOMEval( code, node, doc ) {
doc = doc || document;
var i, val,
script = doc.createElement( "script" );
script.text = code;
...
doc.head.appendChild( script ).parentNode.removeChild( script );
Используя смайл :head:
можно изменить логику выполнения данной функции и перезаписать document.head
, но скрипт все равно выполнится корректно и DOMPurify удалит XSS.
Но использование двух смайлов :head: :head:
приведет к созданию HTMLCollection в document.head
в результате чего вызов document.head.appendChild
вернет исключение в $.globalEval
и контент заметки не будет отфильтрован.
Данная особенность позволяет изменять логику выполнения JS скриптов и приводит к забавным эффектам.
Пример (не относится к CTF таску):
<div id=x></div>
<iframe name=documentElement srcdoc='<a href="tel:<img/src/onerror=alert(1)>" id=clientWidth>a</a>'></iframe>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<script>
$("#x").html('innerWidth:'+$(window).innerWidth()) // == innerWidth:tel:<img/src/onerror=alert(1)>
</script>
Рекомендуемые ссылки:
https://portswigger.net/research/dom-clobbering-strikes-back
https://bugzilla.mozilla.org/show_bug.cgi?id=1420032
https://html.spec.whatwg.org/multipage/dom.html#dom-document-namedItem-which
https://medium.com/@terjanq/clobbering-the-clobbered-vol-2-fb199ad7ec41
https://blog.bi0s.in/2020/08/26/Web/GoogleCTF20-SafeHtmlPaste/
Комментариев нет :
Отправить комментарий
Примечание. Отправлять комментарии могут только участники этого блога.