Deep Analysis of QBot Banking Trojan

11 minute read

QBot is a modular information stealer also known as Qakbot or Pinkslipbot. It has been active for years since 2007. It has historically been known as a banking Trojan, meaning that it steals financial data from infected systems.

Infection Flow

QBot can be delivered in various different ways including Malspam (Malicious Spam) or dropped by other malware families like Emotet.

The infection flow for this campaign is as follows:

1

First, the victim receives a phishing email with a link to a malicious zip file.

2

The zip file contains a very obfuscated VBS file which downloads and launches Qbot executable.

3

The VBS file tries to download Qbot from different places:

  • http://st29[.]ru/tbzirttmcnmb/88888888.png
  • http://restaurantbrighton[.]ru/uyqcb/88888888.png
  • http://royalapartments[.]pl/vtjwwoqxaix/88888888.png
  • http://alergeny.dietapacjenta[.]pl/pgaakzs/88888888.png
  • http://egyorg[.]com/vxvipjfembb/88888888.png

Notice the misleading URL, it looks like it’s downloading a PNG image but the raw data says something else.

Unpacking

QBot is packed with a custom packer, but the unpacking process is really simple. It allocates memory for the unpacked code using VirtualAlloc() and changes memory protection using VirtualProtect(). So we just need 2 breakpoints at VirtualAlloc() and VirtualProtect().

4

Encrypted Strings

Most of QBot strings are encrypted (stored in a continuous blob) and they are decrypted on demand. The decryption routine accepts one argument which is the index to the string then it XORs it with a hardcoded bytes array until it encounters a null byte.

We can use IDAPython to decrypt the strings and add them as comments.

import idc
import idautils

dec_routine = 0x4065B7
enc_strings = 0x40B930
bytes_arr = 0x410120

def decrypt_string(idx):
    if idx >= 0x36F4:
        return    # out of bounds
    res = ""
    while True:
        c = idc.get_wide_byte(enc_strings+idx) ^ idc.get_wide_byte(bytes_arr + (idx&0x3F))
        if c == 0: break
        res += chr(c)
        idx += 1
    return res

xrefs = idautils.CodeRefsTo(dec_routine, 0)
for x in xrefs:
    ea = idc.prev_head(x)
    t = idc.get_operand_type(ea, 1)
    if t == idc.o_imm:
        idx = idc.get_operand_value(ea, 1)
        dec = decrypt_string(idx)
        idc.set_cmt(ea, dec, 1)

And here is the result, that’s much easier to work with.

5

This should take care of most of the strings, the rest of strings indexes are calculated dynamically at runtime.

We decrypt all strings by looping through the encrypted blob and decrypt strings one by one.

idx = 0
while idx < 0x36F4:
    dec = decrypt_string(idx)
    idx += len(dec)+1
    print(dec)

Anti-Analysis

QBot spawns a new process of itself with the "/C" parameter, this process is responsible for doing Anti-Analysis checks.

6

The parent process checks the exit code of this spawned process. If the exit code is not 0, it means that QBot is being analyzed (and so it exits).

7

So let’s go over the anti-analysis techniques.

Checking VM

In VMWare, communication with the host is done through a specific I/O port (0x5658), so QBot uses the in assembly instruction to detect VMWare by reading from this port and checking the return value in ebx if it’s equal to VMXh (VMware magic value).

If we are outside VMWare, a privilege error occurs and this code will return 0.

8

Another Anti-VM trick is to check hardware devices against known devices names used by VMs and Sandboxes.

Here is the list of devices names.

Expand to see more
  VMware Pointing
  VMware Accelerated
  VMware SCSI
  VMware SVGA
  VMware Replay
  VMware server memory
  CWSandbox
  Virtual HD
  QEMU
  Red Hat VirtIO
  srootkit
  VMware VMaudio
  VMware Vista
  VBoxVideo
  VBoxGuest
  vmxnet
  vmscsi
  VMAUDIO
  vmdebug
  vm3dmp
  vmrawdsk
  vmx_svga
  ansfltr
  sbtisht

Checking Processes

QBot loops through running processes and compares their executable names against known analysis tools.

Expand to see more
  Fiddler.exe
  samp1e.exe
  sample.exe
  runsample.exe
  lordpe.exe
  regshot.exe
  Autoruns.exe
  dsniff.exe
  VBoxTray.exe
  HashMyFiles.exe
  ProcessHacker.exe
  Procmon.exe
  Procmon64.exe
  netmon.exe
  vmtoolsd.exe
  vm3dservice.exe
  VGAuthService.exe
  pr0c3xp.exe
  CFF Explorer.exe
  dumpcap.exe
  Wireshark.exe
  idaq.exe
  idaq64.exe
  TPAutoConnect.exe
  ResourceHacker.exe
  vmacthlp.exe
  OLLYDBG.EXE
  windbg.exe
  bds-vision-agent-nai.exe
  bds-vision-apis.exe
  bds-vision-agent-app.exe
  MultiAnalysis_v1.0.294.exe
  x32dbg.exe
  VBoxService.exe
  Tcpview.exe

Checking DLLs

Sandbox detection can be done by enumerating loaded DLLs and comparing them against known DLLs used by sandboxes. Here it’s just using 2 of them.

ivm-inject.dll     # Buster Sandbox Analyzer
SbieDll.dll        # SandBoxie

Checking Filename

Some sandboxes may change the sample file name. So QBot checks if its process name contains one of these strings.

sample
mlwr_smpl
artifact.exe

Checking CPU

The last check is done using CPUID instruction. First it is executed with EAX=0 to get the CPU vendor and compares it with GenuineIntel (Intel processor).

Then it is executed with EAX=1 to get the processors features.

On a physical machine the last bit will be equal to 0. On a guest VM it will equal to 1.

9

Back To Parent

After the Anti-Analysis checks, QBot drops a copy of itself along with a configuration file at "%APPDATA%\Microsoft\<random_folder_name>".

10

Finally, QBot starts the dropped copy in a new process and overwrites itself with a legitimate executable, here it’s "calc.exe".

11

Configuration File

The dropped configuration file is accessed frequently by Qbot, this file is RC4 encrypted. By setting a breakpoint before the contents of the file gets encrypted I got the following data:

Field Description
10=spx143 Campaign ID
11=2 Number of hardcoded C2
1=13.59.00-24/06/2020 Date of Qbot install in HH:MM:ss-dd/mm/yyyy
2=1592996340 Victim Qbot install
50=1 N/A
5=VgBCAE8AWABTAFYAUgA7ADIA Victim network shares
38=1593047244 Last victim call to C2 (Unix time)
45=187.163.101.137 C2 IP
46=995 C2 port
39=45.242.76.104 Victim external IP
43=1593006172 Time of record (Unix time)
49=1 N/A

Persistence

QBot achieves persistence by creating a new registry value under the key "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run". It also registers a scheduled task that runs every 5 hours.

12

Process Injection

QBot tries to inject its unpacked code in one of these processes ("explorer.exe", "mobsync.exe", "iexplorer.exe") and it uses Process Hollowing technique to achieve that.

It first starts a new suspended process with CreateProcessW() then it writes the injected code into the target process using ZwCreateSection(), ZwMapViewOfSection() and ZwWriteVirtualMemory().

Finally it sets the thread context to jump to the injected code and resume execution with ResumeThread().

Core Module

The injected code loads and decrypts one of its resources "307" . After dumping it, I found out that it’s a DLL (this is the core module).

13

From now on, we will be analyzing the core DLL of QBot.

The core module has 2 resources both RC4 encrypted.

14

The first resource gets loaded into memory then RC4 decrypted.

15

The contents of the decrypted resource are:

  • 10=spx143 (Campaign ID)

  • 3=1592482956 (Timestamp)

After some digging, I found out how the resources are decrypted. The first 20 bytes of each resource are the RC4 key of this resource, and the rest are the actual encrypted data.

So by using this find, we can decrypt the other resource "311".

16

Great!!! Now we have the list of C2 servers (150 servers!).

The reason there is many controllers is that these are actually just proxies of infected bots acting as intermediate nodes between the victim and the real C2 and thus hiding the backend infrastructure of the attacker.

So it works like this:

17

C2 Communication

QBot obfuscates its communication with the C2 server by encrypting the payloads using RC4 and encoding the result using Base64.

The communication is also done over SSL, you can notice that the traffic has unusual certificate issuer data.

18

We can use Fiddler to intercept and decrypt the HTTPS traffic.

19

The RC4 key for encrypting the payload is the SHA1 hash of the first 16 bytes of the Base64-decoded payload + a hardcoded salt (The salt is stored as an encrypted string).

Here is an implementation of the decryption algorithm:

HARDCODED_SALT = b"jHxastDcds)oMc=jvh7wdUhxcsdt2"    # decrypted string

def decrypt_payload(encrypted_blob):
    b64_decoded = base64.b64decode(encrypted_blob)
    decryption_key = b64_decoded[:0x10] + HARDCODED_SALT
    sha1hash = hashlib.sha1()
    sha1hash.update(decryption_key)
    decryption_key_hash = sha1hash.digest()
    rc4 = ARC4(decryption_key_hash)
    return rc4.decrypt(b64_decoded[0x10:])

The decrypted payload is in JSON form.

  • Decrypted C2 Request: {“8”:9,”1”:17,”2”:”pnmfcq111232”}
  • Decrypted C2 Response: {“8”:5,”16”:770897804,”39”:”V4UnoDQSEblewhh63UfUqAns”,”38”:1}

Commands List

After establishing communication, the C2 server will send commands indexes to be executed.

Here is the list of commands and their corresponding indexes (I have renamed the important commands).

20

It’s worth mentioning that dynamic imports of the core DLL are stored in the same format as commands "<address, API_index, DLL_index>", the API and DLL indexes are passed to the string decryption routine which returns their corresponding names then it uses LoadLibrary and GetProcAddress to resolve the imports.

21

Let’s go through some of the interesting commands.

Command 13: Lateral Movement

QBot can spread through the network by enumerating network shares using WNetOpenEnumW() and WNetEnumResourceW() then it drops a copy of Qbot into the shared folders.

Then the dropped executable is registered as an auto-start service on the target machine. The names for the service and the dropped file are randomly generated strings.

22

Finally, Qbot deletes the created service and dropped file from the target machine (as it’s successfully infected).

Command 21: Collecting Installed Applications

QBot can collect installed applications by enumeration subkeys of the registry key "HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall".

23

The collected data is appended to the end of a string containing additional information about the victim’s machine and time of collection.

t=i1 time=[<time_of_collect>] ext_ip=[<external_IP>] dnsname=[?] hostname=[<computer_name>] user=[] domain=[] is_admin=[<YES/NO>] os=[<windows_ver>] qbot_version=[<qbot_ver>] install_time=[<qbot_install_time>] exe=[<injected_process>] prod_id=[NULL] iface_n=[<interface_IP>/<interface_IP>] UP] soft=[<app1;ver>|<app2;ver>|...]

Example of collected data:

24

Then the data is RC4 encrypted and written to "wdqlxw32.dll" at the same directory of QBot.

Finally, "wdqlxw32.dll" is Zlib compressed and RC4 encrypted again then it’s saved to "cwdqlxw32.dll" and the original "wdqlxw32.dll" is deleted.

25

The compressed file is then transfered to the C2 server (RC4 encrypted and Base64 encoded) in the key "36" and the compressed file "cwdqlxw32.dll" is also deleted.

26

Command 31: Fetching Plugins

As we said before, QBot is known to be a modular malware. It can load additional plugins received from the C2 server (plugins are RC4 encrypted and Base64 encoded).

QBot tries to inject the received plugin in 3 different processes depending on the machine architecture.

27

It creates a new suspended process then writes the plugin to the process memory using WriteProcessMemory() and then resumes the injected process.

28

At the time of writing this, Qbot has 3 different plugins (“Password grabber”, “Cookie grabber”, “UPnP module”).

Conclusion

QBot is considered to be a sophisticated malware, it’s receiving regular updates from time to time and it’s not likely to go away anytime soon.

There is still more features that I didn’t cover such as WebInjects so maybe I will come back to Qbot later I guess :)

IOCs

Hashes

VBS File: b734caf792c968ca1870c3ec7dda68ad5dc47fef548751afb8509752c185a756

QBot: 112a64190b9a0f356880eebf05e195f4c16407032bf89fa843fd136da6f5d515

URLs

http://st29[.]ru/tbzirttmcnmb/88888888.png

http://restaurantbrighton[.]ru/uyqcb/88888888.png

http://royalapartments[.]pl/vtjwwoqxaix/88888888.png

http://alergeny.dietapacjenta[.]pl/pgaakzs/88888888.png

http://egyorg[.]com/vxvipjfembb/88888888.png

C2 Domains

39.36.254.179:995

24.139.132.70:443

24.202.42.48:2222

72.204.242.138:443

172.242.156.50:995

72.204.242.138:20

68.174.15.223:443

74.193.197.246:443

96.56.237.174:990

64.19.74.29:995

70.168.130.172:443

189.236.166.167:443

68.4.137.211:443

76.187.8.160:443

76.86.57.179:2222

73.226.220.56:443

67.250.184.157:443

75.183.171.155:3389

173.172.205.216:443

173.3.132.17:995

172.78.30.215:443

207.255.161.8:32103

75.137.239.211:443

68.49.120.179:443

206.51.202.106:50003

82.127.193.151:2222

207.255.161.8:2222

207.255.161.8:2087

24.152.219.253:995

187.19.151.218:995

197.37.48.37:993

188.241.243.175:443

72.88.119.131:443

89.137.211.239:443

108.30.125.94:443

187.163.101.137:995

100.19.7.242:443

45.77.164.175:443

80.240.26.178:443

66.208.105.6:443

207.246.75.201:443

199.247.22.145:443

199.247.16.80:443

95.77.223.148:443

68.60.221.169:465

5.107.220.84:2222

41.228.212.22:443

86.233.4.153:2222

68.200.23.189:443

201.146.127.158:443

79.114.199.39:443

87.65.204.240:995

71.74.12.34:443

217.162.149.212:443

195.162.106.93:2222

75.165.112.82:50002

201.248.102.4:2078

96.41.93.96:443

89.247.216.127:443

84.232.238.30:443

103.238.231.40:443

174.34.67.106:2222

98.115.138.61:443

91.125.21.16:2222

84.247.55.190:443

193.248.44.2:2222

74.135.37.79:443

78.96.190.54:443

86.126.97.183:2222

2.50.47.97:2222

68.39.160.40:443

96.232.203.15:443

86.144.150.29:2222

71.220.191.200:443

24.231.54.185:2222

80.14.209.42:2222

24.164.79.147:443

70.183.127.6:995

47.153.115.154:993

184.180.157.203:2222

50.104.68.223:443

67.165.206.193:995

200.113.201.83:993

47.153.115.154:465

24.42.14.241:995

189.160.203.110:443

188.27.76.139:443

207.255.161.8:32102

49.207.105.25:443

71.210.177.4:443

117.242.253.163:443

50.244.112.106:443

69.92.54.95:995

41.34.91.90:995

72.204.242.138:53

41.97.138.74:443

72.29.181.77:2078

71.88.168.176:443

2.50.171.142:443

67.83.54.76:2222

86.125.145.90:2222

47.153.115.154:995

24.122.157.93:443

47.146.169.85:443

72.181.9.163:443

187.155.74.5:443

71.209.187.4:443

74.75.216.202:443

24.44.180.236:2222

24.43.22.220:993

108.188.116.179:443

100.4.173.223:443

76.170.77.99:443

70.95.118.217:443

134.0.196.46:995

68.225.56.31:443

72.204.242.138:32102

72.204.242.138:50001

108.190.151.108:2222

72.204.242.138:465

50.244.112.10:443

173.22.120.11:2222

24.43.22.220:995

24.43.22.220:443

92.17.167.87:2222

72.209.191.27:443

72.204.242.138:80

72.204.242.138:443

71.187.170.235:443

96.56.237.174:32103

71.187.7.239:443

184.98.104.7:995

70.124.29.226:443

137.99.224.198:443

73.23.194.75:443

151.205.102.42:443

64.224.76.152:443

72.204.242.138:32100

173.187.101.221:443

72.179.13.59:443

208.93.202.49:443

70.174.3.241:443

96.37.137.42:443

76.111.128.194:443

67.209.195.198:3389

61.3.184.27:443

24.42.14.241:443

74.56.167.31:443

5.193.61.212:2222

117.216.177.171:443

References

Demystifying QBot Banking Trojan - BSides Belfast

https://www.virusbulletin.com/virusbulletin/2017/06/vb2016-paper-diving-pinkslipbots-latest-campaign

https://www.fortinet.com/blog/threat-research/deep-analysis-qbot-campaign

https://www.vkremez.com/2018/07/lets-learn-in-depth-reversing-of-qakbot.html

https://www.hexacorn.com/blog/2016/07/01/enter-sandbox-part-12-the-library-of-naughty-libraries/

https://www.cyberbit.com/blog/endpoint-security/anti-vm-and-anti-sandbox-explained/