This challenge only has a Gold track.
It's long, slightly frustrating and not easy. At least it seemed easier than decrypt the naughty nice list objective in my opinion.
Hints were given by Dusty Giftwrap.
Frostbit Publication Hint
There must be a way to deactivate the ransomware server's data publication. Perhaps one of the other North Pole assets revealed something that could help us find the deactivation path. If so, we might be able to trick the Frostbit infrastructure into revealing more details.
Frostbit Slumber Hint
The Frostbit author may have mitigated the use of certain characters, verbs, and simple authentication bypasses, leaving us blind in this case. Therefore, we might need to trick the application into responding differently based on our input and measure its response. If we know the underlying technology used for data storage, we can replicate it locally using Docker containers, allowing us to develop and test techniques and payloads with greater insight into how the application functions.
The url to deactivate the ransomware was given in the SantaVision challenge.
Error msg: Unauthorized access attempt. /api/v1/frostbitadmin/bot/<botuuid>/deactivate, authHeader: X-API-Key, status: Invalid Key, alert: Warning, recipient: Wombley
Dusty mentioned that there was a debug mode in the Decrypt challenge. So maybe there's a debug mode in this case as well.
/api/v1/bot/<bot uuid here>/deactivate?debug=true
The X-API-Key header is kind of like an authorization token so that the web application knows that whomever is requesting the deactivation actually has the authorization to do so. The X-API-Key header is controlled by the client. What that means is that we can manipulate the header. Sometimes there are vulnerabilities present when an exploit is introduced in a header.
Creating a header of X-API-Key and a payload of ' gives this:
"Timeout or error in query:\nFOR doc IN config\n FILTER doc.<key_name_omitted> == '{user_supplied_x_api_key}'\n <other_query_lines_omitted>\n RETURN doc"
I didn't recognize this database type because I'd never used it. However, a quick Google search or asking ChatGPT shows that it could be ArangoDB.
Reading the documentation, it seems similar to many other languages used to query dbs, but slightly different in how the data is handled. The most helpful functions I found were here: Document and object functions in AQL | ArangoDB Documentation, here: Miscellaneous functions in AQL | ArangoDB Documentation and here: String functions in AQL | ArangoDB Documentation.
First, I ran tests to see what characters were not allowed. Anything not allowed whether it was a valid query or not returned a "Request Blocked" message.
Second, I ran tests to see what verbs were blocked and if comments were blocked. Comments were blocked. Several verbs were also blocked.
This ended up being vulnerable to time-based blind sql injection. You can get the database to spill the beans on what is a valid character in like a key name for example, by having it sleep for 5 seconds before returning a valid response. So now I needed to get it to sleep.
This worked:
' OR 1==1 AND SLEEP(5) OR '
After that, I needed a conditional statement to test whether or not something was true. The ternary operator was handy.
' OR 1==1 AND 1==1 ? SLEEP(5) : null OR '
What this means is OR 1==1 (ie true) AND IF true, THEN SLEEP for 5 seconds, if not true then don't do anything, and the OR ' would just run the rest of the query.
If it was true, The query would be returned, however, it would take 5 seconds to return. If it was not true, then it wouldn't take 5 seconds to return the query.
The statemetn above - ie
FOR doc IN config
FILTER doc.<key_name_omitted> == '{user_supplied_x_api_key}'
<other_query_lines_omitted>
RETURN doc
config is the collection, doc are the documents in the collection. doc.<key name omitted> is using a key name to select a specific document. There's more to the query. Then it's returning the document.
We know for sure that likely we need to guess that key name given that it's looking to see that that key name is = to the value of the X-API-Key we've given.
So how do we guess it? Similar to Blind SQL injection. We can use string functions like SUBSTRING and LENGTH.
SUBSTRING will grab a piece of data if the data type is a string. Example: If I look at SUBSTRING("apple",0,1) that's looking at the place where the letter a is. So I could trying something like IF SUBSTRING(doc,0,1) == 'a' to see whether or not the value at that portion of the string is a.
LENGTH will return the length of an item that I'm querying about. So, if I do something like LENGTH("apple") it will return 5 because that string is 5 characters long. Keep in mind, the length in this case might actually be 6 if a new line is included.
We also need to grab information about the documents themselves. This is where ATTRIBUTES document function and FIRST array function comes into play. The FIRST array function gets the first item in an array.
ATTRIBUTES will return the top level keys of a document as an array. The reason we use the FIRST keyword is because we only want the first key for now. If we need other key names, we can use the NTH array function instead of FIRST to select whichever key name we're interested in.
First we may want to guess how long it is.
' OR 1==1 AND LENGTH(FIRST(ATTRIBUTES(doc))) >= 10 ? SLEEP(5) : null OR '
It doesn't send a response back for 5 seconds.
' OR 1==1 AND LENGTH(FIRST(ATTRIBUTES(doc))) >= 20 ? SLEEP(5) : null OR '
Immediate response, so we know it's less than 20
' OR 1==1 AND LENGTH(FIRST(ATTRIBUTES(doc))) >= 15 ? SLEEP(5) : null OR '
greater than 15.
' OR 1==1 AND LENGTH(FIRST(ATTRIBUTES(doc))) >= 18 ? SLEEP(5) : null OR '
yes it is, so it could be 19
' OR 1==1 AND LENGTH(FIRST(ATTRIBUTES(doc))) == 18 ? SLEEP(5) : null OR '
Looks like it's 18 characters long. Again, I don't recall whether or not it counted the new line, so it's possible it could count the newline as well, in which case technically it would be 19 characters, but we don't need to guess the newline, so 18 it is. This is just showing how guessing the length can be done.
Now to start guessing. Remember that computers start counting from 0.
' OR 1==1 AND SUBSTRING(FIRST(ATTRIBUTES(doc)),0,1) == 'a' ? SLEEP(5) : null OR '
Nope, the key name doesn't start with an a. We got an immediate response.
few guesses more...
' OR 1==1 AND SUBSTRING(FIRST(ATTRIBUTES(doc)),0,1) == 'd' ? SLEEP(5) : null OR '
It does start with a d.
Then we move onto the next place in the string - ie 1,1. The first number is the place we want - like in this case, 1 - or the second letter of doc. And the second number is how many characters we want to check. In this case, just one. We could test multiple characters if we want, but this works.
' OR 1==1 AND SUBSTRING(FIRST(ATTRIBUTES(doc)),1,1) == 'e' ? SLEEP(5) : null OR '
Thankfully this can be scripted. We can make a script that will do the guesses for us. I chose python. However, results may vary with the way that I did it. I was measuring from the time the request was sent to when a response was sent back according to python. However, it was measuring the delta. So, the valid ones showed as 1 or 2 in my script. It wasn't always reliable. I'm not going to share my script as I'm sure others have done better.
The key ended up being guessable.
deactivate_api_key.
So now we move on to guessing the key itself.
Same thing - get a length first,
' OR 1==1 AND LENGTH(doc.deactivate_api_key) >= 10 ? SLEEP(5) : null OR '
Keep guessing until it's right
then do the following:
' OR 1==1 AND SUBSTRING(doc.deactivate_api_key,0,1) == 'a' ? SLEEP(5) : null OR '
Keep guessing each place until it's right.
Ends up being:
abe7a6ad-715e-4e6a-901b-c9279a964f91
Next, we add this api key as the value for X-API-KEY when going to this url:
/api/v1/bot/<bot uuid here>/deactivate?debug=true
And we get the following response:
{"message":"Response status code: 200, Response body: {\"result\":\"success\",\"rid\":\"\<some rid here>\",\"hash\":\"<some hash here>\",\"uid\":\"403\"}\nPOSTED WIN RESULTS FOR RID <some rid here>","status":"Deactivated"}
Gold achieved.
No comments:
Post a Comment