• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Дневник белой шляпы [Часть 2]: Интервью [GraphQL]

grozdniyandy

White-Hat
Premium
Регистрация
11.08.2023
Сообщения
522
Реакции
677
Гарант сделки
2
#!/bin/sh (Вступление)

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

О себе: Я очень ленивый человек и хочу учиться тому, чему хочу, а не тому, что должен. Вы знаете, как сложно бросить аниме и начать чему-то учиться. Я не смог достичь этого, я не достигну этого, я не хочу этого достигать. Мне нравится то, что я делаю, я заставляю себя делать больше, и это одна из таких ситуаций.

Вся эта статья основана на том, что я понимаю, и в основном предназначена для тех, кто хочет ознакомиться с моей базовой дорожной картой GraphQL!

Уровень
: Легкий-средний
Требования: API (немного), знание простых веб-уязвимостей, логика

История

Моя подруга сказала, что может устроить мне собеседование в одном из мест, и попросила мое резюме. Итак, угадайте, что сделал этот гений? Я написал в своем резюме то, чего не знаю, НЕ ДЛЯ ТОГО, ЧТОБЫ ЛГАТЬ ИМ О СВОИХ ЗНАНИЯХ, я сделал это, чтобы заставить себя выучить то, что я написал.

Честно говоря, я думал, что они со мной не свяжутся. Странная вещь произошла, они это сделали! Это было собеседование на стажировку, я и раньше проходил много стажировок, но никогда нигде не был принят на работу. Я не виню других, если бы я был достаточно силен, никто бы меня не отпустил. И я уже был на некоторых собеседованиях раньше, я всегда ждал вопроса "Вы закончили X стажировок, почему вас не приняли на работу?". Я ждал этого, и наконец это произошло.

Вопросы:
~ Расскажи мне о себе
# Я такой и есть....
~ Вы закончили X стажировок, почему вас не приняли на работу?
# Я говорил отговорки, которые были правдой, но все равно это отговорки.... Мои оправдания были разумными и правдивыми (я ненавижу лгать), так что, думаю, в какой-то момент он, возможно, понял меня.
~ Хорошо, приходи завтра на техническое собеседование.
# Спасибо. Я приду!

Процесс

Одна из тем, которую я написал в своем резюме и о которой не знал, - это "GraphQL". Поэтому мне пришлось изучать GraphQL.

Как вам следует научиться взламывать что-либо?
1. Научитесь создавать его.
2. Научитесь уничтожать его.

Как я обычно поступаю?
Я взламываю без знания о создании.

На этот раз все было по-другому, я начал с урока LinkedIn GraphQL - https://www.linkedin[точка]com/learning/learning-graphql-11292553. Я не эксперт по GraphQL, у меня просто была ночь, чтобы проверить это. Я не закончил его за 1 час, как намеревался, работал около 3-4 часов, практикуясь.

Когда я узнаю что-то, связанное с WEB, я обычно проверяю PentesterLAB / PentesterAcademy / Portswigger. На этот раз я не пользовался PentesterLAB (срок действия моей лицензии истек, и я предпочел кофе вместо того, чтобы продлевать ее).

Mы начнем с базовых знаний GraphQL, а затем перейдем к атакам.

GraphQL [Intro]

В этом разделе я напишу то, что я "понял", а не словарную копи-паст-у.

Мне нравится, когда все организовано, поэтому вот вопросы и ответы.

  1. Что такое язык запросов?
    • Это язык для поиска, модификации и удаления информации из некоторой базы данных.
  2. Что такое API?​
    • "API stands for Application Programming Interface" - шучу. API - это механизм (программное обеспечение / код), который позволяет приложениям взаимодействовать друг с другом. Насколько я понимаю, API берет данные из backend / ОС и представляет их во внешнем интерфейсе приложения​
  3. Как работает API?​
    • Я не хочу это описывать. Либо попросите ChatGPT "Объяснить, как работает API для человека, который никогда не прикасался к компьютеру", либо посмотрите на эту картинку. (Кстати, не поймите меня неправильно, именно так я обычно пишу в ChatGPT)
Изображение [1]: Объяснение работы API

Изображение [1]: Объяснение работы API (источник postman[точка]com)​

Это основные вещи, которые вам следует знать.

Какие типы API существуют?
Честно говоря, я не хотел писать о них, но рано или поздно вы столкнетесь с ними (я абсолютно уверен). Поэтому я думаю, что, по крайней мере, все мы должны обладать базовыми знаниями о них. Я не хочу усложнять этот процесс для читателей, поэтому вот таблица, которую я составил (так как не смог найти ту, которая мне нравится).

NULLRESTSOAPRPCWebHookGraphQL
Форматы данныхJSON/XMLXMLJSON/XML/Protobuf/Thrift/MessagePackJSON/XML/TEXTJSON
Различия
  1. Архитектурный стиль: REST - это архитектурный стиль, который использует набор ограничений для проектирования сетевых приложений.
  2. Без сохранения состояния: Каждый запрос от клиента к серверу должен содержать всю информацию, необходимую для понимания и обработки запроса.
  3. Кэширование: REST API можно кэшировать для повышения производительности.
  4. Унифицированный интерфейс: REST API соответствуют согласованному дизайну интерфейса, включая ресурсы (URI), HTTP-методы, коды состояния и ссылки на гипермедиа.
  1. Связь: Он использует XML для формата сообщений и полагается на различные протоколы, такие как HTTP, SMTP и т.д., для связи.
  2. Сложность: SOAP может быть более сложным из-за его обширных стандартов и функций, включая безопасность и надежность.
  3. Безгражданство: Может быть как без гражданства (stateless), так и с сохранением состояния (stateful), в зависимости от реализации.
  1. Вызов метода: RPC - это протокол, который позволяет программе вызывать процедуру (подпрограмму) для выполнения в другом адресном пространстве (обычно на удаленном сервере).
  2. Безгражданство (Statelessness): вызовы RPC могут быть без гражданства (stateless) или с сохранением состояния (stateful), в зависимости от реализации.
  3. Сложность: Может быть более сложным с точки зрения связи и обработки ошибок по сравнению с более простыми протоколами, такими как REST.
  1. Варианты использования: Обычно используется для обновлений в режиме реального времени, архитектуры, управляемой событиями, и интеграции между различными службами.
  2. Безгражданство (Statelessness): Веб-хуки не имеют гражданства и инициируются событиями на стороне сервера.
  1. Гибкость: Клиенты могут запрашивать несколько типов данных в одном запросе, что сокращает избыточную или недостаточную выборку данных.
  2. Единая конечная точка: GraphQL обычно предоставляет единую конечную точку, и клиенты создают запросы, чтобы указать свои потребности в данных.
  3. Управление версиями (Versioning): Поскольку клиенты указывают необходимые им данные, управление версиями может быть более гибким по сравнению с REST API.
  4. Сложность: Несмотря на гибкость, GraphQL может быть более сложным в реализации, чем традиционные REST API.
Варианты использованияВеб-сервисы/Мобильные приложения/Интернет вещей (IoT)Коммуникационный протокол/Корпоративная интеграция/Веб-сервисыРаспределенные системы/Клиент-серверные приложенияКоммуникация/Автоматизация/Рабочие процессы, управляемые событиямиВеб-приложения/Мобильные приложения/Приложения с интенсивным использованием данных

Очевидно, что есть больше различий, больше объяснений по этому поводу, я просто попытался подвести итог, основываясь на моих собственных мыслях о том, "Что может быть важным?". Возможно, я ошибаюсь, и вы вольны исправить эту таблицу.

На вашем месте, без предыстории, я бы не понял эту таблицу. Итак, позвольте мне объяснить это таким образом.

Что же такое GraphQL?

GraphQL - это язык запросов для API, и основное отличие GraphQL заключается в том, что он отвечает только теми данными, которые нужны пользователю, что предотвращает использование дополнительной полосы пропускания (bandwidth).

Мы собираемся использовать эту игровую площадку для тестовых заданий: snowtooth.moonhighway[точка]com

Изображение [2]: Документы игровой площадки

Изображение [2]: Документы игровой площадки
Документы показывают нам запросы, изменения и подписки, которые мы можем cделать.

Сначала я этого не знал, но есть 2 способа получить информацию: с использованием слова "query" и без его использования. Но оба они считаются "query". Давайте запросим данные обо всех лифтах. Для этого мы сначала должны проверить документы и посмотреть доступные "типы" данных.

1692440011828.png

Изображение [3]: Информация обо всех лифтах
Прежде чем продолжить, давайте рассмотрим это повнимательнее. Типами данных являются id,name,status,capacity... Восклицательный знак, означает, что значение не может быть нулевым. Ниже мы можем видеть "Аргументы", это аргументы, которые мы можем передать, чтобы получить некоторые конкретные данные. В нашем случае этот аргумент "status". Я буду отправлять все запросы со словом "query", потому что так мне удобнее. Давайте запросим id,name,status.

ЗапросОтвет
query { allLifts { id name status } }{ "data": { "allLifts": [ { "id": "astra-express", "name": "Astra Express", "status": "OPEN" }, { "id": "jazz-cat", "name": "Jazz Cat", "status": "OPEN" }, { "id": "jolly-roger", "name": "Jolly Roger", "status": "OPEN" }, { "id": "neptune-rope", "name": "Neptune Rope", "status": "OPEN" }, { "id": "panorama", "name": "Panorama", "status": "HOLD" }, { "id": "prickly-peak", "name": "Prickly Peak", "status": "OPEN" }, { "id": "snowtooth-express", "name": "Snowtooth Express", "status": "OPEN" }, { "id": "summit", "name": "Summit", "status": "CLOSED" }, { "id": "wallys", "name": "Wally's", "status": "HOLD" }, { "id": "western-states", "name": "Western States", "status": "CLOSED" }, { "id": "whirlybird", "name": "Whirlybird", "status": "HOLD" } ] } }


Вот как мы создаем простой запрос, легко, не так ли? Теперь давайте создадим запрос и используем аргумент "status". Enum - это аббревиатура от слова "enumeration". Перечисления (enumeration) - это особый тип для перечисления всех возможных значений в поле. Проще говоря, "enum" - это ограниченный список опций для поля. Если вы этого не поняли, не волнуйтесь, дело в практике, и вы это поймете. Чтобы просмотреть информацию об аргументе, мы должны нажать на него. После нажатия на "LiftStatus" мы видим доступные перечисления "OPEN/CLOSED/HOLD".

Изображение [4]: Информация об аргументе allLifts

Изображение [4]: Информация об аргументе "allLifts"​

Время запрашивать данные! Аргументы используются внутри круглых скобок "()". Давайте запросим все лифты со статусом "OPEN"

ЗапросОтвет
query { allLifts (status: OPEN){ id name status } } { "data": { "allLifts": [ { "id": "astra-express", "name": "Astra Express", "status": "OPEN" }, { "id": "jazz-cat", "name": "Jazz Cat", "status": "OPEN" }, { "id": "jolly-roger", "name": "Jolly Roger", "status": "OPEN" }, { "id": "neptune-rope", "name": "Neptune Rope", "status": "OPEN" }, { "id": "prickly-peak", "name": "Prickly Peak", "status": "OPEN" }, { "id": "snowtooth-express", "name": "Snowtooth Express", "status": "OPEN" } ] } }

Хорошо, теперь возникает вопрос, что делать, если я хочу запросить 2 статуса одновременно. Что делать, если я хочу видеть как статус "HOLD", так и статус "OPEN".

Прежде чем сделать это, давайте снова запросим информацию, но на этот раз я буду использовать "liftCount" внутри "query". "liftCount" также использует "LiftStatus" в качестве аргумента.

Изображение [5]: Информация об аргументе liftCount

Изображение [5]: Информация об аргументе "liftCount"​

ЗапросОтвет
query { liftCount(status:OPEN) allLifts { id name status } } { "data": { "liftCount": 6, "allLifts": [ { "id": "astra-express", "name": "Astra Express", "status": "OPEN" }, { "id": "jazz-cat", "name": "Jazz Cat", "status": "OPEN" }, { "id": "jolly-roger", "name": "Jolly Roger", "status": "OPEN" }, { "id": "neptune-rope", "name": "Neptune Rope", "status": "OPEN" }, { "id": "panorama", "name": "Panorama", "status": "HOLD" }, { "id": "prickly-peak", "name": "Prickly Peak", "status": "OPEN" }, { "id": "snowtooth-express", "name": "Snowtooth Express", "status": "OPEN" }, { "id": "summit", "name": "Summit", "status": "CLOSED" }, { "id": "wallys", "name": "Wally's", "status": "HOLD" }, { "id": "western-states", "name": "Western States", "status": "CLOSED" }, { "id": "whirlybird", "name": "Whirlybird", "status": "HOLD" } ] } }

На этот раз мы использовали "liftCount(status:OPEN)" вместо использования "allLifts (status: OPEN)". Пожалуйста, обратите внимание, что количество "liftCount" отображается только для статуса открыто, в то время как список "liftCount" показывает все. Теперь вы можете подумать, что мы можем просто использовать "liftCount(status:OPEN)" и"liftCount(status:HOLD)", но это не сработает.

ЗапросОтвет
query { liftCount(status:OPEN) liftCount(status:HOLD) allLifts { id name status } } { "error": { "errors": [ { "message": "Fields \"liftCount\" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.", "locations": [ { "line": 2, "column": 3 }, { "line": 3, "column": 3 } ], "extensions": { "code": "GRAPHQL_VALIDATION_FAILED", "exception": { "stacktrace": [ "GraphQLError: Fields \"liftCount\" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.", " at Object.SelectionSet (/app/node_modules/graphql/validation/rules/OverlappingFieldsCanBeMergedRule.js:67:29)", " at Object.enter (/app/node_modules/graphql/language/visitor.js:323:29)", " at Object.enter (/app/node_modules/graphql/utilities/TypeInfo.js:370:25)", " at visit (/app/node_modules/graphql/language/visitor.js:243:26)", " at Object.validate (/app/node_modules/graphql/validation/validate.js:69:24)", " at validate (/app/node_modules/apollo-server-core/dist/requestPipeline.js:221:34)", " at Object.<anonymous> (/app/node_modules/apollo-server-core/dist/requestPipeline.js:118:42)", " at Generator.next (<anonymous>)", " at fulfilled (/app/node_modules/apollo-server-core/dist/requestPipeline.js:5:58)", " at runMicrotasks (<anonymous>)" ] } } } ] } }

Переведите, чтобы понять причину этой ошибки.

Теперь, если мы хотим запросить с помощью 2 разных перечислений, мы можем использовать "alias". Это позволяет нам присвоить аргументам любое имя, и таким образом мы можем написать 2 аргумента с разными "enum".

ЗапросОтвет
query { otkrit: liftCount(status:OPEN) jdyom: liftCount(status:HOLD) allLifts { id name status } } { "data": { "otkrit": 6, "jdyom": 3, "allLifts": [ { "id": "astra-express", "name": "Astra Express", "status": "OPEN" }, { "id": "jazz-cat", "name": "Jazz Cat", "status": "OPEN" }, { "id": "jolly-roger", "name": "Jolly Roger", "status": "OPEN" }, { "id": "neptune-rope", "name": "Neptune Rope", "status": "OPEN" }, { "id": "panorama", "name": "Panorama", "status": "HOLD" }, { "id": "prickly-peak", "name": "Prickly Peak", "status": "OPEN" }, { "id": "snowtooth-express", "name": "Snowtooth Express", "status": "OPEN" }, { "id": "summit", "name": "Summit", "status": "CLOSED" }, { "id": "wallys", "name": "Wally's", "status": "HOLD" }, { "id": "western-states", "name": "Western States", "status": "CLOSED" }, { "id": "whirlybird", "name": "Whirlybird", "status": "HOLD" } ] } }


Kей, в предыдущих случаях нам приходилось использовать "enum" в самом запросе, но на самом деле мы можем отправить запрос в формате json, содержащий переменные запроса. Чтобы иметь возможность отправить запрос, нажмите на "QUERY VARIABLES" в левом нижнем углу.


ЗапросОтвет
query ($xssstatus: LiftStatus){ liftCount(status:$xssstatus) allLifts { id name status } } { "data": { "liftCount": 6, "allLifts": [ { "id": "astra-express", "name": "Astra Express", "status": "OPEN" }, { "id": "jazz-cat", "name": "Jazz Cat", "status": "OPEN" }, { "id": "jolly-roger", "name": "Jolly Roger", "status": "OPEN" }, { "id": "neptune-rope", "name": "Neptune Rope", "status": "OPEN" }, { "id": "panorama", "name": "Panorama", "status": "HOLD" }, { "id": "prickly-peak", "name": "Prickly Peak", "status": "OPEN" }, { "id": "snowtooth-express", "name": "Snowtooth Express", "status": "OPEN" }, { "id": "summit", "name": "Summit", "status": "CLOSED" }, { "id": "wallys", "name": "Wally's", "status": "HOLD" }, { "id": "western-states", "name": "Western States", "status": "CLOSED" }, { "id": "whirlybird", "name": "Whirlybird", "status": "HOLD" } ] } }
Переменные запроса
{ "xssstatus": "OPEN" }

Теперь возникает вопрос, что, если мы отправим 2 переменные "xsstatus" с разными параметрами.

ЗапросОтвет
query ($xssstatus: LiftStatus){ liftCount(status:$xssstatus) allLifts { id name status } } { "data": { "liftCount": 2, "allLifts": [ { "id": "astra-express", "name": "Astra Express", "status": "OPEN" }, { "id": "jazz-cat", "name": "Jazz Cat", "status": "OPEN" }, { "id": "jolly-roger", "name": "Jolly Roger", "status": "OPEN" }, { "id": "neptune-rope", "name": "Neptune Rope", "status": "OPEN" }, { "id": "panorama", "name": "Panorama", "status": "HOLD" }, { "id": "prickly-peak", "name": "Prickly Peak", "status": "OPEN" }, { "id": "snowtooth-express", "name": "Snowtooth Express", "status": "OPEN" }, { "id": "summit", "name": "Summit", "status": "CLOSED" }, { "id": "wallys", "name": "Wally's", "status": "HOLD" }, { "id": "western-states", "name": "Western States", "status": "CLOSED" }, { "id": "whirlybird", "name": "Whirlybird", "status": "HOLD" } ] } }
Переменные запроса
{ "xssstatus": "OPEN", "xssstatus": "CLOSED" }

Результат содержал количество лифтов только со статусом закрыто, это означает, что если мы отправим переменную с 2 разными ключами (опциями), то будет учтен последний.

Запросы, которые мы отправляем, являются "анонимными", потому что мы не дали им имен. Давайте скопируем и вставим наш запрос 2 раза и посмотрим, что произойдет, прежде чем давать запросам имена! В этом случае, когда мы пытаемся запустить, мы видим 2 "<Unnamed>". Это потому, что оба наших запроса анонимны.

Изображение [6]: Кнопка запуска для 2 анонимных запросов.

Изображение [6]: Кнопка запуска для 2 анонимных запросов.​

ЗапросОтвет
query ($xssstatus: LiftStatus){ liftCount(status:$xssstatus) allLifts { id name status } } query ($xssstatus: LiftStatus){ liftCount(status:$xssstatus) allLifts { id name status } } { "error": { "errors": [ { "message": "This anonymous operation must be the only defined operation.", "locations": [ { "line": 1, "column": 1 } ], "extensions": { "code": "GRAPHQL_VALIDATION_FAILED", "exception": { "stacktrace": [ "GraphQLError: This anonymous operation must be the only defined operation.", " at Object.OperationDefinition (/app/node_modules/graphql/validation/rules/LoneAnonymousOperationRule.js:28:29)", " at Object.enter (/app/node_modules/graphql/language/visitor.js:323:29)", " at Object.enter (/app/node_modules/graphql/utilities/TypeInfo.js:370:25)", " at visit (/app/node_modules/graphql/language/visitor.js:243:26)", " at Object.validate (/app/node_modules/graphql/validation/validate.js:69:24)", " at validate (/app/node_modules/apollo-server-core/dist/requestPipeline.js:221:34)", " at Object.<anonymous> (/app/node_modules/apollo-server-core/dist/requestPipeline.js:118:42)", " at Generator.next (<anonymous>)", " at fulfilled (/app/node_modules/apollo-server-core/dist/requestPipeline.js:5:58)", " at runMicrotasks (<anonymous>)" ] } } }, { "message": "This anonymous operation must be the only defined operation.", "locations": [ { "line": 10, "column": 1 } ], "extensions": { "code": "GRAPHQL_VALIDATION_FAILED", "exception": { "stacktrace": [ "GraphQLError: This anonymous operation must be the only defined operation.", " at Object.OperationDefinition (/app/node_modules/graphql/validation/rules/LoneAnonymousOperationRule.js:28:29)", " at Object.enter (/app/node_modules/graphql/language/visitor.js:323:29)", " at Object.enter (/app/node_modules/graphql/utilities/TypeInfo.js:370:25)", " at visit (/app/node_modules/graphql/language/visitor.js:243:26)", " at Object.validate (/app/node_modules/graphql/validation/validate.js:69:24)", " at validate (/app/node_modules/apollo-server-core/dist/requestPipeline.js:221:34)", " at Object.<anonymous> (/app/node_modules/apollo-server-core/dist/requestPipeline.js:118:42)", " at Generator.next (<anonymous>)", " at fulfilled (/app/node_modules/apollo-server-core/dist/requestPipeline.js:5:58)", " at runMicrotasks (<anonymous>)" ] } } } ] } }
Переменные запроса
{ "xssstatus": "OPEN" }


Теперь пришло время присвоить имена обоим запросам, чтобы не возникло ошибки и все было правильно. Я называю первый запрос "xss1", а второй - "xss2". Как креативно, а? Вы можете нажать на любой из них. Если вы хотите, чтобы запрос xss1 работал, вы должны нажать на xss1, если вы хотите, чтобы запрос xss2 работал, вы должны нажать на xss2. В нашем случае оба они одинаковы.
1692447254018.png

Изображение [7]: Кнопка запуска для 2 запросов.​

ЗапросОтвет
query xss1 ($xssstatus: LiftStatus){ liftCount(status:$xssstatus) allLifts { id name status } } query xss2 ($xssstatus: LiftStatus){ liftCount(status:$xssstatus) allLifts { id name status } } { "data": { "liftCount": 6, "allLifts": [ { "id": "astra-express", "name": "Astra Express", "status": "OPEN" }, { "id": "jazz-cat", "name": "Jazz Cat", "status": "OPEN" }, { "id": "jolly-roger", "name": "Jolly Roger", "status": "OPEN" }, { "id": "neptune-rope", "name": "Neptune Rope", "status": "OPEN" }, { "id": "panorama", "name": "Panorama", "status": "HOLD" }, { "id": "prickly-peak", "name": "Prickly Peak", "status": "OPEN" }, { "id": "snowtooth-express", "name": "Snowtooth Express", "status": "OPEN" }, { "id": "summit", "name": "Summit", "status": "CLOSED" }, { "id": "wallys", "name": "Wally's", "status": "HOLD" }, { "id": "western-states", "name": "Western States", "status": "CLOSED" }, { "id": "whirlybird", "name": "Whirlybird", "status": "HOLD" } ] } }
Переменные запроса
{
"xssstatus": "OPEN"
}

Это базовые знания о запросах GraphQL, которые вы можете получить от меня, дальнейшее вы можете изучить самостоятельно. Вот ключевые слова тем, которые я затронул перед изучением атак, вы можете учиться на их основе, если хотите: mutations, subscriptions, fragment, @skip, @include, if, Meta Fields, __type, __typename, __kind.

Самоанализ (Introspection) - это способность сервера GraphQL предоставлять информацию о своей собственной схеме. Это позволяет клиентам запрашивать у сервера подробную информацию о типах, полях и других элементах. В случае, если мы сможем выполнять запросы GraphQL, мы будем использовать самоанализ для получения информации, это похоже на "документы", которые мы использовали.

Метаполя (Meta Fields) содержат метаданные о схеме. Например, мета-поле __type позволяет вам запрашивать саму схему, чтобы получить информацию о типах, определенных в этой схеме.

Давайте отправим запрос на самоанализ, чтобы понять "query" "Lift".

ЗапросОтвет
query { __type(name: "Lift") { name kind fields { name type { name kind description } } } } { "data": { "__type": { "name": "Lift", "kind": "OBJECT", "fields": [ { "name": "id", "type": { "name": null, "kind": "NON_NULL", "description": null } }, { "name": "name", "type": { "name": null, "kind": "NON_NULL", "description": null } }, { "name": "status", "type": { "name": "LiftStatus", "kind": "ENUM", "description": "An enum describing the options for `LiftStatus`: `OPEN`, `CLOSED`, `HOLD`" } }, { "name": "capacity", "type": { "name": null, "kind": "NON_NULL", "description": null } }, { "name": "night", "type": { "name": null, "kind": "NON_NULL", "description": null } }, { "name": "elevationGain", "type": { "name": null, "kind": "NON_NULL", "description": null } }, { "name": "trailAccess", "type": { "name": null, "kind": "NON_NULL", "description": null } } ] } } }

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

Изображение [8]: запрос type в документах.

Изображение [8]: запрос "type" в "документах".

Я не собираюсь говорить о XSS, SQLi, RCE или о чем-либо еще, чего вы можете достичь с помощью GraphQL. Логика здесь такая же, как и в любом другом веб-приложении. Главная уязвимость, которую нужно понять, - это "самоанализ", что не так уж сложно.

Прежде чем продолжить, это ваше домашнее задание:
https://portswigger[точка]net/web-security/graphql/lab-graphql-reading-private-posts
https://portswigger[точка]net/web-security/graphql/lab-graphql-accidental-field-exposure
https://portswigger[точка]net/web-security/graphql/lab-graphql-find-the-endpoint

Итак, давайте рассмотрим 2 простых случая. К сожалению, у меня нет кода, чтобы поделиться с вами этими лабораториями. Вы можете спросить: "Если у нас нет кода, и вы просто показываете решение labs, к которому мы, вероятно, не будем прикасаться, в чем смысл?" Ну, у вас есть лаборатории "portswigger", которые похожи на эти, я не хочу объяснять иx, как я делал это в предыдущем случае, потому что решение и так есть. В предыдущем случае я решил portswigger lab только потому, что это был аналог моего собственного случая.

Лаборатория № 1
Цель
: Получить флаг
Если вы хотите знать, откуда взялась эта лаборатория, не стесняйтесь писать мне в личку.

Как обычно, сначала мы открываем веб-сайт. Вы можете это не читать.
Я не мистер Робот, чтобы запускать "cURL", анализировать заголовки, использовать "whatweb" и любые дополнительные функции перед входом на веб-сайт. Я просто открываю его на виртуальной машине в браузере, подобном "chromium", и если в этом случае кто-то причинит вред моему компьютеру, респект, парень хороший! Я просто попытаюсь связаться с этим парнем и спросить его, какие темы мне следует затронуть, чтобы самосовершенствоваться.
Изображение [9]: Открытие лаборатории

Изображение [9]: Открытие лаборатории
Буду честен, ребята, я не знал, что делать, первое, что приходит на ум, это прочитать error и попытаться найти параметр, через который мы можем отправить запрос GraphQL.

Изображение [10]: Отправка пустого запроса

Изображение [10]: Отправка пустого запроса
Я угадал с параметром. Если вы знаете, какие еще параметры мы можем использовать, напишите об этом в комментариях, пожалуйста, это может быть очень полезным знанием на случай, если кто-то вроде меня захочет автоматизировать весь процесс. Если бы мне пришлось угадывать параметры, я бы использовал: query, q, graphql, graph, api. И, возможно, я бы добавил цифры от 1 до 5 после каждого параметра (на всякий случай) Например: api1, api2.....
Я бы добавил цифры в конце только потому, что, возможно, какая-то система будет иметь другой API или обновится до нового API (например, от REST к GraphQL), просто добавив номер в конце конечной точки. Не осуждайте меня за то, что я говорю, что "Это глупая идея". Убедившись, что заголовки типа "X-Forwarded-For" работают как обход в системах, я не думаю, что найдется что-то глупее.

В любом случае, в нашу лабораторию, чтобы решить эту проблему, мы должны отправить запрос на самоанализ, но в случае, если раньше мы знали название "query", в то время как в этом случае мы ничего не знаем. Вы можете просто пропустить все и использовать это: https://gist.github[точка]com/localh0t/240a8037922a0b168ea85fd8fef7bded или вы можете попробовать сделать все это вручную, как я предпочитаю.

Чтобы сделать это вручную, мы должны сначала отправить запрос {__schema{types{name}}}. Давайте разберем это на части.
Схема (__schema) - это описание данных (types) , которые клиенты могут запрашивать. Типы могут представлять типы объектов, скалярные типы , типы перечислений и многое другое. "name" - означает, что мы запрашиваем имена типов, определенных в схеме.
1692453405859.png

Изображение [11]: Получение имен​
Теперь у нас есть название "NashFlag", и мы можем сделать запрос.


query { __type(name: "NashFlag") { name kind fields { name type { name kind description } } } }

1692453743652.png

Изображение [12]: Получение имен полей
Теперь у нас есть имя "query" и имя "field", которых достаточно для отправки запроса.

query { NashFlag { value } }

Изображение [13]: Получение флага

Изображение [13]: Получение флага​

Вторая лаборатория такая же, как и первая, я не вижу причин это объяснять.

Как бы я искал graphql в больших областях?

Я бы использовал "waybackurls" для получения URL-адресов домена / поддоменов. Позже я бы создал пользовательский инструмент, который удалит значение любого параметра, заменит его на GraphQL introspection и проверит ответ.

ChatGPT TIME!

Я не программист, как я писал в своей предыдущей статье, я не пишу код, это делает ChatGPT, я просто исправляю его.

Что должен делать наш код?
Наш код должен удалить все значения параметров и заменить их на {__schema{types{name}}}, и если результат содержит __schema, query или mutation, он должен вывести это как "успех".

#!/bin/bash

# Function to replace parameter values with "{__schema{types{name}}}"
function replace_params() {
local url="$1"
local replaced_url=""
IFS='?' read -ra parts <<< "$url" # Split URL into base and query parts

if [ "${#parts[@]}" -eq 2 ]; then
replaced_url="${parts[0]}?" # Base URL
IFS='&' read -ra params <<< "${parts[1]}" # Split query parameters

for param in "${params[@]}"; do
IFS='=' read -ra param_parts <<< "$param" # Split parameter into name and value
param_name="${param_parts[0]}"
replaced_url="${replaced_url}${param_name}=%7b%5f%5fschema%7btypes%7bname%7d%7d%7d&"
done

replaced_url="${replaced_url%&}" # Remove the trailing '&' if present
else
replaced_url="$url" # No query parameters, just use the base URL
fi

echo "$replaced_url"
}

# Function to check if response contains "__schema" or "name":"Query" or "name":"Mutation"
function check_response() {
local response="$1"

if [[ "$response" == *'{"__schema":'* || "$response" == *'"name":"Query"'* || "$response" == *'"name":"Mutation"'* ]]; then
echo "Success"
else
echo "Failure"
fi
}

# Read file name from user input
read -p "Enter the file name containing URLs: " file_name

# Check if the file exists
if [ ! -f "$file_name" ]; then
echo "File not found!"
exit 1
fi

# Process each URL in the file
while IFS= read -r url; do
replaced_url=$(replace_params "$url")
response=$(curl -s "$replaced_url") # Send a GET request to the modified URL
check_result=$(check_response "$response")

echo "Original URL: $url"
echo "Modified URL: $replaced_url"
echo "Check result: $check_result"
echo
done < "$file_name"

Изображение [14]: GraphQL.sh

Изображение [14]: GraphQL.sh
Моменты, которые, как мне кажется, я рассмотрел:
Множество параметров

Моменты, которые, как мне кажется, я не учел:
Threading
Брандмауэры
Разные ответы

Если я получу несколько комментариев от людей, которые хотят использовать это как обычный инструмент. Я подумаю о том, чтобы сделать что-нибудь приятное для белого использования.

Я ничего не обещаю. Если я что-то обещаю, то делаю это либо потому, что злюсь, либо потому, что я больше уверен в себе, чем в логотипе Кали.

Несколько полезных ссылок:
https://github[точка]com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md#common-graphql-endpoints
https://chat.openai[точка]com

Автор grozdniyandy

Источник https://xss.pro/​

 
Последнее редактирование:
комментарий будет готов через 1-2 часа, я опубликовал это, потому что иногда забываю их оставлять. Не удаляйте, пожалуйста
Скрытый контент для пользователей групп Администратор.
 
Последнее редактирование:
комментарий будет готов через 1-2 часа, я опубликовал это, потому что иногда забываю их оставлять. Не удаляйте, пожалуйста
Скрытое содержимое
xD
 
комментарий будет готов через 1-2 часа, я опубликовал это, потому что иногда забываю их оставлять. Не удаляйте, пожалуйста
Скрытое содержимое
Тогда, наверное, вы забыли прокомментировать мою предыдущую статью =)
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх