SecNot

oct 08, 2020

Leaky Data Diodes

A data diode is a network device that only allows data to travel in one direction, they are used primarily in high security applications like nuclear plants, industrial automation, and armies all over the world. There reason they aren't more widely used, the sacrifice for their high security is that normal transport protocols don't work over data diodes, as they require a bidirectional connection.

And here is where everything becomes muddy, because to overcome this limitation some data diodes use a secondary channel from the isolated side to the untrusted side to send flow control information back so TCP connection can pass through. This makes them potentially vulnerable to a covert channel attack, but there's almost no information on vulnerabilities, attacks, or test tools.

So I wrote leaky diode a test tool to check if they are resistant to a couple of a very simple attacks.

The Attacks

The attacks described in this post require a compromised host on the isolated side, and have a very low throughput (aroud 1 Bytes/min), so they can't realistically be used to leak more than a few KB of data.

Regardless of the limitations there're still many fields where even such small leaks would be catastrophic, and would love to be able to test their diodes so lets dive in.

Close Delay

Close delay uses the delay between the start of a connection and the time it's closed by the server to encode the secret bits.

  • 1 (CLIENT) Open a connection to the server.
  • 2 (CLIENT) Send a request for one of the secret bits and a threshold delay, then continue sending junk data to keep the connection alive.
  • 3 (SERVER) After receiving a request, close the connection inmediately if the value of the secret bit is 0, or wait threshold delay before closing the connection if the value is 1.
  • 4 (CLIENT) If the delay between sending the request to the server and the server closing the connection is greater than threshold/2 then the bit value is 1, and 0 otherwise.

This attack works well in almost all circunstances, as it doesn't require much bandwith and is not affected by network lattency or buffers in the data diodes. The drawback is that uses so many connections tha can be easily detected.

Flow Modulation

Flow modulation uses the flow control mechanisms of TCP to transmit data using the throughput of a connection as the carrier.

  • 1 (CLIENT) Connect with the server and set the tx rates to signal low and high bits. (i.e- 60KB/sec for low 300KB/sec for high bits)
  • 2 (CLIENT) Send a request for one of the secret's bits, and start sending random data at a rate greater than the max encoding rate.
  • 3 (SERVER) On reception of a request start throttling the connection to the to the speed that encodes the value of the requested bit/bits. (as provided in step 1)
  • 4 (CLIENT) Wait until the data rate stabilizes/settles and then sample it to obtain the bit value.
  • 5 (CLIENT) Go to step 1 until all the secret bits have been read.

This attack is harder to detect than close delay as it uses a single connection and not thousands to exfiltrate the data, the data sent can also be made to mimic another protocol like http to make it even harder to detect. And with enough care a finely tunned tool could use very close tx rates for high and low values, so it's indistinguishable from a legitimate connection.

The drawback is that it's vulnerable to network congestion, QoS, and large TCP stack buffers that can make it inconsistent.

Lower level attacks

If you have privileges to directly manipulate the stack, there are few more possible attacks that may work depending on the diode:

  • Using the ACK delay since the reception of a packet.
  • Only sending ACK on even or odd sequence numbers, to encode low/high bits.
  • Manipulating the window size.

Some of these are harder to implement and easier to defend against by the diode, but are great candidades to embed into the OS. Just to be clear I haven't tested any of them.

Test tool

If you have a diode you want to test, download leaky diode

On the isolated side launch the server:

$leaky_server public_ip port 'secret string to leak'

and on the untrusted side one of:

$leaky_client server_ip server_port --mode flow --partial

or

$leaky_client server_ip server_port --mode close --partial

Then wait for a few minutes until the first bytes start to arrive, or if you are impacient add --verbose argument to show notifications on each bit sent/received.

Thank you for reading, and share the results!!!

Click to read and post comments

jul 14, 2015

Two Scoops of Django 1.8

Portada del libro Two Scoops of Django 1.8

Se acaba de publicar la nueva entrega de Two Scoops of Django la guía definitiva de buenas practicas para Django 1.8, tengo la versión anterior y he comprado esta. No es un libro para principiantes, pero se lo recomiendo a todos los desarrolladores experimentados que seguro que encuentran algún truco o consejo útil. Además la versión de Django 1.8 tiene la particularidad de ser “Long-Term Support” (LTS) por lo que tiene soporte de 3 años, así que el libro va a estar actualizado durante más tiempo que versiones anteriores.

Por ahora sólo está disponible en inglés y lo puedes comprar en Amazon

Click to read and post comments

jul 12, 2015

Vista para la descarga de archivos generados dinámicamente

Hoy he estado programando en Django una vista para la descarga de informes, en la que los usuarios registrados de una página pueden pedir un informe de la actividad para un determinado año. La particularidad es que esos informes se generan de forma dinámica en el momento de la petición, nada complicado pero antes de empezar a programar he buscado que paquetes había disponibles, y me he encontrado django-downloadview. Con eso y un poco de código extra para comprobar que el usuario está autenticado he solucionado el problema. Algunas veces CBV parecen magia.

#views.py
from django.shortcuts import render
from django.core.files.base import ContentFile
from django_downloadview import VirtualDownloadView
from django.http import HttpResponseForbidden, HttpResponseServerError


class LoginRequiredMixin(object):

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated():
            return HttpResponseForbidden()
        else:
            return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)


class DownloadReportView(LoginRequiredMixin, VirtualDownloadView):

    def generar_report(self, user, year):
        # Generar contenido del informe para el usuario y fecha
        return "Report content for {} {}".format(user.username, year)

    def get_file(self):
        # Metodo de VirtualDownloadView que devuelve el archivo virtual
        report_name = 'report.txt'
        return ContentFile(self.report_content, name=report_name)

    def get(self, request, *args, **kwargs):

        report_year = kwargs.get('year', None)

        # El contenido del informe se genera aquí en lugar de en get_file, para
        # simplificar la
        try:
            self.report_content=self.generate_report(request.user, report_year)
        except Exception:
            return HttpResponseServerError("There was an error while generating the report")

        return super(DownloadReportView, self).get(request, *args, **kwargs)

Y el archivo urls.py para quien le pueda interesar:

#urls.py
from django.conf.urls import url
from .views import DownloadReportView

urlpatterns = [
        url( # Handle report downloads
                regex = r'^download_report/(?P<year>[0-9]{4})$',
                view  = DownloadReportView.as_view(),
                name  = 'download_user_report'),
]
Click to read and post comments

jul 08, 2015

Crea un servicio de Hosting VPS con Django I

Este es el primer artículo de una series en la que describo como usar Django en conjunción con DigitalOcean para crear tu propio proveedor de de servidores VPS. Algo que hace años hubiera sido impensable sin un equipo de administradores y programadores, y que hoy puede implementarse con unos pocos miles de lineas de código python.

Este artículo es una introducción al API de DigitalOcean y a su libreria de python, la cual usaré en futuros artículos para implementar el servicio.

API de DigitalOcean

El API de Digitalocean actualmente se encuentre en su segunda versión y es muy extenso, no solo permite crear, destruir, y manejar Droplets (nombre que usa para sus VPS), sino que permite controlar todos los aspectos de sus servicios, desde la gestión de DNS, hasta la creación backups, o gestión imágenes. Puede acceder a la documentación del API aquí pero por suerte ya existe una libreria para python llamada python-digitalocean que simplifica enormemente el proceso, crear un Droplet puede ser tan sencillo como:

import digitalocean
droplet = digitalocean.Droplet(token="digitalocean-personal-access-token",
                           name='servidor.midominio.com',
                           region='ams2', # Amsterdam
                           image='ubuntu-14-04-x64', # Ubuntu 14.04 x64
                           size_slug='512mb')
droplet.create()
droplet_id = droplet.id

El identificador devuelto durante la creación del Droplet luego puede ser usado para manejarlo, en el siguiente ejemplo se reinicia, apaga y después elimina:

import digitalocean

manager = digitalocean.Manager(token="digitalocean-personal-access-token")
droplet = manager.get_droplet(droplet_id)

droplet.reboot()
droplet.power_off()
droplet.destroy()

La librería también permite redirigir dominios gestionados desde la DNS de DigitalOcean, por ejemplo si tienes la dirección IP y el hostname de un Droplet puedes redirigir un subdominio:

import digitalocean
import tldextract

droplet_ip = "83.54.134.34"
droplet_hostname = "servidor.midominio.com"

# Crear subdominio usando el hostname del servidor
d_subdomain, d_domain, d_suffix = tldextract.extract(droplet_hostname)

domain = digitalocean.Domain(token="digitalocean-personal-access-token",
                            name=d_domain+"."+d_suffix)

result = domain.create_new_domain_record(type="A", name=d_subdomain, data=droplet_ip)

domain_record_id = result['domain_record']['id']

# Eliminar registro creado
record = digitalocean.Record(
            id=domain_record_id,
            domain_name=d_domain+"."+d_suffix,
            token="digitalocean-personal-access-token")

record.destroy()

Y con esto tenemos los fundamentos para manejar Droplets, pero antes de poder entregarlo al usuario es necesario configurarlo/customizarlo al gusto del usuario.

Metadata y Cloudinit

DigitalOcean incluye un API de Metadatos, que permite a los Droplets acceder a sus propios datos, suministrar datos de usuario durante su creación, y procesar esos datos usando CloudInit si es que contienen alguno de los formatos soportados. En el siguiente ejemplo se envía una cadena que contiene dos variables:

import digitalocean

data = 'USUARIO="admin"\nCLAVE="secreto"'

droplet = digitalocean.Droplet(token=settings.DIGITALOCEAN_SECRET_TOKEN,
                           name='servidor.midominio.com',
                           region='ams2', # Amsterdam
                           image='ubuntu-14-04-x64',
                           size_slug='512mb',
                           user_data=data)
droplet.create()
droplet_id = droplet.id

Desde el droplet se puede acceder a los datos suministrados:

$ curl -sS http://169.254.169.254/metadata/v1/user-data
USUARIO="admin"
CLAVE="secreto"

CloudInit como su nombre indica es una herramienta para inicializar servidores en la nube, la primera vez que arranca el servidor extrae los datos de configuración, y si contienen alguno de los formatos soportados los procesa. Uno de esos formatos es shell script, por ejemplo podemos enviar el siguiente script para crear un archivo de swap de 2GB:

#!/bin/bash
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
echo '/swapfile none swap sw 0 0' >>/etc/fstab

Cloud Config es otro formato muy versátil, que permite realizar la mayoría de las tareas más comunes de configuración de forma muy sencilla, y ademas proporciona funcionalidad extra con su sistema de módulos, en el siguiente ejemplo se añade los mismos 2GB de swap:

#cloud-config
swap:
    filename: /swapfile
    size: 2000000000

Por último el formato Include File consiste en una lista de URLS cuyo contenido es descargado y procesado usando las mismas reglas que si fueran los datos proporcionados con user-data, por ejemplo:

#include
https://myvpsservice.com/scripts/swap-2GB
https://myvpsservice.com/scripts/install-apache
https://myvpsservice.com/scripts/add-keys

Seguro que puedes ver el potencial de éste formato para esta aplicación. Si te interesa conocer el resto de los formatos y módulos disponibles te recomiendo que heches un vistazo a la documentación de cloud-init.

En la segunda parte de esta serie profundizare más en como implementar un sistema de configuración de VPS con Django.

Click to read and post comments

jun 27, 2015

jun 24, 2015

DNIField para Django

Si vas a crear un formulario donde se pueda introducir un DNI/NIF no necesitas crear tu propio DNIField o programar un validador, ya existe en un paquete llamado django-localflavor que agrupa ese tipo de campos para los distintos países.

Por ejemplo para España incluye:

  • ESPhoneNumberField - Números de teléfonos fijos y móviles.
  • ESIdentityCardNumberField - Numero de identificación NIF/CIF/NIE.
  • ESCCCField - Código de cuenta de cliente en formato EEEE-OOOO-CC-AAAAAAAAAA
  • ESProvinceSelect - Selección de provincia.

Instalación

Como siempre pip es la mejor opción para instalar el paquete:

$ pip install django-localflavor

Luego añade 'localflavor' a INSTALLED_APPS en settings.conf:

INSTALLED_APPS = (
    # ...
    'localflavor',
)

Uso

Un ejemplo de como crear un formulario sencillo:

#forms.py
from django import forms
from localflavor.es.forms import ESIdentityCardNumberField, ESPhoneNumberField, ESProvinceField

class ClienteForm(forms.Form):
    nombre = forms.CharField(max_length=200)
    dni = ESIdentityCardNumberField(only_nif=True)
    telefono = ESPhoneNumberField()
    ...

Al campo dni se le ha añadido la opción only_nif=True para que sólo acepte NIF y NIE (Numero identificación extranjeros), sin ella también aceptaría códigos CIF.

Click to read and post comments