Trustwave SpiderLabs Uncovers Unique Cybersecurity Risks in Today's Tech Landscape. Learn More

Trustwave SpiderLabs Uncovers Unique Cybersecurity Risks in Today's Tech Landscape. Learn More

Services
Capture
Managed Detection & Response

Eliminate active threats with 24/7 threat detection, investigation, and response.

twi-managed-portal-color
Co-Managed SOC (SIEM)

Maximize your SIEM investment, stop alert fatigue, and enhance your team with hybrid security operations support.

twi-briefcase-color-svg
Advisory & Diagnostics

Advance your cybersecurity program and get expert guidance where you need it most.

tw-laptop-data
Penetration Testing

Test your physical locations and IT infrastructure to shore up weaknesses before exploitation.

twi-database-color-svg
Database Security

Prevent unauthorized access and exceed compliance requirements.

twi-email-color-svg
Email Security

Stop email threats others miss and secure your organization against the #1 ransomware attack vector.

tw-officer
Digital Forensics & Incident Response

Prepare for the inevitable with 24/7 global breach response in-region and available on-site.

tw-network
Firewall & Technology Management

Mitigate risk of a cyberattack with 24/7 incident and health monitoring and the latest threat intelligence.

Solutions
BY TOPIC
Microsoft Exchange Server Attacks
Stay protected against emerging threats
Rapidly Secure New Environments
Security for rapid response situations
Securing the Cloud
Safely navigate and stay protected
Securing the IoT Landscape
Test, monitor and secure network objects
Why Trustwave
About Us
Awards and Accolades
Trustwave SpiderLabs Team
Trustwave Fusion Security Operations Platform
Trustwave Security Colony
Partners
Technology Alliance Partners
Key alliances who align and support our ecosystem of security offerings
Trustwave PartnerOne Program
Join forces with Trustwave to protect against the most advance cybersecurity threats
SpiderLabs Blog

From Response To Request, Adding Your Own Variables Inside Of GraphQL Queries For Account Take Over

For those wondering what GraphQL is…

“GraphQL is a query language for your API, and a server-side runtime for executing queries using a type system you define for your data. GraphQL isn't tied to any specific database or storage engine and is instead backed by your existing code and data.” (Taken from https://graphql.org/learn/)

For those who are already familiar with GraphQL, especially from a security perspective, the first thing we tend to think about is “Introspection” – the ability for a user to get the server to hand over all the information related to the schema and queries by issuing a magic request. This blog post is not about that aspect. In general, you should disable “Introspection” on production systems, but from an attacker (and pentester!) point of view, there are still ways around this to get things back, due to helpful and verbose error messages from GraphQL – but that’s for another blog post.

This blog post is instead about looking at the normal operation of a GraphQL query, looking closely at the response and then using that information to feed back into the request (the GraphQL query itself), to, you got it, make it do something it wasn’t intended to do – resulting in account take over in this instance.

Let’s jump right in at the deep end.

Imagine a typical (retail) web application; customers have accounts (or they can register), they log in and buy stuff with their payment details held on file. The company then ships products out to the customer’s address (again on file) once ordered. Amazon was onto something here… Within this application the user has the ability to view/update their ‘profile’ via a profile page, they can change their name, e-mail address, home address, security question and security answer. Now, this profile page (and the whole application) ‘under the hood’ makes use of GraphQL to retrieve this goodness from the server. When the user selects their profile page the following GraphQL query is sent off by the application.

DOC_19978_picture1

Figure 1. GraphQL request sent off when the user visits their profile page.

Based on the user’s request for the profile page, the subsequent response (shown below) is returned by the server (from GraphQL), which the application then takes and populates into the UI elements in the profile page itself.

DOC_19979_picture2

 Figure 2. GraphQL response returned which is used to populate UI elements in the profile page.

So if we look at the response, our spidey senses should immediately be tingling when we see the “id” reference and what looks like an incremental/predictable identifier per customer – what we call Indirect Object Reference (IDOR) vulnerabilities in pentester land. As a pentester proof of concept, we’d usually increase/decrease this “id” on our own value (e.g., “12342” becomes “12343”), access another user’s profile and clock off, job done. But hold up there, partner, not so fast! A slight problem exists, we’re used to seeing these kinds of things in the request and in this instance it lives in the response. Hacking up responses isn’t going to pay those bills.

Park that thought for a moment. It is at this point that we look more closely at the request and break it down a little, zooming in (literally) on the first part.

DOC_19980_picture3

Figure 3. The baseline request to retrieve user details has no variables configured.

This first part of this request is creating an operation name called “myProfile” and then a query of the same name is created within this operation. The data is to be returned back within “current_user”, the data being the user’s id, their e-mail address, etc. That’s not the interesting bit. What is interesting is that the “variables” section which we can see is currently blank.

The developer intended this (getter) call to populate the profile page with the data to take no (user-defined) arguments. In the design of the application (of the profile page), the web application (via GraphQL) makes a call to the server endpoint in the context of the requesting user and retrieves the data belonging to them. Going back to the variables thing before (or lack of), think of these as arguments (or ‘argvs[]’ if you like) to a computer program. In GraphQL we can use variables within a query, just like arguments. Just because the developer didn’t intend on the call to pass any arguments doesn’t mean it has to stay that way. Let’s grab that black hoody from the back of the chair, put it on and hood up, hacker mode enabled.

Now, we know that the application uses the “id” key as an identifier for customers – we learned that from the response data for our own user. Now with the hacker hoody on, we can re-write the request (shown below) as follows, taking that “id” value from the response and adding it into the request, but now as an argument, adding it into the “variables” bit as “id”. We then tweak the query to define and reference this new variable as an argument to use when it makes the call, via the addition of “($id: Int!)” and the “user(id: $id)” bits. Lastly, we change the value from our own (“12342”) to one number above it (we could go below also) to stumble upon another user. In this instance we change this from “12342” to “12343”. We then light the fuse and fire the canons delivering the GraphQL ball - fingers in those ears.

DOC_19981_picture4

Figure 4. Rolling our own GraphQL query for the profile page, now with added variables, id=12343.

Meanwhile, the server receives this request (the same endpoint as before) but this time it sees that this query contains the variable “id” of value “12343” and uses this in plucking the corresponding data from its store. Microseconds later and we (the client) receive the response, with the profile data relating to other user who has an “id” of “12343” (our next door neighbor in “id” terms) which happens to be “venom”.

DOC_19982_picture5

Figure 5. User profile details relating to another user (venom, id=12343) are returned.

Not shown here, but next steps, we would then use the forgotten password functionality, piping in venom’s e-mail address and provide the correct security answer (“me uhdfhgfhguha234”) (which we learned of via this attack) and reset their password to take over their account.

Taking this further (again, not shown here), we could take over all customer accounts in the application by sending this baseline request to something like Burp Intruder (or busting out Python/curl) to increment through all the “id” values of “00000” to “99999” to return us the e-mail addresses and corresponding security answers for every customer.

Time to celebrate with a cup of tea and a few biscuits.

As you can see, the attack isn’t all that complex, we’re not spitting out shellcode or spraying any heaps. It isn’t all that different to a usual IDOR you’d usually see, however, it requires a little more effort to exploit. Sometimes these things can be overlooked because the baseline request lacks such an argument to manipulate in the first place. The endpoint, GraphQL in this instance (but it could be any endpoint), is however happy to support such a request but no one ever sends it one – poor endpoint. What I’m trying to say (the purpose of this blog post), rather than conveying any pentester technical wizardry (because this isn’t all that complex), is that sometimes it is worth trying those requests – re-creating those baseline requests, based on things you learn in the responses of the application or elsewhere (JavaScript, etc). You may just retrieve that treasure.

From a defensive standpoint, why is this happening and how does one stop it? This happens because on the server side, code (and logic) exists to parse any user-defined “id” values in order to be more useful elsewhere in the application. Perhaps (and likely) there is an administrative interface which exists whereby an administrator needs to make queries on arbitrary users; pull up their details, reset their password, etc. which means that this same GraphQL endpoint is also used (and therefore shared) by that functionality too. If that’s the case, it needs to be locked down or perhaps divided up and moved to an administrator only endpoint – not available in a general customer (publicly accessible) one.

As always, thanks for reading!

Latest SpiderLabs Blogs

Zero Trust Essentials

This is Part 5 in my ongoing project to cover 30 cybersecurity topics in 30 weekly blog posts. The full series can be found here.

Read More

Why We Should Probably Stop Visually Verifying Checksums

Hello there! Thanks for stopping by. Let me get straight into it and start things off with what a checksum is to be inclusive of all audiences here, from Wikipedia [1]:

Read More

Agent Tesla's New Ride: The Rise of a Novel Loader

Malware loaders, critical for deploying malware, enable threat actors to deliver and execute malicious payloads, facilitating criminal activities like data theft and ransomware. Utilizing advanced...

Read More