CASE 1 : Ifwatch Malware Part 2

This is the second part of my findings around the ifwatch malware spotted on routers.
In this post I will reveal the embedded http server and how it enabled me to collect all the samples of all architectures.

In part 1 I’ve showed how to unpack the source and a mysterious file called !whisper. By analyzing the source code I’ve discovered that every bot includes a fileserver exposed via http and tftp.

ifwatch source findfile

There are 2 download types :
Type1 : The pl file which is platform independent compressed perl code.
Type2 : The platform specific binaries.

ifwatch httpd

The url builds as follow  host:port/bn/$type/$id

For type 1 the id is actually ignored but for type 2 the parameter get packed to 64bytes Hash.
From other pieces of code I could deduce that this was actually the SHA256 hash of the file.ifwatch source register

ifwatch source findfile
Looking even deeper in the code I found references to the !whisper file and a minute later that file decoded to 40 hashes :

armeb-sf/bn > fe42c8e22025654c81e10f7a93f82c1ef5d2c40c30604ee0ffcec2102fd99604
armeb-sf/dl > 161250dc959e7e520118155286314cb56025f7c5ca3976712ecb5090dcb7746d
armeb-sf/rf > f8a3dc935ecb9aadff73236da954ad85474ebd68f99ba3cc085f580f52706394
armeb-sf/tn > 87f0bf620835c18ecfc330616192e484e182632a727b1bdb85dce453f942b321
armgeb-sf/bn > 52eb7b862f6558288854710b5d7f61e9275c1068ac3aaa77ac8ffdb962983fe5
armgeb-sf/dl > ef1c47602c1f7fcc8c790debf77e7ab1be9cbb0d0bec655308faebd6a8a8a3d0
armgeb-sf/rf > 04a6a13ff7da430a58bf0c53d525404d44220749913c0188dc68d996764e574e
armgeb-sf/tn > 68903a2c82c07b0b3c72500adee34e034fdac7c5c388a617fc08c0d79af13bda
armgel-sf/bn > 500b017c585181f230c1b9b2b9a03548551a28410c5f0dd640d90abe0abe6454
armgel-sf/dl > 94cedd5dfe733422de2ee23c78821eab6938a1f3ccd876dbeec5659e38e909ff
armgel-sf/rf > 1f465a9b36991ceee39840f23b2397752a04386c3a96cb48bb74c7d15a96ac57
armgel-sf/tn > 88b0f1cc969703b068b9c771602744fc9661254ae8bbda91098a1d3325228454
arm-sf/bn > 97960d3dea228c875cd1bd965450279a2c019f99e8a3a4fa5fba9cf17eecc401
arm-sf/dl > df13514f1f94a30d83b103ed92130c66159d417ce71fc2367cfc550fc9eb10f3
arm-sf/rf > 4d5af001c48c1dd37d932003a6780ccb28e1537b9dee197a694e8d437d16ee9b
arm-sf/tn > b70203bc092be0d8e7b8c1e9f3a2424ae22857fc25550af217e3fad19b7bd913
mips1-sf/bn > 7b0e85574671301deee08db03a8e6670268c0df66ff874f1e67f356b9a8fd10f
mips1-sf/dl > 0fc73fb359675fbebf7102bc369e2f1455e94ae23c16a0e330a3a4f7fe7956a7
mips1-sf/rf > 6e237d4ff8ba216f433adc0040701c161a8ee99e3f9df1a411fa6f6aa6d09621
mips1-sf/tn > 8223cc8e0ed3ac448fc020dbc8f81d9bdd6bc930e41bca77315f08f5cd8f9876
mipsel1-sf/bn > 677e0ff1579781e471f6182518de39224a31002b3491c23236044bd29bbaa8d8
mipsel1-sf/dl > 5a674ac420193c225a4d041f2d38ef36ba010bec7428450dffc2a6c09415eb8f
mipsel1-sf/rf > 267be689437edf064dc4219f00b8fed6300d79c99e4ff6a2726416429cf9ec4c
mipsel1-sf/tn > d1685a38f747f8e192d16c1059de0c4a7c114a25754649970ff907a653d79f27
ppc-sf/bn > c377f1eddfdbbfae880c5fc11b444d21d3061923a93767d922e66b9f72344a8e
ppc-sf/dl > affc527803844bf5d7c0a7a5b4e13df30d9c65bc635c7a3e06ac9146e566879a
ppc-sf/rf > 513527ad13f47d2f7a188d3688d788e0ba4370b3d602501940516248d5847f33
ppc-sf/tn > a08ef2aabde32abb9ba23b4339d843a973a6804a41cd22d66c1de449d0e0c0c0
sh4b-sf/bn > 4ad5157c950c0ad261d562eeb2ee9341c07bc3dcae834e9967a5d36b21c00e5b
sh4b-sf/dl > 48e1fc4b2a12aaab1163a2e4c0ef1e841f113596cf9bb86bca3d282ff466ebb1
sh4b-sf/rf > f8176cc1a800d17acfeecd13a7322eb38a50ef94bb7418f841c58e9160196448
sh4b-sf/tn > 7d7f5048346b904664e6b25caf55d35966ec1a4d158dd1425ca2b2dceb6f9c6e
sh4l-sf/bn > f3e76b7489a189b74e36cb37acd13f374fa00d7ebded06b447709de02cee1954
sh4l-sf/dl > f432f68320ccb101efc55ec2d51d090a71b56fceeb85ec3d95350d282d0833cc
sh4l-sf/rf > 5b8d6057aecd9815b2d88330f5b1c72af5530cd744756cf3ebec91467156574d
sh4l-sf/tn > 3a8496923be89b5cd5f6b0eca58205b3cb0855c243d2175ca26770a4b1873ff3
x86/bn > 28af19831c28ec5e58d8dc8cbc1dedc8a9b47fc93781ea65e983fb11749df244
x86/dl > cefc86696e6852b9800450cdf0c0a49bf2bd6b74725cfa9289087ac9c399befe
x86/rf > 50578cc804a3ba11f5fbfbbed9bb1403ecf1470fc011d0fde1f423f6c1d588cb
x86/tn > 73d4d001273425840cbb3110050f61b4aaeb542bc979abf71036eae01304ded

Using these hashes it was possible to download all the samples from different bots in the network.
Some of the bots even host all the executables, i suspect these machines not to be routers because of the disk space this requires.

ifwatch samples

These samples were uploaded to VT with a detection rate of 0. Another copy of these went to @MalwareMustDie .
The above hashes were shared a week ago on Pastebin and Twitter. I think owners of this malware are following my account as the pastbin went removed couple of minutes later.

But it’s too late boys, I’ve broken most of your kit, and you’re now doomed to start all over again…
Your configs are decoded, your sources are clear, and by the way… you have stupid coding bugs ! ifwatch VirusTotal

CASE 1 : ifwatch malware Part 1

During some exploration of my own home router, I discovered some processes for which I couldn’t locate the executable, at least not at the place it was mentioned it should be.

The process informations were pointing to /sbin/ifwatch-if but actually loaded executable sat in /dev/netslink/ .

ifwatch processes

That path was also present in my mount info, what was another proof of compromise to me.

ifwatch files

I’ve gathered a copy of these and passed them through VT with detection rate of 0.

Qemu and strace quickly stopped working, because some nasty debugger hooks that are used to avoid me going into that path.


These were the indicators of my first ‘Real World’ case : Undetected, maybe unknown family of malware, quite complex and using advanced techniques to avoid disassembly and emulation.

So I started digging out the 4 executables and 2 data files were in that folder.

.net_bn , .net_tn, .net_rf, .net_dl were statically compiled arm binaries.

The elf headers were stripped and section headers absent so objdump couldn’t help me getting the disassembly.

ifwatch elf headers

Strings were almost absent in most of the executables, except for the .net_bn file, that was actually to huge to analyze completely for now.
However some strings caught my attention.

ifwatch bn strings ifwatch bn strings2

The interesting part comes from the path and indicates the usage of staticperl : http://search.cpan.org/~mlehmann/App-Staticperl-1.43/bin/staticperl

Other strings captured from the other executables :

Yee1Ahwv Ciecoqu7 ZohHoo5i ZieH8yie Zip0miib

Some are used in the communication protocols that I will cover later.

That made the case actually very interesting and different from the easy strings & objdump cases that are solved in 5 minutes.
I’ve searched around for these limited amount of strings and had no results on VT , Google, KernelMode.


Before digging deeper into the executables I took a look at the 2 data files present.

One of those contained little strings but was mainly scrambled data. The other file was containing lot of strings, but these were apparently all mixed and scrambled too.

Somewhere in the middle of all these strings were some names of libraries CBOR::XS, Compress::LZF and by greping those out of the strings it was possible to rebuild an unpacking algorithm to decompress and extract the .net_pl file into multiple source files.

Perl Packages containing the core of the botnet.

ifwatch pl files

And another folder with perl scripts that helped me a lot with the rest. Thank you guys !

ifwatch pl files2

The source was minified and not clearly readable but perltidy did a great job restoring this to a readable format.
A couple of minutes later I had clear readable source of the botnet and another new scrambled mysterious file called !whisper.

From the code it was possible to understand what the .net_cf file was used for.

I still remember that beautiful moment where that list of scrambled code appeared as an array of values. port, id, secret :-), speedtest, time on/offline, ipchange count, start count, etc.

A lot of nice info but what I was looking for are ip addresses or domain names and till now none of these showed up.

Part of the config contained an array with scrambled entries, I tried a couple of conversion but none of them did it, so I went back to the source code and looked for places accessing that particular part of the config and there it was, the conversion method to go from scrambled data to ip :port.

I said that there was one of these arrays but actually there are 2 one called ‘seeds’ and another one ‘hpv’. That hpv was also to find in the source file names and will be subject to later analysis

ifwatch ips

So now I have scripts to unpack the net_pl file, read the settings of net_cf file and extract ip:port of the seeds and clients. That’s cool. But not enough to take it down so lets continue.

A couple of hours later I took a fresh copy of this file and I had totally different ips showing up. I can admit some ip rotation but not that much, so I decided to restart the router over and over and collect these files.

This gave me 100+ seeds and some 3000+ clients. The strangest part is that there was not a lot of duplication in the files, and that made me think that this is actually much bigger than it looked like in the first place.

Next step is to dig deeper in the code, analyze the communication protocol and decode the !whisper file.