GraphQL - Hiểu để hack (Phần 2)
I. Lỗ hổng của GraphQL API
Những lỗ hổng của GraphQL thường phát sinh do lỗi triển khai và thiết kế. Ví dụ, tính năng introspection có thể được bật, cho phép kẻ tấn công truy vấn API để thu thập thông tin về cấu trúc của nó.
Thường thì các cuộc tấn công vào GraphQL có thể cho phép kẻ tấn công truy xuất dữ liệu hoặc thực hiện các hành động không được phép nhằm khai thác hoặc chỉnh sửa dữ liệu trái phép. Những cuộc tấn công này có thể gây ra ảnh hưởng nghiêm trọng, đặc biệt nếu người dùng có thể giành quyền quản trị bằng cách thao tác truy vấn hoặc thực thi một kỹ thuật tấn công CSRF. Các GraphQL API có lỗ hổng có thể dẫn đến các vấn đề tiết lộ thông tin.
Trong phần này, chúng tôi sẽ tìm hiểu cách kiểm tra các GraphQL API. Nếu bạn chưa tìm hiểu về graphql thì có thể tạm dừng tại đây và đọc bài viết này của mình trước GraphQL - Hiểu để hack (Phần 1). Giờ thì chúng ta bắt đầu vào việc tìm những lỗ hổng của GraphQL Api thôi.
II. Finding GraphQL endpoints
1. Universal queries
Nếu bạn gửi truy vấn query{__typename}
tới bất kỳ endpoint GraphQl nào, chúng ta có thể nhìn thấy response chứa chuỗi {"data": {"__typename": "query"}}
. Điều này được xem là một truy vấn tổng quát và là một công cụ hữu ích để kiểm tra xem một URL có tương ứng với một dịch vụ GraphQL hay không.
Truy vấn này hoạt động vì mọi endpoints GraphQL đều có một trường gọi là __typename
trả về loại đối tượng đã được truy vấn dưới dạng một chuỗi.
2. Common endpoint names
Các dịch vụ GraphQL thường sử dụng các endpoint suffixes giống nhau. Khi kiểm tra các điểm cuối GraphQL, bạn nên cố gắng gửi các truy vấn đến các endpoints sau để kiểm tra:
/graphql
/api
/api/graphql
/graphql/api
/graphql/graphql
Nếu các điểm cuối phổ biến này không trả về phản hồi GraphQL, bạn cũng có thể thử thêm /v1 vào đường dẫn:
/v1/graphql
/v1/api
/v1/api/graphql
/v1/graphql/api
/v1/graphql/graphql
3. Request methods
Bước tiếp theo trong việc tìm GraphQL endpoint là kiểm tra bằng cách sử dụng cách gửi các requqest khác nhau.
Với các GraphQL endpoint ở môi trường production, chỉ chấp nhận các yêu cầu POST có tiêu đề Content-Type là application/json
, vì điều này giúp bảo vệ khỏi các lỗ hổng CSRF. Tuy nhiên, một số endpoint có thể chấp nhận các phương thức thay thế, như yêu cầu GET hoặc yêu cầu POST sử dụng header là Content-Type: x-www-form-urlencoded
.
Nếu bạn không thể tìm thấy các endpoint GraphQL bằng cách gửi yêu cầu POST đến các điểm cuối phổ biến, hãy thử gửi lại truy vấn tổng quát bằng cách sử dụng các phương thức HTTP khác (PUT, PATCH...)
III.Exploiting unsanitized arguments
Ở bước này, bạn có thể bắt đầu tìm lỗ hổng bảo mật. Kiểm tra các đối số truy vấn là nơi tốt để bắt đầu.
Nếu API sử dụng các đối số để truy cập trực tiếp vào các đối tượng, có thể có lỗ hổng về kiểm soát phân quyền truy cập. Người dùng có thể truy cập thông tin mà họ không được cấp quyền bằng cách cung cấp các đối số của đối tượng cần khai thác. Đây là lỗ hổng Insecure Direct Object References (IDOR).
Ví dụ dưới đây yêu cầu một danh sách các sản phẩm của một shop online:
#Example product query
query {
products {
id
name
listed
}
}
Danh sách sản phẩm chỉ bao gồm những sản phẩm được liệt kê ra (không bao gồm sản phẩm có id=3
)
#Example product response
{
"data": {
"products": [
{
"id": 1,
"name": "Product 1",
"listed": true
},
{
"id": 2,
"name": "Product 2",
"listed": true
},
{
"id": 4,
"name": "Product 4",
"listed": true
}
]
}
}
Từ phản hồi trên chúng ta có thể thấy một số điểm chú ý như sau:
- ID của sản phẩm là một chuỗi số tăng dần
- Sản phẩm có ID 3 không được trả về trong response, có thể vì lý do nó đã được lọc ra
Bằng cách thử truy cập trực tiếp tới sản phẩm có ID 3, chúng ta có thể truy vấn được thông tin sản phẩm đó một cách trực tiếp thông qua request truy vấn thông tin sản phẩm:
#Query to get missing product
query {
product(id: 3) {
id
name
listed
}
}
Và server khi nhận được yêu cầu, do không được phân quyền đúng và an toàn nên đã trả về kết quả là thông tin sản phẩm có ID bằng 3
#Missing product response
{
"data": {
"product": {
"id": 3,
"name": "Product 3",
"listed": no
}
}
}
IV. Discovering schema information
Bước tiếp theo trong việc kiểm thử API là tìm hiểu thông tin về cấu trúc cơ bản của hệ thống.
Cách tốt nhất để làm điều này là sử dụng các truy vấn introspection. Introspection là một chức năng tích hợp trong GraphQL cho phép bạn truy vấn máy chủ để lấy thông tin về cấu trúc.
Introspection giúp bạn hiểu cách tương tác với một API GraphQL. Nó cũng có thể tiết lộ dữ liệu có thể nhạy cảm, như các trường mô tả.
1. Using introspection
Sử dụng introspection để khám phá thông tin về cấu trúc, truy vấn trường __schema
. Trường này có sẵn trên tất cả các truy vấn.
Tương tự như các truy vấn thông thường, bạn có thể chỉ định các trường và cấu trúc của phản hồi mà bạn muốn khi chạy một truy vấn introspection. Ví dụ, bạn có thể muốn phản hồi chỉ chứa tên của các mutation có sẵn.
2. Probing for introspection
Để đảm bảo an toàn nhất cho ứng dụng graphql, chúng ta nên tắt introspection trong môi trường production, nhưng không phải lúc nào mọi người cũng tuân thủ lời khuyên này.
Chúng ta có thể kiểm tra bằng việc thực hiện truy vấn bên dưới. Nếu introspection được kích hoạt, phản hồi sẽ trả về tên của tất cả các truy vấn có sẵn.
#Introspection probe request
{
"query": "{__schema{queryType{name}}}"
}
Ngoài ra, nếu chúng ta sử dụng BurpSuite bản pro, chúng ta có thể sử dụng Burp Scanner để tự động kiểm tra introspection trong quá trình quét của nó. Nếu nó phát hiện rằng introspection đã được kích hoạt, Burp sẽ thông báo lỗi "GraphQL introspection enabled".
3. Running a full introspection query
Bước tiếp theo là chạy một truy vấn introspection đầy đủ đối với các endpoint để bạn có thể nhận được càng nhiều thông tin về cấu trúc của graphql càng tốt.
Truy vấn mẫu dưới đây trả về thông tin đầy đủ về tất cả các truy vấn (queries), mutation, subscription, loại dữ liệu (types) và đoạn mã (fragments).
#Full introspection query
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
args {
...InputValue
}
onOperation #Often needs to be deleted to run query
onFragment #Often needs to be deleted to run query
onField #Often needs to be deleted to run query
}
}
}
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
}
}
}
}
Lưu ý: Nếu introspection được kích hoạt nhưng truy vấn ở trên không chạy, hãy thử loại bỏ các directives: onOperation
, onFragment
và onField
khỏi cấu trúc truy vấn. Nhiều điểm cuối không chấp nhận các directives này như một phần của truy vấn introspection.
4. Visualizing introspection results
Các phản hồi từ các truy vấn introspection có thể chứa rất nhiều thông tin, nhưng thường rất dài và khó xử lý.
Chúng ta có thể dễ dàng xem các mối quan hệ giữa các thực thể schema bằng cách sử dụng một công cụ trực quan GraphQL Visualizer . Đây là một công cụ trực tuyến lấy kết quả từ truy vấn introspection và tạo ra biểu đồ minh họa của dữ liệu được trả về, bao gồm các mối quan hệ giữa các operations và types.
5. Using InQL
Một phương án thay thế cho việc chạy một truy vấn introspection thủ công, bạn có thể sử dụng tiện ích mở rộng InQL của Burp Suite InQL - GraphQL in Burp Suite
InQL là một tiện ích mở rộng của Burp Suite giúp bạn kiểm tra các GraphQL API một cách an toàn. Khi bạn cung cấp một URL cho nó (có thể thông qua việc cung cấp một liên kết điểm cuối trực tiếp hoặc thông qua việc tải lên một tệp JSON), nó sẽ thực hiện một truy vấn introspection yêu cầu tất cả các truy vấn và mutations và hiển thị một cấu trúc để dễ dàng khám phá các kết quả.
V. Lab demo khai thác lỗ hổng GraphQL API
1. Accessing private GraphQL posts
Accessing private GraphQL posts
Bài lab cho chúng ta một danh sách các bài post. Yêu cầu của bài lab là truy cập được vào một bài viết bí mật và lấy ra được password.
Bước 1: Lấy các query cần thiết sử dụng InQl Scanner
Để làm được, trước hết chúng ta có thể cài đặt InQL và sử dụng. Nhìn vào request để lấy ra tất cả các bài posts:
Tiếp theo chúng ta sử dụng chức năng InQl Scanner
Nhập link: https://0a6100d203b8eb468eab039700b0005e.web-security-academy.net/graphql/v1 và ấn Load
Chúng ta thấy được các trường của một bài post:
date
summary
image
author
isPrivate
title
paragraphs
id
postPassword
Chúng ta thấy rằng id các bài posts tăng dần và bị thiếu id 3, vậy chúng ta hãy thử truy vấn tới bài viết có id 3
Bước 2: Sử dụng query để lấy thông tin
Để sử dụng query, chúng ta cần vào một bài posts và xem request. Hoặc chúng ta có thể thấy luôn query getBlogPost trong công cụ InQL Scanner
Bước 3: Thay đổi query để lấy thông tin bí mật
Giờ chúng ta chỉ cần thay id post thành 3 là chúng ta có thể thấy được bài viết có id 3. Và thêm trường postPassword
để lấy được password
Giờ thì submit thôi
2. Lab: Accidental exposure of private GraphQL fields
Lab: Accidental exposure of private GraphQL fields
Bài lab yêu cầu login được tài khoản admin và xóa user carlos
Bước 1: Sử dụng InQL Scanner để lấy thông tin query và mutation: Load link: https://0a510021036109d180244e7100cf00f0.web-security-academy.net/graphql/v1
Bước 2: Sử dụng query getUser
Query mặc định với id là 1334, chúng ta sẽ thử sửa thành 1
- Send query to repeater:
- Edit id from
1334
to1
để lấy thông tin đăng nhập admin (admin thường có id là 1)
Bước 3: Login với tài khoản admin và xóa user carlos
VI. Tổng kết
Qua nội dung bài viết, chúng ta có thể thấy khi sử dụng GraphQL không đúng có thể gây ra một số vấn đề bảo mật nghiêm trọng làm lộ dữ liệu nhạy cảm. Vì vậy khi sử dụng, chúng ta cần lưu ý một số biện pháp an toàn:
- Nên disable introspection trên môi trường production để hạn chế việc lộ những thông tin nhạy cảm về hệ thống api (endpoint, query, mutation...)
- Nếu cần sử dụng introspection thì cần lưu ý review lại toàn bộ API's schema để đảm bảo không để lộ ra những trường thông tin nhạy cảm, không cần thiết.
- Đảm bảo suggestions (gợi ý query đúng) được disabled để hạn chế nguy cơ việc kẻ tấn công có thể tìm được endpoint
- Đảm bảo rằng API's schema không để lộ bất kỳ thông tin nhạy cảm hay bí mật nào (Ví dụ: password, email, code, pin...).
All rights reserved