How works

1 comment

Please note the implementation below does not work anymore in most cases. Check out the closing note for more info.
Recently there has been quite some attention around, and the security of the WhatsApp application.
Today released a small windows application to change WhatsApp statuses from your desktop.
As I’m not a big fan of the Windows OS (for me), I was curious to take a further look on how the application works – and wanted to find out what the buzz (and assumed insecurity of WhatsApp) was about. This post explains what I did, and what the results are. I’ll leave interpretations and conclusions around WhatsApp security up to the reader, but do feel it’s quite obvious :)

The core of the WhatsApp status changer is a small routine, making an HTTP request to the WhatsApp server, pretending it’s an iPhone client:

private void btnChangeStatus_Click(object sender, EventArgs e)
HttpWebRequest request = (HttpWebRequest) WebRequest.Create("");
request.KeepAlive = false;
request.ProtocolVersion = HttpVersion.Version10;
request.Method = "POST";
request.UserAgent = "User-Agent:WhatsApp/2.6.7 iPhone_OS/5.0.1 Device/Unknown_(iPhone4,1)";
byte[] bytes = Encoding.ASCII.GetBytes("cc=31&me=" + 
               Uri.EscapeDataString(this.txtPhoneNumber.Text) + 
               "&s=" + Uri.EscapeDataString(this.txtNewStatus.Text));
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = bytes.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(bytes, 0, bytes.Length);
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
MessageBox.Show("Status has been changed to: " + this.txtNewStatus.Text);

Breaking down the code is pretty easy, even with my limited knowledge of .NET code. The call is constructed as a plain HTTP GET request to with three parameters:

  • cc=31
    I’m assuming ‘cc’ stands for ‘country code’, in the application hardcoded to 31, the country code of the Netherlands.

  • me=<phonenumber>
    This is the (encoded) phone number of the recipient (i.e. your number, as you obviously wouldn’t want to change someone else’s status :) )

  • s=<status>
    This is the (encoded) status you want to set.

All parameters should be URL-escaped, to allow the call to go trough.

Obvously this is trivial to replicate. The WhatsApp status changer is not that well-written, and doesn’t contain error handling. This is trivial to add, but the application makes the point.
There is no security whatsoever in the way WhatsApp deals with these calls. I would at least expect some kind of authentication based on a device ID or some other shared secret. This would be my first advice to WhatsApp.

Looking at this, I wonder what the rest of the security is like – but personally I’m less enthousiastic for using WhatsApp for privacy-sensitive communication – and I certainly will trust WhatsApp much less.

If anyone feels like implementing above in some kind of more *nix-friendly library or app, I’d be happy to hear about it. Enjoy, and please use this information responsibly.

Update: Thanks to BlueSky – A quick and easy way to do this from a *nix command shell, assuming you have curl installed:

curl –data-urlencode cc=”31″ –data-urlencode me=”+thephonenumber” –data-urlencode s=”yourstatus” \
     -A “WhatsApp/2.6.7 iPhone_OS/5.0.1 Device/Unknown_(iPhone4,1)-H “Accept: */*” \
     -H “Accept-Language: en-us” -H “Accept-Encoding: gzip, deflate” \
     -L -k -v

Update #2: BlueSky wrote an implementation in PHP – and placed it on pastebin.

Update #3: WhatsApp implemented an IP check – so this doesn’t work as well as it used to. The check entails checking if the update-request is for a WhatsApp account currently signed in, and checks if it is coming from the same IP as the target client is using. This means above could should still work for target WhatsApp users behind the same NAT (for example).

Restricting access to IPMI modules on public IP’s

Comments Off

IPMI modules are a great way to get full control over a remote server, without buying extra remote console / remote power hardware. You can take over the entire server, do KVM or serial over IP, and it’s even possible to mount ISO’s on a local workstation, and boot the server off it.
The problem is, IPMI modules should only be accessible from a management network that is not publicly reachable.

Recently I wanted to place my server in a (shared) co-location facility, unable to use a private network for the IPMI module in my supermicro server. The original plan was to use a local iptables firewall on the IPMI module itself, as it runs an ARM linux implementation. Unfortunately the iptables install included with the firmware is severely broken, and can’t be used:

# iptables -I INPUT -p tcp -s x.x.x.x -j ACCEPT
iptables v1.2.11: Couldn't load target `standard':/lib/iptables/ cannot open shared object file: No such file or directory

Try `iptables -h' or 'iptables --help' for more information.

Crosscompiling iptables is an option, but would also mean extensive modification of the flash image, as there is no (easy) way to transfer files to the BMC, and flash storage space is limited.

The customized linux install also doesn’t allow for safe shutdown of unneeded services, and this would still expose the module to the internet. This post at serverfault shows that I’m not the only one looking for a solution for this.

In the end, a friend (thanks mtp!) came up with a simple solution to at least limit access to the public IPMI IP – simply removing the default gateway, and setting routes to some static IP’s that are allowed to connect. In combination with a small enough netmask, this is secure enough for my purposes.

Comments Off

dm-crypt benchmarks

Comments Off

I finally got around to re-installing my new server. This machine will be my secondary co-located box, serving as secondary DNS, MX, and as a machine to experiment with. As I don’t want to risk downtime on more important services, I’ll be running KVM virtual machines, so I won’t make a mess of the services I want to keep running at all time.

The new box is quite fast, running on a core i7 CPU, with 12Gb of RAM and 4 1.5Tb disks in raid10,f2 – so i thought it would be nice to spend some of that power on encrypting the volume groups my virtual machines will be running on.  Before moving it to the datacenter, I’m playing around with it, and ran some benchmarks on dm-crypt, to get a feeling about the performance loss. Oh, and I’m running Ubuntu Jaunty.

First I ran a benchmark on a 500Gb encrypted volume. The disks have been configured in linux software raid10,f2, using a 256Kb chunk size. I used cryptsetup on the md device, to create an encrypted dm-crypt device, attached it, and marked it as a physical volume for LVM. I’ve left out the output from the commands.

root@cipher:~# cryptsetup luksFormat /dev/md3 -c aes -s 256 -h sha256
root@cipher:~# cryptsetup luksOpen /dev/md3 crypted
root@cipher:~# pvcreate /dev/mapper/crypted

After this, I created a volume group using the freshly created pv, and activated a 500Gb logical volume, created an xfs filesystem and mounted it:

root@cipher:~# vgcreate crypted_vg /dev/mapper/crypted
root@cipher:~# lvcreate -n test_lv -L500G crypted_vg
root@cipher:~# mkfs.xfs /dev/crypted_vg/test_lv
root@cipher:~# mount /dev/crypted_vg/test_lv /mnt/

On the mounted filesystem, I ran bonnie++ to benchmark performance of dm-crypt. During the benchmark cpu load of 6 of the 8 cores were mostly at 0. The two remaining cores were taking up 80%+ load, by the bonnie++ and the kcryptd processes:

root@cipher:~# bonnie++ -d . -s 24000 -u rvdm:rvdm
Version 1.03c       ------Sequential Output------ --Sequential Input- --Random-
                    -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
cipher       24000M 73629  97 62418   6 40186   5 55728  79 98903   9 477.3   1
                    ------Sequential Create------ --------Random Create--------
                    -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16  1516   5 +++++ +++   990   2  1193   3 +++++ +++   866   2

What’s missing in the output above are the commands to create a directory for bonnie to work in, and to make it writable for the right user.

After the benchmark, I wiped the LV, VG and PV, killed the dm-crypt mapping with cryptsetup, and was left with a new usable md device. To keep the tests as similar as possible, I’ve created a new vg, containing the entire md device. After creating a new 500Gb LV, mounting and xfs-ing it, I ran the second benchmark:

root@cipher:~# bonnie++ -d . -s 24000 -u rvdm:rvdm
Version 1.03c       ------Sequential Output------ --Sequential Input- --Random-
                    -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
cipher       24000M 82330  98 157554  17 55584   8 67361  82 226305  22 595.0   1
                    ------Sequential Create------ --------Random Create--------
                    -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16  1154   2 +++++ +++   925   2  1241   3 +++++ +++  1186 3

It’s clear from the output that the overhead from using dm-crypt is quite big; up to 100% in some cases. Also, CPU usage increases significantly. I will be using dm-crypt for crypting some of the virtual machines, but I’ll be sure to only use it when it’s worth the performance penalty..

Note: I realise the bonnie++ output gets chopped off. To be honest, I’m too lazy to fix that :)

Comments Off