πŸ“ŠCraft CMS Unauthenticated SQLi via GraphQL

Craft CMS Unauthenticated Blind (time-based) SQL Injection via GraphQL API Endpoint. Craft CMS <= 3.7.31

During a private assessment I discovered a public facing GraphQL endpoint with introspection enabled. After some further digging I found that the majority of the calls could be made without authentication. These calls had a lot of options that seemed to affect the query being done on the database so I decided to play around with a few of the queries. In doing so I discovered that the orderBy argument returns a very verbose error that contains full paths to files a well as the query being executed itself.

Because I now could get an insight into what was running on the backend, Craft CMS (Pro or enterprise edition because of the GraphQL endpoint), I could figure out exactly how I wanted to craft the query in order to get some sort of SQL injection. To do this though I needed to escape and write to the query itself.

This part took the longest surprisingly because it wasn’t as simple as adding a β€œ or β€˜. After two days of messing about with the queries I found it. Including \` in the query, basically anywhere seemed to do the trick. And in the end, for the payload, I ended up just doing β€œβ€” \\` \n”.

Getting a query to execute now also turned out to be harder than expected and was not, and still is not, as easy as pointing SQLmap at the call and letting it do its thing. Because these are stacked queries and the injection point was injecting into two locations I had to find a way to complete the current query, without errors, and get it to execute the next query without errors. I was able to do this but the last part of the data would always error out, and still does, but the queries are executed before it errors. I was unable to get rid of the now junk data at the end because no MySQL comments worked on all the lines, even multi-line (/*) comments. I believe this is due to the queries having a bunch of new lines in them and also the fact that the multiline comments couldn’t be closed at the end. Someone smarter than me might manage to find a way to do this though.

Because of those blocks I could only manage to get Blind (Time-Based) SQL injection, but this is still very powerful for an attacker and they can still read out of the databases and possibly even get RCE as the default DB user for MySQL in Craft CMS is root, allowing for write access to the system which can be abused by uploading things like PHP webshells.

This issue was patched in Craft CMS 3.7.32.

POC is located here.

This has been assigned CVE-2024-37843.

Last updated