# Deep Analysis of QBot Banking Trojan **[n1ght-w0lf.github.io/malware analysis/qbot-banking-trojan/](https://n1ght-w0lf.github.io/malware%20analysis/qbot-banking-trojan/)** ### Abdallah Elshinbary Malware Analysis & Reverse Engineering Adventures 11 minute read July 15, 2020 ----- 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: First, the victim receives a phishing email with a link to a malicious zip file. The zip file contains a very obfuscated VBS file which downloads and launches Qbot executable. ----- 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` ----- ## 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. 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. ----- 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). 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. ----- 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. ## Back To Parent After the Anti-Analysis checks, QBot drops a copy of itself along with a configuration file at ``` "%APPDATA%\Microsoft\" . ``` Finally, QBot starts the dropped copy in a new process and overwrites itself with a legitimate executable, here it’s `"calc.exe" .` ## 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. ## 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). From now on, we will be analyzing the core DLL of QBot. The core module has 2 resources both RC4 encrypted. The first resource gets loaded into memory then RC4 decrypted. ----- 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" .` 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: ----- ## 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. ----- We can use `Fiddler to intercept and decrypt the HTTPS traffic.` The RC4 key for encrypting the payload is the SHA1 hash of the first 16 bytes of the Base64decoded 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). ----- It’s worth mentioning that dynamic imports of the core DLL are stored in the same format as commands `"", 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. ``` 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. 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" . ``` ----- 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=[] ext_ip=[] dnsname=[?] hostname= [] user=[] domain=[] is_admin=[] os=[] qbot_version=[] install_time=[] exe= [] prod_id=[NULL] iface_n=[/] UP] soft=[||...] ``` Example of collected data: 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. ``` ----- 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.` ## 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. It creates a new suspended process then writes the plugin to the process memory using ``` WriteProcessMemory() and then resumes the injected process. ``` ----- 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.youtube.com/watch?v=iB1psRMtlqg) ----- https://www.virusbulletin.com/virusbulletin/2017/06/vb2016-paper-diving-pinkslipbots-latestcampaign [https://www.fortinet.com/blog/threat-research/deep-analysis-qbot-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.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-naughtylibraries/ [https://www.cyberbit.com/blog/endpoint-security/anti-vm-and-anti-sandbox-explained/](https://www.cyberbit.com/blog/endpoint-security/anti-vm-and-anti-sandbox-explained/) -----