Thanks to r00tBSD, I've now got a access to the database of malware.lu. To celebrate this, I've try to reverse a dead-simple sample: the infamous BackDoor.Wirenet.1!
Preliminary analysis
$ file 9a0e765eecc5433af3dc726206ecc56e
9a0e765eecc5433af3dc726206ecc56e: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), BuildID[sha1]=0xeb836a1de23ce2cbe86a30064bc20e9f2c8b024c, stripped
Ho, shared libs, nice.
The string command give us a lot of informations :
- The binary doesn't seems to be packed!?
- AES and RC4 for crypto
- Password stealing for Mozilla Firefox/Thunderbird/Seamonkey, Pidgin, Opera and Chromium?
- Xorg stuffs, likely screenshots
- Keylogger
- A remote shell using bash is available, or sh if not.
- Autostart/persistence
- SOCKS4/5 proxy
- No IP/login/filename/... in cleartext
$ strings 9a0e765eecc5433af3dc726206ecc56e | grep cp
This command give a quick overview of wirenet's capabilities.
Functionalities overview
A quick look into the disassembly code tells us that the malware is also able to control the mouse, download/upload/execute/rename/move files, update/uninstall itself, list/kill processes, get a windows list, change windows names, ...
Analysis of interesting functions
DecryptSettings
Let's go for dead-simple crypto !
.text:08052DEC
.text:08052DEC public DecryptSettings
.text:08052DEC DecryptSettings proc near ; CODE XREF: ReadSettings+Ap
.text:08052DEC
.text:08052DEC var_114 = byte ptr -114h
.text:08052DEC
.text:08052DEC push ebx
.text:08052DED sub esp, 11Ch
.text:08052DF3 push 10h
.text:08052DF5 push offset BuilderEncryptionKey
.text:08052DFA lea ebx, [esp+128h+var_114]
.text:08052DFE push ebx
.text:08052DFF call RC4Setup
.text:08052E04 add esp, 0Ch
.text:08052E07 push 0FFh
.text:08052E0C push offset ConnectionString
.text:08052E11 push ebx
.text:08052E12 call RC4Crypt
.text:08052E17 add esp, 0Ch
.text:08052E1A push 0FFh
.text:08052E1F push offset ProxyString
.text:08052E24 push ebx
.text:08052E25 call RC4Crypt
.text:08052E2A add esp, 0Ch
.text:08052E2D push 20h
.text:08052E2F push offset Password
.text:08052E34 push ebx
[...]
.text:08052EBB call RC4Crypt
.text:08052EC0 add esp, 128h
.text:08052EC6 pop ebx
.text:08052EC7 retn
.text:08052EC7 Decry
It seems to decrypt settings strings with RC4, and the key is located at BuilderEncryptionKey.
Let's decrypt all this mess with some pythonic magic !
import sys
from Crypto.Cipher import ARC4
tab = [
{'name':'ConnectionString', 'size':256, 'offset':0xf610},
{'name':'ProxyString', 'size':256, 'offset':0xf510},
{'name':'Password', 'size':32, 'offset':0xf4ec},
{'name':'HostId', 'size':16, 'offset':0xf4c4},
{'name':'MutexName', 'size':8, 'offset':0xf4b8},
{'name':'InstallPath', 'size':128, 'offset':0xf434},
{'name':'SetupKeyName1', 'size':16, 'offset':0xf420},
{'name':'SetupKeyName2', 'size':38, 'offset':0xf3f8},
{'name':'KeyloggerFileName', 'size':128, 'offset':0xf374},
{'name':'BoolSettingByte', 'size':4, 'offset':0xf370},
{'name':'ConnectionType', 'size':4, 'offset':0xf36c},
]
if len(sys.argv) != 2:
print('Usage: %s wirenet_sample') % sys.argv[0]
sys.exit()
fd = open(sys.argv[1], 'r')
#the RC4 key is at offset 0xf4d8
fd.seek(0xf4d8)
key = fd.read(16)
for s in tab:
rc4 = ARC4.new(key)
fd.seek(s['offset'])
data = fd.read(s['size'])
decyphered = rc4.decrypt(data)
clear_text = decyphered.split('\x00')[0]
print('%s: %s') % (s['name'], clear_text)
This prints :
ConnectionString: 212.7.208.65:4141;
ProxyString: -
Password: sm0k4s523syst3m523 #AES key
HostId: LINUX
MutexName: vJEewiWD
InstallPath: %home%/WIFIADAPT
SetupKeyName1: WIFIADAPTER #Key used for autostart
SetupKeyName2: -
KeyloggerFileName: %Home%\.m8d.dat
BoolSettingByte: 237I
ConnectionType: 001
InstallHost
First, a mutex named vJEewiWD is created, and depending of options, multiples things can happens:
- The malware can be installed in "InstallPath", which is
%home%/WIFIADAPT - The malware can be automatically started on login, by creating a file named
%HOME%/.config/autostart/WIFIADAPTER.desktop. - The malware can be also be automatically started by a line into the
%HOME%/.xinitrc. - The malware can be run as a daemon, by forking itself
- The keylogger may be immediately started in a separated thread.
Password stealer
Nothing hardcore, nor noticeably cleaver here. I don't get why the author didn't use the sqlite library instead of hackish crap.
GetMozillaProductPasswords
This function recovers logins/passwords from Thunderbird, Firefox and Seamonkey. The procedure is common for the 3 products:
- Get profile path by looking into
%HOME%/[.mozilla[/firefox|/seamonkey]|.thunderbird]/profiles.inifor the Path variable - Open the
signons.sqlitewhich contains logins/passwords - Execute
"select * from moz_logins"to get every passwords using Mozilla's own functions:- PK11_GetInternalKeySlot
- PK11_Authenticate
- NSSBase64_DecodeBuffer
- PK11SDR_Decrypt
- PK11_FreeSlot
- NSS_Shutdown<
GetGoogleChromePasswords
Data are either in %HOME%/.config/google-chrome/Default/Login Data for Google Chrome, or in %HOME%/.config/chromium/Default/Login Data for Chromium.
This time, no sql request : the sqlite table is decoded as it.
GetOperaWand
This time, the whole file (%HOME%/.opera/wand.dat) containing logins/passwords is sent to the C&C, not a dumped list.
Maybe the author was to lazy to write a decrypted?
GetPidginPasswords
Some XML parsing of %HOME%/.purple/accounts.xml.
Keylogging
On GNU/Linux, deploying a keylogger doesn't require a root account. The routine used in this malware is quite common:
XOpenDisplay()to get the DisplayXQueryExtension([..]XInputExtension[..])to check if the XInputExtension is presentXListInputDevices()to get input devices, looking for a "AT" or a "System Keyboard" one.XOpenDevice()to handle the deviceXSelectExtensionEvent()to get interesting events- An infinite loop calling
XNextEvent()to get events. If an event is found, it's logged into%HOME%\.m8d.datusing the (ugly)LogKey(XKeyEvent *, Display *)function.
CaptureScreen
This function does the following:
- Get an XImage using XGetGeometry then XGetImage.
- Convert the XImage to BMP.
- Convert the BMP to JPEG.
Misc. functions
- cpGetComputerName: using gethostname()
- cpGetOSVersion: looking into /etc/lsb-release
- cpGetUsername: using getenv("USER")
- ChangeWindowTitle: using XChangeProperty()
Communication with the C&C
Nothing funky here. The C&C's IP is 212.7.208.65:4141, everything is transmitted using AES, with "sm0k4s523syst3m523" as key. The authentication packet is "RGI28DQ30QB8Q1F7" (in clear in the binary).
A whois tells us that this is a Dediserv server (PL) routed via LeaseWeb (NL). Let's see what nmap's got for us:
# nmap [..] 212.7.208.65
22/tcp filtered ssh
80/tcp filtered http
111/tcp open rpcbind
| rpcinfo:
| 100000 2,3,4 111/udp rpcbind
| 100024 1 41019/udp status
| 100000 2,3,4 111/tcp rpcbind
|_ 100024 1 28564/tcp status
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
No exact OS matches for host
Conclusion
Nothing groundbreaking, but since this was my first malware reversing under GNU/Linux, I had a lot of fun.