After some weeks of not seeing the RDP scanner module of Trickbot, I recently observed that the module was again distributed among the bots in our tracking lab. Since Bitdefender already published a report on the module in March 2020, I focused on checking whether or not the command-and-control (C2) communication of the module remained more or less the same or if there was anything groundbreakingly new. Short answer: there wasn’t. There may be some under-the-hood fixes or improvements but I (as of yet) did not stumble upon anything significant that wasn’t already found by Bitdefender: the module still receives its mode of action, target servers, usernames, and password candidates from the C2 server and then does what the mode tells it to do. But while I was checking that, I also had a look at the actual data that we received from the C2 server.

Password List

My intuition on the password list was that it is just a dictionary of words to try. This is also suggested by the URL which is used to retrieve the password list: hxxps://%c2%/%gtag%/%bot_id%/rdp/dict. Thus I did not have a closer look at the password list at that time, because everything looked the way Bitdefender described it and I had no reason to look at it in detail. But one or two days later, I re-requested the list of passwords to see whether the list changed in the meantime – and it did indeed. Because of that I had a quick look at what changed and then I noticed that I overlooked something right from the start (literally, duh!). On the left side of the picture you see what I had a quick look at after retrieving the password list from the C2 server with curl (and thus seeing only the last lines of the output). On the right side there is the very same password list, just seen from the start.

trickbot_passwordlist

To the keen eye it seems that they may be using some kind of templating mechanism to adjust the list of passwords and use more specific credential candidates. With that thought in mind I spun up my analysis environment and started digging into the module to see what the Trickbot gang is actually doing there (spoiler: yes, they do some kind of templating – but not just the find-and-replace kind).

Transforming them P@ssw0rds

As mentioned before, this is not a simple find-and-replace but instead they can change the credential candidates to better fit the attacked host. In that sense, I decided to call those things transforms instead of templates because they are not just templates that are filled out but a little bit more dynamic. Example:

  • %username%123 → myuser123 (lowercase)
  • %Username%123 → Myuser123 (lowercase but first char uppercase)
  • %UsErNaMe%123 → MyUsEr123 (alternating case, starting with uppercase char)
  • %EMANRESU%123 → RESUYM123 (uppercase and reversed)

And that is essentially how the markers in the password list work. I was able to extract all 91 transformations that are currently available to the rdpscanDll (as of 2020-08-14). Please find the list with all transforms with an example and a description for each of them at the end of this blog post.

Some of the transforms can even be parameterized to a certain degree: %OriginalUsername%, %OriginalDomain%, and %domain% can be prepended or appended with an (N) to indicate whether the first N or last N characters of the element should be used (or everything if no parameters are present).

Reconnaissance

After finding the list of transforms, I decided to ask my favorite internet search engine whether these names for the transforms are known related to RDP. And I indeed found a RDP brute force tool by a certain z668 which seemingly makes use of some of the transforms that are used in the rdpscanDll. Although this tool seems to be a standalone application, the names of the transforms and the context of their use could suggest a connection between z668 and the Trickbot gang – at least to a certain degree. Sure, the connection may not be really strong because the Trickbot module is written in C++ and the RDP tool seems to be written in C#. But given the fact that C# can load and use native DLLs and considering that z668 forked the FreeRDP project on Github, the actual scanner may indeed be written in C/C++ (and probably using FreeRDP). Thus it is possible that the Trickbot gang may have obtained the source code from z668 to integrate the RDP scanner into their module framework and to use their C2 communication protocol. But: this is just guessing based on some more or less loose facts – I could easily be completely wrong with that.

Transform List

Transform Identifier Example Description
EmptyPass tries an empty password
GetHost fills in the hostname of the currently attacked IP (ex: myhost)
IP the currently attacked IP address (ex: 234.234.234.234)
Port fills in the currently attacked port (ex: 3389)
IpReplaceDot 234.234.234.234 → 234234234234 remove the dots of the IP address
RemoveNumerics us3rn4me → usrnme removes all number from the username
RemoveLetters us3rn4m3 → 343 removes all letters from the username
RemoveOtherSymbols usern@m3 → usernm3 removes all non-alphanumeric characters from the username
OriginalUsernameLettersBeginInverse 123admin456 → 123654nimda keeps all non-letters (i.e. digits, special chars) at the beginning of the username and reverses the rest (“invert [from where] letters begin
OriginalUsernameLettersBeginSwap 123admin456 → admin456123 swaps all non-letters (i.e. digits, special chars) at the beginning of the username with the rest (“swap [where] letters begin”)
OriginalUsernameLettersEndInverse admin123root → admintoor321 keeps all letters at the beginning of the username and reverses the rest (“invert [where] letters end”)
OriginalUsernameLettersEndSwap admin123root → 123rootadmin swaps all letters at the beginning of the username with the rest (“swap [where] letters end”)
OriginalUsernameNumsBeginInverse admin123root → admintoor321 keeps all non-digits at the beginning of the username and reverses the rest (“invert [from where] nums begin
OriginalUsernameNumsBeginSwap admin123root → admintoor321 swaps all non-digits at the beginning of the username with the rest (“swap [where] nums begin”)
OriginalUsernameNumsEndInverse 123admin → 123nimda keeps all digits at the beginning of the username and reverses the rest (“invert [where] nums end”)
OriginalUsernameNumsEndSwap 123admin456 → admin456123 swaps all digits at the beginning of the username with the rest (“swap [where] nums end”)
OriginalUsernameInsert %OriginalUsernameInsert%(N)SOMESTRING → SOMEusernameSTRING (ex: N = 4) insert username after Nth character of SOMESTRING
OriginalUsername use the username as password
OnlyName Firstname Lastname → Firstname uses only the first name (everything left of the first space) of the username as password
OnlySurname Firstname Lastname → Lastname uses only the last name (everything right of the first space) of the username as password
username Admin → admin username in lowercase
Username AdMin → Admin username lowercase but first char upper
UsErNaMe Admin → AdMiN username in alternating case, starting with uppercase
uSeRnAmE Admin → aDmIn username in alternating case, starting with lowercase
USERNAME Admin → ADMIN username in uppercase
EMANRESU Admin → NIMDA username in uppercase and reversed
EmanresuLowercase AdMin → Nimda username reversed and lowercase, first char uppercase
Emanresu AdMin → NiMdA username reversed, first char upper
emanresuLowercase AdMin → nimda username reversed and lowercase
emanresuUppercase AdMin → NIMDA username reversed and uppercase
emanresu Admin → nimda username reversed and lowercase
ReplaceFirst_X-x administrator → @dministrator (ex: %ReplaceFirst_a-@%) replaces the first occurrence of X with x in the username (needle and replacement can be more than 1 char)
ReplaceFirstI_X-x Administrator → @dministrator (ex: %ReplaceFirstI_a-@%) case insensitively replaces the first occurrence of X with x in the username (needle and replacement can be more than 1 char)
ReplaceLast_X-x administrator → @dministrator (ex: %ReplaceLast_a-@%) replaces the last occurrence of X with x in the username (needle and replacement can be more than 1 char)
ReplaceLastI_X-x Administrator → @dministrator (ex: %ReplaceLastI_a-@%) case insensitively replaces the last occurrence of X with x in the username (needle and replacement can be more than 1 char)
ReplaceAll_X-x administrator → @dministrator (ex: %ReplaceAll_a-@%) replaces all occurrences of X with x in the username (needle and replacement can be more than 1 char)
ReplaceAllI_X-x Administrator → @dministrator (ex: %ReplaceAllI_a-@%) case insensitively replaces all occurrences of X with x in the username (needle and replacement can be more than 1 char)
DomainRemoveNumerics test-123.com → test-.com removes all digits from the domain
DomainRemoveLetters test-123.com → -123. removes all letters from the domain
DomainRemoveOtherSymbols test-123.com → test123com removes all non-alphanum chars from the domain
OriginaldomainInsert %OriginaldomainInsert%(N)SOMESTRING → SOMEdomainSTRING (ex: N = 4) insert domain after Nth character of SOMESTRING
OriginaldomainPart test-123.com → 123com (ex: %OriginaldomainPart%(6)) takes the last N chars of the domain name (ignoring any dots)
OriginaldomainNumsBeginInverse test-123.com → test-moc.321 keeps all non-digits at the beginning of the domain and reverses the rest (“invert [from where] nums begin
OriginaldomainNumsBeginSwap test-123.com → 123.comtest- swaps all non-digits at the beginning of the domain with the rest (“swap [where] nums begin”)
OriginaldomainNumsEndInverse 123-test.com → 123moc.tset- keeps all digits at the beginning of the domain and reverses the rest (“invert [where] nums end”)
OriginaldomainNumsEndSwap 123-test.com → -test.com123 swaps all digits at the beginning of the domain with the rest (“swap [where] nums end”)
OriginaldomainLettersBeginInverse test-123.com → test-moc.321 keeps all non-letters (i.e. digits, special chars) at the beginning of the domain and reverses the rest (“invert [from where] letters begin
OriginaldomainLettersBeginSwap 123-test.com → test.com123- swaps all non-letters (i.e. digits, special chars) at the beginning of the domain with the rest (“swap [where] letters begin”)
OriginaldomainLettersEndInverse test-123.com → testmoc.321- keeps all letters at the beginning of the domain and reverses the rest (“invert [where] letters end”)
OriginaldomainLettersEndSwap test-123.com → -123.comtest swaps all letters at the beginning of the domain with the rest (“swap [where] letters end”)
Originaldomainleft test-123.com → test-123 takes the left part of the domain (everything left of the first dot) and lowercases the first character
OriginalDomainleft test-123.com → Test-123 takes the left part of the domain (everything left of the first dot) and capitalizes the first character
Originaldomainright test-123.com → test-123 takes the right part of the domain (everything right of the first dot) and lowercases the first character
OriginalDomainright test-123.com → Test-123 takes the right part of the domain (everything right of the first dot) and capitalizes the first character
Originaldomain uses the plain domain name
OriginalDomain test-123.com → Test-123.com uses the domain name and capitalizes the first character
NiamodLowercase abc%NiamodLowercase%123 abc123
niamodLowercase test-123.com → Moc.321-tset reverses and lowercases the domain name, first character capitalized
niamodUppercase test-123.com → mOC.312-TSET reverses and capitalizes the domain name, first char lowercase
domainleftHyphen test-123.com → test takes everything left of the first hyphen
DOMAINLEFTHYPHEN test-123.com → TEST takes everything left of the first hyphen, capitalized
DomainleftHyphen test-123.com → Test takes everything left of the first hyphen, first char capitalized
domainrightHyphen test-123.com → 123.com takes everything right of the first hyphen
DOMAINRIGHTHYPHEN test-123.com → 123.COM takes everything right of the first hyphen, capitalized
DomainrightHyphen test-abc.com → Abc.com takes everything right of the first hyphen, first char capitalized
domainleftUnderscore test_123.com → test takes everything left of the first underscore
DOMAINLEFTUNDERSCORE test_123.com → TEST takes everything left of the first underscore, capitalized
DomainleftUnderscore test_123.com → Test takes everything left of the first underscore, first char capitalized
domainrightUnderscore test_abc.com → abc.com takes everything right of the first underscore
DOMAINRIGHTUNDERSCORE test_123.com → 123.COM takes everything right of the first underscore, capitalized
DomainrightUnderscore test_abc.com → Abc.com takes everything right of the first underscore, first char capitalized
DomainReplaceFirst_X-x EXAMPLE-attack.com → [email protected] (ex: %DomainReplaceFirst_a-@%) replaces the first occurrence of X with x in the domain (needle and replacement can be more than 1 char)
DomainReplaceFirstI_X-x EXAMPLE-attack.com → [email protected] (ex: %DomainReplaceFirstI_a-@%) case insensitively replaces the first occurrence of X with x in the domain (needle and replacement can be more than 1 char)
DomainReplaceLast_X-x EXAMPLE-attack.com → [email protected] (ex: %DomainReplaceLast_a-@%) replaces the last occurrence of X with x in the domain (needle and replacement can be more than 1 char)
DomainReplaceLastI_X-x EXAMPLE-attack.com → [email protected] (ex: %DomainReplaceLastI_a-@%) case insensitively replaces the last occurrence of X with x in the domain (needle and replacement can be more than 1 char)
DomainReplaceAll_X-x EXAMPLE-attack.com → EXAMPLE-@[email protected] (ex: %DomainReplaceAll_a-@%) replaces all occurrences of X with x in the domain (needle and replacement can be more than 1 char)
DomainReplaceAllI_X-x EXAMPLE-attack.com → EX@MPLE-@[email protected] (ex: %DomainReplaceAllI_a-@%) case insensitively replaces all occurrences of X with x in the domain (needle and replacement can be more than 1 char)
niamod test-123.com → moc.321-tset reverses the domain name
Niamod test-123.com → Moc.321-tset reverses the domain name, first char capitalized
domainleft TEST-123.com → test-123 everything left of the first dot, lowercased
DOMAINLEFT Test-123.com → TEST-123 everything left of the first dor, capitalized
Domainleft test-123.com → Test-123 everything left of the first dot, lowercased but first char capitalized
domainright TEST-123.com → com everything right of the first dot, lowercased
DOMAINRIGHT Test-123.com → COM everything right of the first dor, capitalized
Domainright test-123.com → Com everything right of the first dot, lowercased but first char capitalized
domain TEST-123.com → test-123.com domain name, lowercase
Domain TEST-123.com domain name lowercased, first char capitalized
DoMaIn test-123.com → TeSt-123.cOm domain name in alternating case, starting with uppercase
dOmAiN test-123.com → tEsT-123.CoM domain name in alternating case, starting with lowercase
DOMAIN test-123.com → TEST-123.COM domain name capitalized
NIAMOD test-123.com → MOC.321-TSET domain name reversed and capitalized