Offensive Golang (I)

Offensive Golang

Dentro de la seguridad Ofensiva en general, así como en el desarrollo de malware en particular, se requiere salirse de lo convencional para explorar nuevas ideas o herramientas que puedan permitir lograr los objetivos.

Un buen reflejo de este pensamiento podría ser el uso del lenguaje de programación menos común Go (Golang) en el desarrollo de malware. Golang es un lenguaje de programación que ha sido desarrollado por Google y tiene más de una década de antigüedad, pero desde hace unos años se han identificado diferentes ransomwares y artefactos que intentan aprovecharse de las ventajas que posee este lenguaje. Esto ha propiciado una mayor popularidad de Golang en este campo y han aparecido nuevas herramientas y técnicas.

En este artículo se pretende explicar los diferentes componentes y funcionalidades básicas que componen el repositorio OffensiveGolang que he desarrollado, tomando como inspiración la charla en la DEFCON 29 de Ben Kurtz y el repositorio de Michael Long.

Pero antes de comenzar se debe resolver la pregunta principal, ¿Por qué Golang?:

  • Multiplataforma: Es posible desarrollar código en Go y compilarlo tanto para Windows, Linux como MacOS, únicamente es necesario proporcionar las variables de entorno adecuadas. A continuación, se muestra un ejemplo de compilación de un simple “Hello World” desde Linux para obtener un ejecutable en Windows, y la compilación de un Portable Executable (PE) en Linux.
package main
import “fmt”
func main() {
      fmt.Println(“hello world”)
}
$ GOOS=windows GOARCH=amd64 go build -ldflags=’-w -s’ -o ‘hello.exe’ main.go
  • Más complejo de analizar: Los binarios resultantes de la compilación en Golang suelen ser más pesados en comparación con otros lenguajes, por ejemplo, el binario compilado anteriormente tiene un tamaño de casi 2 MB, lo que puede añadir dificultad en el análisis. Ahora bien, si durante el proceso de compilación se especifican las flags –w y –s, el ejecutable final no permitirá el acceso a información de depuración ni a la tabla de símbolos de Go, esto se traduce en que se dificulta el acceso al código fuente. En la imagen a continuación se puede observar cómo no es posible acceder al código fuente del binario si se compila estableciendo los parámetros -ldflags=’-w -s’.
Ejemplo de binario compilado con ldflags -s y -w
  • CGO: Se trata de un paquete de Golang que permite realizar llamadas a código en C. Además, en un mismo fichero con la extensión .go se puede añadir código en C y ejecutarse. El uso de este paquete permite realizar llamadas a la API de Windows, generar ficheros del tipo Dynamic link library (DLL) o reutilizar métodos en C. En el código a continuación se muestra un ejemplo básico que se explica en la Wiki de Golang.
package cgoexample

/*
#include <stdio.h>
#include <stdlib.h>

void myprint(char* s) {
      printf(“%s\n”, s);
}
*/
import “C”

import “unsafe”

func Example() {
      cs := C.CString(“Hello from stdio\n”)
      C.myprint(cs)
      C.free(unsafe.Pointer(cs))
}
  • La existencia de múltiples paquetes: Es frecuente el uso de terceras librerías en Golang, dado que existen muchos paquetes que proporcionan funciones que permiten realizar acciones de forma mucho más fácil en este lenguaje.

No obstante, antes de desarrollar algunos artefactos en Golang hay que tener en cuenta ciertos aspectos que no juegan a favor del lado de un atacante.

  • A pesar de que un binario en Golang puede resultar más complejo de analizar, no toda la información se ofusca en una compilación normal, hay herramientas específicas que se encargan de obtener información de un binario compilado en Go, como es el caso de redress o plugins para IDA entre otras.

En la imagen a continuación se muestra como mediante redress es posible obtener rutas, paquetes e interfaces que son utilizados en un fichero DLL ejemplo del repositorio OffensiveGolang.

Uso de redress con una dll compilada del repositorio OffensiveGolang

Sin embargo, cabe destacar que existen diferentes herramientas como Garble que permiten ofuscar gran parte de la información que contienen los binarios. A continuación, se muestra la información que obtiene redress del binario que se ha mostrado anteriormente, pero en esta ocasión compilado mediante Garble.

C:\> garble -literals -seed=random -tiny build -buildmode=c-shared -o evil.dll
Ejemplo de utilización de redress con un binario compilado mediante Garble

Como se puede observar en la imagen anterior, algunos nombres de paquetes y funciones han sido ofuscados, sustituyendo el nombre por caracteres aleatorios. No obstante, aún se puede obtener el nombre de paquetes externos o funciones exportadas en el paquete principal.

  • Cuando se utiliza CGO, el código en C es compilado por defecto con GCC, esto implica que herramientas como Garble no pueden ofuscar la exportación de funciones en C. Además, herramientas nativas de los sistemas Windows, como dumpbin permiten obtener información del binario, como se muestra a continuación (funciones importadas y funciones exportadas, respectivamente).
C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools> dumpbin /IMPORTS evill.dll
Métodos de las API de Windows que importa la dll maliciosa
C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools> dumpbin /EXPORTS evill.dll
Métodos que exporta la dll maliciosa

En las imágenes previas se observa cómo es posible identificar los métodos que importa y exporta respectivamente el fichero DLL malicioso que utiliza el paquete CGO y que se ha compilado con Garble.

Ahora bien, una vez se ha puesto en contexto de las ventajas y limitaciones que existen en el desarrollo de artefactos ofensivos en Golang, toca explicar más detalladamente de qué trata el repositorio OffensiveGolang, y qué podemos encontrar en él.

Pero eso lo veremos en el siguiente artículo.