Примеры атак XSS и способов их ослабления. Часть 1

TG_hack_[ DR.Bro ]

Бот форума
Регистрация
26.02.21
Сообщения
43
Реакции
22
Кредиты
0 ₽
Баллы
18
Понимание межсайтового скриптинга и способов борьбы с ним необходимо каждому веб-разработчику. Это один из самых распространенных видов уязвимостей – злоумышленники часто проводят атаки XSS для кражи данных и нарушения работоспособности сервисов.

Перевод публикуется с сокращениями, автор оригинальной статьи Russel Jones.

Межсайтовый скриптинг (XSS) – это атака, которая позволяет JavaScript через один сайт работать с другим. XSS интересен не из-за технической сложности, а скорее потому, что он эксплуатирует некоторые из основных механизмов безопасности браузеров и из-за огромной распространенности.
Background

Изначально Всемирная Паутина представляла собой набор статических документов HTML, которые браузер должен был отображать для просмотра пользователями. По мере развития Интернета возрастали и требования к документам, что привело к появлению JavaScript и файлов cookie: скрипты нужны для интерактивности документа, а cookies – чтобы браузеры могли сохранять его состояние.

Появление этих возможностей привело к тому, что браузеры не только визуализируют HTML, но и вмещают в памяти в качестве API для разработчиков представление, называемое объектной моделью документа (DOM). DOM предлагает древовидную структуру тегов HTML, а также доступ к файлам cookie для получения состояния. Со временем модель превратилась из предназначенной преимущественно для чтения структуры в структуру read-write, обновление которой приводит к повторному рендерингу документа.

Как только документы получили возможность запускать код, браузеры должны были определить контекст выполнения для программ на JavaScript. Политика, которая была разработана, называется Same-Origin и по-прежнему является одним из фундаментальных примитивов безопасности браузера. Изначально в ней утверждалось, что JavaScript в одном документе может получить доступ только к собственному DOM и к DOM других документов с тем же происхождением. Позже, когда был добавлен XMLHttpRequest и Fetch, появилась модифицированная версия Same-Origin. Эти API не могут выдавать запросы к любому источнику, они могут только читать ответ на запросы от того же источника.

Что же такое происхождение? Это кортеж протокола, имени хоста и порта документа.

Фрагмент 1: Кортеж из схемы, хоста и порта этого URL-адреса.
Код:
https://www.example.com:443/app^^^^^^^^^^^^^^^^^^^^ ^^^SchemeHostPort

Иллюстрация Same-Origin в действии. JavaScript работает на www.evil.com и не может получить доступ к DOM на www.example.com.

Политика Same-Origin отлично помогает смягчать атаки на статические сайты, как показано на рисунке выше. Однако с атаками на динамические ресурсы, принимающие пользовательский ввод, ситуация немного сложнее из-за смешивания кода и данных, которая позволяет злоумышленнику выполнять контролируемый ввод в исходном документе.

Атаки XSS обычно бывают трех видов: рефлективными, хранимыми и основанными на DOM.

Рефлективные и хранимые XSS-атаки принципиально одинаковы, поскольку полагаются на вредоносный ввод, отправляемый на бекенд и представляющий этот ввод пользователю сервер. Рефлективные XSS обычно возникают в виде злонамеренно созданной злоумышленником ссылки, по которой затем переходит жертва. Хранимые XSS происходят, когда злоумышленник загружает вредоносный ввод. Атаки на основе DOM отличаются тем, что они происходят исключительно на стороне клиента и включают вредоносный ввод, манипулирующий DOM.
Примеры

Рефлективные атаки XSS


Ниже можно увидеть простое веб-приложение на Go, которое отражает свой ввод (даже если это вредоносный скрипт) обратно пользователю. Вы можете использовать это приложение, сохранив его в файле xss1.go и запустив go run xss1.go.

Фрагмент 3: Пример веб-приложения с рефлективной (отраженной) XSS-атакой.
Код:
package mainimport ("fmt""log""net/http")func handler(w http.ResponseWriter, r *http.Request) {w.Header().Set("X-XSS-Protection", "0")messages, ok := r.URL.Query()["message"]if !ok {messages = []string{"hello, world"}}fmt.Fprintf(w, "<html><p>%v</p></html>", messages[0])}func main() {http.HandleFunc("/", handler)log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))}

Чтобы увидеть XSS-атаку, перейдите по уязвимому URL-адресу ниже.
Код:
http://localhost:8080?message=<script>alert(1)</script>

Взгляните на источник: сервер вернул документ, который выглядит примерно так, как показано во фрагменте 4. Обратите внимание, как смешение кода и данных позволило произойти этой атаке.

Фрагмент 4: Пример вывода уязвимого для XSS веб-приложения.
Код:
<html><p><script>alert(1)</script></p></html>

Этот пример может показаться неправдоподобным, поскольку защита XSS была явно отключена. Эта ее форма основана на эвристике с обходными путями для различных браузеров. Она была отключена для создания кроссбраузерных примеров, иллюстрирующих основные концепции XSS-атак. Некоторые браузеры удаляют эту защиту: например, в Google Chrome 78 и выше вам не понадобится строка w.Header().Set("X-XSS-Protection", "0"), чтобы атака сработала.
Хранимые XSS-атаки

Хранимые XSS-атаки похожи на рефлективные, но пэйлоад поступает из хранилища данных, а не из ввода непосредственно. Например, злоумышленник может загрузить в веб-приложение зловреда, который затем будет показан каждому авторизованному юзеру.

Ниже приведен простой чат, который иллюстрирует этот вид атак. Вы можете сохранить приложение в файле xss2.go и запустить с помощью команды go run xss2.go.

Фрагмент 5: Хранимая XSS-атака.
Код:
package mainimport ("fmt""log""net/http""strings""sync")var db []stringvar mu sync.Mutexvar tmpl = `<form action="/save">Message: <input name="message" type="text"><br><input type="submit" value="Submit"></form>%v`func saveHandler(w http.ResponseWriter, r *http.Request) {mu.Lock()defer mu.Unlock()r.ParseForm()messages, ok := r.Form["message"]if !ok {http.Error(w, "missing message", 500)}db = append(db, messages[0])http.Redirect(w, r, "/", 301)}func viewHandler(w http.ResponseWriter, r *http.Request) {w.Header().Set("X-XSS-Protection", "0")w.Header().Set("Content-Type", "text/html; charset=utf-8")var sb strings.Buildersb.WriteString("<ul>")for _, message := range db {sb.WriteString("<li>" + message + "</li>")}sb.WriteString("</ul>")fmt.Fprintf(w, tmpl, sb.String())}func main() {http.HandleFunc("/", viewHandler)http.HandleFunc("/save", saveHandler)log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))}

Чтобы увидеть атаку XSS, перейдите по ссылке и введите сообщение <script>alert(1);</script>.

  • Атака делится на две фазы:
    • пейлоад сохраняется в хранилище данных в функции storeHandler;
    • когда страница визуализируется во ViewHandler, пейлоад добавляется к выходным данным.
XSS-атаки на основе DOM

Такие атаки не связаны с бекендом и происходят исключительно на стороне клиента. Они интересны тем, что современные веб-приложения перемещают логику к клиенту, а атаки происходят, когда пользователь напрямую манипулирует DOM. Хорошей новостью для злоумышленников является то, что DOM имеет широкий спектр способов эксплуатации, наиболее популярными из которых являются innerHTML и document.write.

Ниже приведен пример обслуживающего статический контент веб-приложения. Код тот же, что и в примере с рефлективными XSS, но здесь атака будет происходить полностью на стороне клиента. Вы можете сохранить приложение в файле xss3.go и запустить его командой go run xss3.go.

Фрагмент 6: Пример веб-приложения с XSS-атакой на основе DOM.
Код:
package mainimport ("fmt""log""net/http")const content = `<html><head><script>window.onload = function() {var params = new URLSearchParams(window.location.search);p = document.getElementById("content")p.innerHTML = params.get("message")};</script></head><body><p id="content"></p></body></html>`func handler(w http.ResponseWriter, r *http.Request) {w.Header().Set("X-XSS-Protection", "0")fmt.Fprintf(w, content)}func main() {http.HandleFunc("/", handler)log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))}

Чтобы увидеть эту атаку, перейдите по ссылке http://localhost:8080/?message="<img src=1 onerror=alert(1);/>". Обратите внимание, что вектор атаки немного отличается и innerHTML не будет выполнять скрипт напрямую, однако он добавит HTML-элементы, которые затем выполнят код на JavaScript. В приведенном примере добавляется элемент image, который запускает скрипт при возникновении ошибки (она всегда появляется, поскольку злоумышленник подставляет неверный источник).

Если хотите напрямую добавить элемент скрипта, придется использовать другой приемник XSS. Замените элемент script из фрагмента 6 элементом script из фрагмента 7 и перейдите по следующей ссылке: http://localhost:8080/?message="<script>alert(1);</script>". Атака сработает, потому что document.write принимает элементы скрипта напрямую.

Фрагмент 7: Еще один пример атаки XSS на основе DOM.
Код:
<script>window.onload = function() {var params = new URLSearchParams(window.location.search);document.open();document.write(params.get("message"));document.close();};</script>
Связанные направления атак

Хотя обычно их не называют атаками XSS, существует несколько связанных направлений, о которых стоит упомянуть.
Content-type

Всему виной неправильная настройка типа содержимого ответов HTTP. Это может произойти как на уровне бекенда (ответ имеет неверный набор заголовков Content-Type), так и при попытке браузера проснифферить тип MIME. Internet Explorer был особенно восприимчив к этому, и классическим примером является служба загрузки изображений: злоумышленник может загрузить JavaScript вместо картинки. Браузер видит, что тип контента был установлен на image/jpg, но пейлоад содержит скрипт – он выполняется, что приводит к атаке XSS.
Urlschemes

Следующий тип атаки – активность через URL со схемой JavaScript. Представим веб-сайт, который позволяет пользователю контролировать цель ссылки, как показано во фрагменте 8. В этом случае злоумышленник сможет предоставить URL, выполняющий некий JavaScript с помощью нашей схемы.

Чтобы опробовать этот тип атаки, можно сохранить приложение в файле xss4.go, запустить командой go run xss4.go и перейти по ссылке .

Фрагмент 8: XSS-атака, введенная через схему URL-адресов.
Код:
package mainimport ("fmt""log""net/http")func handler(w http.ResponseWriter, r *http.Request) {w.Header().Set("X-XSS-Protection", "0")links, ok := r.URL.Query()["link"]if !ok {messages = []string{"example.com"}}fmt.Fprintf(w, `<html><p><a href="%v">Next</p></html>`, links[0])}func main() {http.HandleFunc("/", handler)log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))}

Продолжение следует…

Источник
 
Верх Низ