prsecurity.org

Casual Analysis of Valak C2

You can play with the C2 using the notebook on my github.

The sample under investigation is 0dcee360983fa2e073e9f07e06b6d695, sto.exe.

When executing the sample on app.any.run, we observe two initial http requests to atm506[.]com and housefull2006[.]com, of which only one is responsive.

There is no response in the sandbox, so let’s analyze the payload. Looking more carefully at the payload, it appears to be somewhat obfuscated version of base64:

77u_bm9uY2U9N2JjZGY3NWFkMjM3YjhlMDJlMzAxZjQwOTFmY/jZiYzgmdmVyc2lvbj02JmdpZD1yZXNjdTEmc29mdD1WYWxhay/Z1c2VybmFtZT1hZG1pbiZwY25hbWU9VVNFUi1QQyZkb21haW4/9VVNFUi1QQyZjb3JwPWZhbHNlJmlkPUY2MTA0MDlDNUJBRDY4/NDk5Mjk1NzJBMUFCQjJFRjU5JnNpZz1mM2E5ZjY4OTA2MWI1N/TdjN2Q5MjY5YjZjY2Y3OTYxOA_2cea.html

It appears that == was replaced with _2cea, = with _3DF, / with _ and + with -. Applying the transformation and decoding the request, we get the following registration request:

"nonce":"7bcdf75ad237b8e02e301f4091fb6bc8",
"version":"6",
"gid":"rescu1",
"soft":"Valak",
"username":"admin",
"pcname":"USER-PC",
"domain":"USER-PC",
"corp":"false",
"id":"F610409C5BAD6849929572A1ABB2EF59",
"sig":"f3a9f689061b557c7d9269b6ccf79618"

The sig field appears to be the md5 hash of the previous data points concatenated in the URL format. With that, we can write up our own registration request.

If bot_id is unique, the registration request will return another script with a --BODY header. Decoding the script, we get the bot config and some utilities:

Specifically of interest is the GetQuery function, which provides another URL for getting tasks and plugins:

The check-in message looks like this:

"gid":"rescu1",
"id":"F610409C5BAD6849929572A1ABB2EF59"

Let’s call this API with our bot_id:

It returns a number of plugins and 2 tasks (VT links):

The modules uses a known technique for executing serialized .NET objects.

After observing each module in Wireshark, the following deductions can be made:

The data is sent base64-encoded via POST body. The path contains same obfuscated base64 string that sets the processing configuration for the content.

data = {
    "nonce1":"981",
    "nonce2":"2324",
    "id":F610409C5BAD6849929572A1ABB2EF59,
    "plugin":"ipgeo",
    "ltype":"GEOINFO_JSON",
}

The code path is affected only by ltype parameter, and can take the following values:

The list above contains only observed values, and may not be complete. A special note needs to be mentioned around PASSWORD_GRABBER, which unlike other options, takes it input as XML. The C2 is written in PHP using Laravel framework.

And it uses a simple web authentication.