Interfaces de RPC fantásticas y cómo encontrarlas
Edición y contribuciones adicionales de Tricia Howard
Introducción
En los últimos meses nuestro equipo ha dedicado grandes esfuerzos a investigar las MS-RPC por su complejidad y por su naturaleza generalmente poco investigada. Es posible que haya visto las innumerables entradas de blog sobre las vulnerabilidades que hemos descubierto como resultado de este trabajo, tales como srvsvc y Wininit.exe, por ejemplo. Con la enorme cantidad de datos y herramientas que hemos recopilado durante la investigación, lo lógico era depositarlo todo en un único lugar: nuestro kit de herramientas de RPC.
Este repositorio de código abierto tiene todo lo necesario para ayudarle a adentrarse en el mundo de las RPC: herramientas, artículos, entradas de blog y conferencias, así como información sobre vulnerabilidades de RPC y pruebas de concepto. Este repositorio se ha creado en un esfuerzo por hacer que el conocimiento de las RPC sea más accesible tanto para los que las combaten como para los investigadores. Al fin y al cabo, estamos más seguros cuando trabajamos juntos. A cualquier persona que nos haya leído, que haya utilizado las herramientas o compartido nuestro trabajo: ¡gracias! Nos alegramos de que le sea de utilidad.
Una de las herramientas del kit de herramientas es nuestro RPC Interface Analyzer, que ayuda a los profesionales de la seguridad como usted a identificar de forma rápida y sencilla las pistas que conducen a las interfaces potencialmente vulnerables. Esta entrada de blog pretende explicarle sus objetivos y hallazgos, así como ofrecer una descripción general de las RPC y algunos de sus mecanismos de seguridad para aquellos que aún no los conozcan.
¿Qué es una RPC y cuáles son sus mecanismos de seguridad?
Una llamada a procedimiento remoto (RPC) es una forma de comunicación entre procesos (IPC) que permite a un cliente invocar un procedimiento expuesto por un servidor RPC. El cliente llama a la función como si fuera una llamada a procedimiento normal, sin (casi) necesidad de codificar los detalles para la interacción remota. El servidor se puede alojar en un proceso diferente en la misma máquina o en una máquina remota.
Windows utiliza mucho MS-RPC (la implementación de RPC de Windows) para numerosos servicios diferentes, como la programación de tareas, la creación de servicios, la configuración de impresoras y recursos compartidos y la gestión de datos cifrados almacenados de forma remota. El amplio alcance de sus usos y la naturaleza remota de las RPC como vector son precisamente los motivos por los que estamos hablando de este tema ahora mismo y por los que hemos dedicado tantos recursos a la investigación de las RPC. Tiene muchas funcionalidades, así que llama la atención desde el punto de vista de la seguridad.
En las siguientes secciones veremos cómo son las devoluciones de llamadas de seguridad de RPC y cómo puede utilizar la automatización para analizarlas y crear posibles pistas nuevas para la investigación sobre seguridad y vulnerabilidades.
¿Qué es una llamada de seguridad de RPC y cómo funciona?
En resumen, una devolución de llamada de seguridad es una de las varias maneras de proteger una interfaz de RPC. Es una devolución de llamada personalizada implementada por el desarrollador del servidor RPC. Su lógica la decide el desarrollador, que puede aplicar el control de acceso basado en el usuario, la autenticación, los tipos de transporte o impedir el acceso a funciones específicas expuestas por el servidor.
Finalmente, la devolución de llamada arroja RPC_S_OK para permitir la comunicación del cliente con el servidor o uno de los códigos de error de RPC, como RPC_S_ACCESS_DENEGED, para denegarla.
La decisión de aceptar o rechazar la solicitud de un cliente se basa normalmente en un atributo (o más de uno) que analizaremos a continuación.
Secuencia de protocolo
El cliente puede comunicarse con el servidor a través de varios transportes: TCP, canales con nombre, ALPC y otros. La devolución de llamada de seguridad puede comprobar este atributo para filtrar solo las solicitudes de conexión local, solo las solicitudes remotas, la comunicación TCP, etc.
Aunque los valores de secuencia de protocolo no están documentados, asignamos las secuencias de protocolo que se pasan a la devolución de llamada de seguridad dentro de la estructura RpcCallAttributes a los siguientes valores:
#define ncacn_ip_tcp 1
#define ncacn_np 2
#define ncalrpc 3
#define ncacn_http 4
Otras secuencias de protocolo (como ncacn_hvsocket) se pueden probar mediante la devolución de llamada de seguridad a través de un análisis del enlace de cadenas.
Nivel de autenticación
Comprobar el nivel de autenticación del cliente es bastante común en las devoluciones de llamada de seguridad. De esta forma, el servidor puede definir el nivel de autenticación mínimo que espera de sus clientes.
Hay varios niveles de autenticación, cada uno amplía el nivel anterior:
#define RPC_C_AUTHN_LEVEL_DEFAULT 0
#define RPC_C_AUTHN_LEVEL_NONE 1
#define RPC_C_AUTHN_LEVEL_CONNECT 2
#define RPC_C_AUTHN_LEVEL_CALL 3
#define RPC_C_AUTHN_LEVEL_PKT 4
#define RPC_C_AUTHN_LEVEL_PKT_INTEGRITY 5
#define RPC_C_AUTHN_LEVEL_PKT_PRIVACY 6
Por ejemplo, un servidor espera un nivel de autenticación de RPC_C_AUTHN_LEVEL_PKT_PRIVACY para asegurarse de que los datos de comunicación solo son visibles para el cliente y el servidor o bien espera el nivel de autenticación de RPC_C_AUTHN_LEVEL_NONE para indicar que no hay autenticación.
Servicio de autenticación
El servicio de autenticación especifica el servicio que se encarga de validar la directiva de autenticación proporcionada por el nivel de autenticación.
Los valores constantes del servicio de autenticación son:
#DEFINE RPC_C_AUTHN_NONE 0
#DEFINE RPC_C_AUTHN_DCE_PRIVATE 1
#DEFINE RPC_C_AUTHN_DCE_PUBLIC 2
#DEFINE RPC_C_AUTHN_DEC_PUBLIC 4
#DEFINE RPC_C_AUTHN_GSS_NEGOTIATE 9
#DEFINE RPC_C_AUTHN_WINNT 10
#DEFINE RPC_C_AUTHN_GSS_SCHANNEL 14
#DEFINE RPC_C_AUTHN_GSS_KERBEROS 16
#DEFINE RPC_C_AUTHN_DPA 17
#DEFINE RPC_C_AUTHN_MSN 18
#DEFINE RPC_C_AUTHN_DIGEST 21
#DEFINE RPC_C_AUTHN_NEGO_EXTENDER 30
#define RPC_C_NETLOGON 68 (Undocumented)
#DEFINE RPC_C_AUTHN_MQ 100
#Define RPC_C_AUTHN_DEFAULT 0xffffffff
RPC_C_AUTHN_NONE, por ejemplo, desactiva la autenticación, mientras que RPC_C_AUTHN_WINNT utiliza el protocolo NTLM.
Puede encontrar la lista completa de servicios de autenticación y sus valores en esta página de GitHub.
Sesión NULL
Una sesión NULL es una conexión anónima. En este caso, el cliente se comunica con el servidor sin autenticación, es decir, sin nombre de usuario ni contraseña.
En la mayoría de los casos, si se registra una devolución de llamada de seguridad, las sesiones NULL se bloquean por defecto a menos que se proporcione el indicador RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH en el registro del servidor (lea sobre otros casos aquí). Las devoluciones de llamada de seguridad también pueden comprobar las sesiones de NULLI.
Al bloquear el acceso a estas sesiones, la devolución de llamada de seguridad protege la interfaz de RPC de usuarios no autenticados.
Número de operación (opnum)
Opnum representa la función de interfaz que el cliente solicita ejecutar. Más exactamente, opnum es el índice de la tabla de funciones del servidor RPC.
Al comprobar el valor opnum, el servidor puede limitar o bloquear el acceso de los clientes a funciones específicas: funciones que manejan datos confidenciales para clientes remotos, funciones que acceden a la memoria del núcleo o a la funcionalidad para clientes en modo usuario, etc.
El equipo de seguridad de Akamai ha publicado una entrada de blog sobre un interesante ejemplo de devolución de llamada de seguridad que se basa en esta comprobación.
Otras comprobaciones de devolución de llamada de seguridad posibles son:
Origen de la llamada: para comprobar si la llamada procede del modo de usuario o del modo de núcleo.
PID de cliente: para permitir solo procesos específicos.
Enlace de cadenas: para validar atributos de conexión RPC como secuencia de protocolo, dirección de red, información de terminal, etc.
Suplantación: para asegurarse de que el servidor puede ejecutar el código en el contexto de seguridad del cliente.
También hay comprobaciones complejas. Por ejemplo, en la devolución de llamada LsaCapRpcIfCallbackFn en lsasrv.dll, si el servicio de autenticación es netlogon, el nivel de autenticación debe ser menor que rpc_C_AUTHN_LEVEL_PKT_INTEGRITY.
RPC Interface Analyzer: un tutorial práctico
RPC Interface Analyzer es una herramienta de automatización para investigar interfaces de RPC. Permite a los investigadores encontrar y analizar interfaces de RPC de dos fuentes diferentes, ya sea archivos de definición de interfaz (IDL) o archivos PE.
Archivos IDL
Archivos IDL Son los archivos que definen una interfaz de RPC y sus funciones. Al analizar los archivos IDL disponibles públicamente, podemos obtener información acerca de una interfaz de RPC y las funciones que expone su servidor, junto con sus parámetros y tipos de valor de retorno. Con esta información, un investigador puede buscar funciones potencialmente vulnerables. Por ejemplo, funciones que reciben un argumento PATH, como en el caso de PetitPotam.
Para ejecutar nuestro analizador de IDL, ejecute los siguientes comandos:
1. Descargue todos los archivos IDL desde el sitio web de Microsoft a su máquina mediante el script idl_scraper:
idl_scraper.py [-h] [-o OUTPUT] [-p PROTOCOL]
2. A continuación, ejecute idl_parser para analizar estos archivos IDL:
idl_parser.py [-h] [-r] input_path [output_path]
La respuesta será un archivo CSV que contiene los nombres de interfaz de RPC, identificadores únicos universales (UUID), nombres de funciones expuestas y firmas.
Archivos PE
Analizar los archivos IDL puede ser útil, pero podríamos perder algunas interfaces de RPC, ya que solo tenemos acceso a los archivos IDL disponibles públicamente. Otro enfoque es buscar interfaces de RPC en nuestro sistema de archivos local, en archivos PE (archivos .exe o .dll). Consideramos que este enfoque es preferible a comprobar los procesos activos, ya que de esta forma no se nos escapan los servidores RPC que no están activos o que se ejecutan en procesos protegidos.
El RPC PE Analyzer busca interfaces de RPC registradas por la función RpcServerRegisterIf (y sus variantes) y analiza los argumentos que se le pasan, en caso de que se proporcione un desensamblador. Cuando se ejecuta sin él, en el modo predeterminado, el analizador encuentra interfaces de RPC mediante regex. Esta charla detalla el proceso de búsqueda.
Para utilizar RPC PE Analyzer en su modo predeterminado, ejecute el siguiente comando:
pe_rpc_scraper.py <scrape_path> <output_path>
Este comando le proporciona la respuesta básica que incluye el UUID de interfaz, su rol (cliente/servidor) y sus nombres y direcciones de función.
Si desea información más detallada, puede proporcionarle un desensamblador con su ruta (si no es la predeterminada) al script:
pe_rpc_scraper.py [-d {idapro,radare}] [-P DISASSEMBLER_PATH] <scrape_path> <output_path>
El uso de la opción de desensamblador añade también la información de registro de la interfaz, incluyendo:
Indicadores proporcionados en el registro del servidor.
El nombre y la dirección de la devolución de llamada de seguridad de la interfaz.
El descriptor de seguridad del servidor RPC (si lo tiene).
Si está habilitado el almacenamiento en caché global de la devolución de llamada de seguridad.
Nuestra última función, que sale junto con esta entrada de blog, también proporciona un análisis de la devolución de llamada de seguridad en sí.
Ejemplo de uso
Supongamos que queremos analizar todas las interfaces de RPC disponibles en nuestra máquina. Podemos ejecutar RPC PE Analyzer, proporcionar una copia de C:\Windows\System32 como nuestro scrape_path y revisar la respuesta.
pe_rpc_scraper.py -d idapro "C:\Users\User\Documents\System32_Copy"
Dado que la respuesta está en formato JSON, es fácil iterarla y buscar información específica. Por ejemplo:
Buscar todos los clientes/servidores RPC en un archivo DLL.
Buscar todos los clientes/servidores RPC en una máquina.
Buscar los clientes de una interfaz de RPC específica.
Encontrar la devolución de llamada de seguridad RPC de un servidor RPC determinado.
Descubrir qué interfaces de RPC utilizan el almacenamiento en caché a nivel de interfaz (lea esta entrada de blog para ver por qué este tipo de almacenamiento en caché es problemático).
Estos son solo algunos de los muchos usos de esta respuesta. Nos encantaría enterarnos de otros usos e ideas.
Nueva función: información sobre la devolución de llamada de seguridad
Estructura RpcCallAttributes
RPC_CALL_ATTRIBUTES es una estructura que contiene datos relativos a la solicitud del cliente. La devolución de llamada de seguridad de la interfaz en el servidor puede obtener esta información invocando la función RpcServerInqCallAttributes .
typedef struct tagRPC_CALL_ATTRIBUTES_V3_W
{
unsigned int Version;
unsigned long Flags;
unsigned long ServerPrincipalNameBufferLength;
unsigned short *ServerPrincipalName;
unsigned long ClientPrincipalNameBufferLength;
unsigned short *ClientPrincipalName;
unsigned long AuthenticationLevel;
unsigned long AuthenticationService;
BOOL NullSession;
BOOL KernelModeCaller;
unsigned long ProtocolSequence;
RpcCallClientLocality IsClientLocal;
HANDLE ClientPID;
unsigned long CallStatus;
RpcCallType CallType;
RPC_CALL_LOCAL_ADDRESS_V1 *CallLocalAddress;
unsigned short OpNum;
UUID InterfaceUuid;
unsigned long ClientIdentifierBufferLength;
unsigned char *ClientIdentifier;
} RPC_CALL_ATTRIBUTES_V3_W;
Ya hemos mencionado las pruebas que ejecutan las devoluciones de llamada de seguridad. Pueden consultar algunos de estos valores individualmente (por ejemplo, RpcStringBindingParseW para recibir la secuencia de protocolo, RpcBindingInqAuthClient para información de autenticación, etc.) o utilizar esta estructura que lo contiene todo y solo requiere una llamada a función. De hecho, en la mayoría de las devoluciones de llamada de seguridad que analizamos, se invoca RpcServerInqCallAttributes y se utiliza la estructura rpc_CALL_ATTRIBUTES para consultar todos los atributos simultáneamente. Esta circunstancia hace que esta estructura sea muy interesante si desea entender la lógica de la devolución de llamada de seguridad.
La estructura tiene actualmente tres versiones diferentes (1, 2 y 3) y cada una es una extensión de su versión anterior y tiene versiones ANSI y Unicode. Puede encontrar las diferentes versiones y sus miembros en este GitHub.
Información sobre la devolución de llamada de seguridad
La nueva incorporación a nuestro kit de herramientas RPC es la información sobre la devolución de llamada de seguridad, que forma parte del RPC PE Analyzer. Permite echar un vistazo a las comprobaciones y verificaciones que realiza la devolución de llamada de seguridad antes de aprobar o denegar las solicitudes de los clientes.
El análisis de la devolución de llamada de seguridad de una interfaz de RPC y específicamente su acceso a la estructura RPC_CALL_ATTRIBUTES puede arrojar algo de luz sobre la interfaz. Así, puede buscar devoluciones de llamada de seguridad que comprueben el atributo de servicio de autenticación si desea filtrar las interfaces de RPC que utilizan (o no) un protocolo de autenticación específico. También puede consultar las interfaces de RPC que no comprueban las sesiones NULL antes de permitir solicitudes de cliente y cuyos indicadores de registro del servidor permiten sesiones NULL, para descubrir interfaces de RPC potencialmente vulnerables.
¿Cómo funciona?
Para cada devolución de llamada de seguridad, el analizador:
Busca qué versión de la estructura rpc_CALL_ATTRIBUTES se está utilizando y define la estructura relevante en los tipos locales de IDA.
Busca la variable local RpcCallAttribute y aplica la estructura rpc_CALL_ATTRIBUTES como tipo
Analiza las comprobaciones que realiza la devolución de llamada de seguridad que utiliza esta estructura y muestra qué miembro se está testando, el valor con el que se le está comparando y con qué operador (== / != / > < / / etc.).
¿Cómo puedo utilizarlo?
El uso no ha cambiado. Cada vez que ejecute RPC PE Analyzer con el indicador de desensamblador de IDA, la respuesta de cada interfaz de RPC pasará a incluir la información de devolución de llamada de seguridad, si accede a los miembros de la estructura RpcCallAttributes y qué testa.
Nota: Esta incorporación solo está disponible actualmente para IDA. La ejecución del analizador con la opción Radare no incluye información sobre la devolución de llamada de seguridad.
Una vez que tenga la respuesta, puede utilizarla para encontrar interfaces de RPC que puedan ser vulnerables y filtrarlas según sus necesidades. Algunos ejemplos:
Obtener una interfaz de RPC que utilice solo el nivel de autenticación de RPC_C_AUTHN_LEVEL_PKT_PRIVACY.
Obtener las interfaces de RPC que esperan conexiones locales.
Obtener las interfaces de RPC que solo esperan llamadas de modo de núcleo.
Obtener interfaces de RPC que comprueban opnum, como en el caso de vulnerabilidad de almacenamiento en caché.
Resumen
Ha quedado claro que la RPC es un terreno fértil para la investigación, especialmente teniendo en cuenta lo antigua e integrada que está en tantos procesos críticos. Llevamos casi un año recopilando recursos en nuestro repositorio, pero todavía queda mucho por hacer para aprovechar al máximo el potencial de amenaza de las RPC. Todavía está poco investigado en general. Aunque recientemente se le ha prestado más atención, todavía queda mucho por descubrir sobre las innumerables formas en que los atacantes podrían aprovecharlo.
La naturaleza intrínseca de la RPC justifica la investigación de todos los aspectos potencialmente peligrosos. Esperamos que nuestra investigación continua y las herramientas de nuestro repositorio arrojen más luz sobre este vector y que animen a otros investigadores a profundizar en él también. Tanto para los responsables de la seguridad de las RPC como para los investigadores que buscan nuevos temas, las RPC ofrecen un gran potencial de conocimiento.
¿Preparado para empezar? Consulte el repositorioy no se olvide de contarnos en Twitter qué ha encontrado.