Escalamiento de Privilegios Local mediante Manejo Incorrecto de Autorización en el Instalador del Controlador de Impresora EPSON

Jan 27, 2025

CVE Number

CVE-2025-4960

Credits

Carlos Garrido of Pentraze Cybersecurity

Descripción de la Vulnerabilidad

La herramienta com.epson.InstallNavi.helper se instala en el sistema durante el proceso de instalación del driver de la impresora EPSON L3250. Se identificó una vulnerabilidad de escalamiento de privilegios local debido a múltiples fallos en la implementación del helper. En concreto, este no autentica correctamente al cliente que inicia la conexión ni implementa de forma adecuada el modelo de autorización de macOS.

Un atacante puede crear un cliente malicioso para comunicarse con el servicio vulnerable utilizando el protocolo XPC. Dado que el servicio no verifica el estado de firmado de código del cliente ni realiza comprobaciones de identidad apropiadas, expone funcionalidades de alto privilegio a usuarios no autorizados.

Además, el helper invoca incorrectamente la API AuthorizationCopyRights utilizando un named right que él mismo registra en el sistema dentro de la base de datos de autorización (también conocida como policy database) en /var/db/auth.db. Estos derechos, introducidos por el propio servicio vulnerable, son excesivamente permisivos y carecen de restricciones de acceso adecuadas. Como resultado, cualquier usuario local, independientemente de su nivel de privilegio, puede solicitar y obtener autorización a través del authorization daemon (authd). En una configuración segura, este tipo de derechos debería estar estrictamente acotado y limitado a usuarios y grupos con altos privilegios (por ejemplo, admin o root) para acciones que requieren acceso elevado, como ejecutar binarios privilegiados, instalar componentes del sistema o modificar configuraciones globales.

Como consecuencia, un atacante puede realizar operaciones privilegiadas, como ejecutar comandos arbitrarios, ejecutar scripts o instalar paquetes, sin necesidad de credenciales administrativas.

Entendiendo la autorización

La autorización se refiere al proceso mediante el cual una entidad, como un usuario, proceso o servicio, recibe permiso para realizar operaciones restringidas o privilegiadas. En macOS, el término también se utiliza comúnmente para referirse al derecho o regla específica definida en la base de datos de autorización (/var/db/auth.db) que controla el acceso a dichas operaciones.

macOS proporciona un framework de autorización flexible y granular diseñado para controlar el acceso a operaciones privilegiadas. Dependiendo de la arquitectura de la aplicación, existen varios modelos mediante los cuales el software puede solicitar y aplicar autorización. Comprender estos modelos es esencial para evaluar la postura de seguridad de cualquier aplicación que realice tareas privilegiadas.

Modelo Autorrestrictivo

En un modelo de aplicación autorrestrictivo, la aplicación gestiona su propia autorización creando una referencia de autorización e interactuando directamente con el Security Server (securityd). Cuando se solicita una operación privilegiada, el Security Agent (com.apple.security.agent) puede solicitar autenticación o consentimiento al usuario. Si se concede la autorización, la operación continúa.

Imagen obtenida de la guía Apple Authorization Services Programming Guide

Modelo Factorizado

El software de instalación de EPSON L3250 adopta el modelo de aplicación factorizada, en el cual la aplicación principal delega todas las operaciones privilegiadas a un componente separado conocido como helper tool (o PrivilegedHelperTool). A diferencia de las aplicaciones autorrestrictivas,donde el código privilegiado reside dentro de la aplicación principal, las aplicaciones factorizadas aíslan la funcionalidad sensible en un servicio independiente que se ejecuta como un proceso separado con permisos elevados.

Este diseño ofrece dos ventajas clave:

  1. Mejor auditabilidad, ya que la lógica privilegiada queda confinada a un binario bien definido y aislado.

  2. Mayor seguridad, al minimizar los privilegios de la aplicación principal y limitar la superficie de ataque al helper tool.

Sin embargo, estos beneficios dependen completamente de una implementación correcta del modelo de autorización. Como demostraremos, fallos de diseño en los named rights definidos por el helper tool en la base de datos de autorización, en la forma en que se obtienen dichos derechos y en cómo el servicio interactúa con Authorization Services bajo determinadas condiciones, pueden derivar en vulnerabilidades críticas.

Imagen obtenida de la guía Apple Authorization Services Programming Guide

Según el flujo de trabajo recomendado por Apple, una aplicación cliente debería comenzar realizando preautorización (pre-authorization), es decir, intentando obtener los privilegios necesarios (por ejemplo, system.preferences.admin) para llevar a cabo operaciones que requieren acceso elevado, como modificar preferencias del sistema a nivel global. Esto se realiza típicamente mediante AuthorizationCreate() seguido de AuthorizationCopyRights(), utilizando flags como kAuthorizationFlagInteractionAllowed (que permite interacción con el usuario para autenticación a través del Security Agent, com.apple.security.agent), kAuthorizationFlagPreAuthorize y kAuthorizationFlagExtendRights. Aunque este proceso en dos pasos se considera una buena práctica, también es posible crear y autorizar en una sola llamada pasando los derechos requeridos directamente a AuthorizationCreate() junto con los flags adecuados.

Aunque la preautorización (pre-authorization) no es estrictamente obligatoria, se considera una buena práctica. Garantiza que los clientes no autorizados sean rechazados de forma temprana, antes de llegar al helper tool, evitando comunicación innecesaria entre procesos y consumo de recursos. Esto no solo mejora el rendimiento, sino que también reduce la superficie de ataque general al limitar la exposición del servicio privilegiado únicamente a solicitudes debidamente autorizadas.

En el contexto de las aplicaciones factorizadas, el cliente (aplicación principal) puede generar una representación externa de la sesión de autorización utilizando AuthorizationMakeExternalForm(). Esto produce un handle de 12 bytes (AuthorizationExternalForm) que puede transmitirse de forma segura vía XPC al componente del lado servidor (el helper tool). El helper puede entonces validar los derechos del cliente y determinar si debe proceder con la operación privilegiada solicitada.

En este modelo se utilizan dos enfoques comunes:

  1. El cliente realiza preautorización, y el helper tool simplemente valida la referencia de autorización existente utilizando AuthorizationCopyRights() para asegurar que se concedió el derecho requerido.

  2. El cliente no realiza preautorización, y el helper tool asume la responsabilidad completa de invocar AuthorizationCopyRights() para solicitar el privilegio necesario en nombre del usuario.

La diferencia clave entre estos enfoques radica en dónde se inicia el flujo de autorización, ya sea de forma proactiva por el cliente (enfoque 1), o de forma reactiva por el servidor (enfoque 2). En ambos casos, sin embargo, el helper tool debe verificar que la referencia de autorización proporcionada incluye el named right requerido antes de ejecutar cualquier acción privilegiada.

Ingeniería Reversa (RE)

Fase 1: Servicio Mach

El software de impresoras EPSON se basa en el protocolo XPC para facilitar la comunicación entre el EPSON Installer y su helper tool asociado. Para habilitar esta comunicación, el helper debe registrarse y escuchar conexiones entrantes utilizando un nombre específico de servicio Mach. Al examinar el método init de la clase EWIHelperTool, podemos confirmar que el servicio se inicializa llamando a initWithMachServiceName: con el nombre que utilizará para el registro XPC.

Este nombre de servicio Mach es crítico, ya que identifica el endpoint al que deben apuntar los clientes (legítimos o maliciosos) para establecer una conexión con el helper tool.

/* @class EWIHelperTool */
-(void *)init {
    var_20 = self;
    *(&var_20 + 0x8) = *0x1000092d8;
    rax = [[&var_20 super] init];
    rbx = rax;
    if (rax != 0x0) {
            rax = [NSXPCListener alloc];
            rax = [rax initWithMachServiceName:@"com.epson.InstallNavi.helper"];
            rdi = *(rbx + 0x8);
            *(rbx + 0x8) = rax;
            [rdi release];
            [*(rbx + 0x8) setDelegate:rbx];
    }
    rax = rbx;
    return rax;
}

Fase 2: Aceptando una Conexión en el Helper

Las conexiones XPC entrantes se gestionan a través del método listener:shouldAcceptNewConnection:, definido por el protocolo NSXPCListenerDelegate.

Cuando el helper recibe un mensaje inicial a través de un NSXPCConnection, el sistema invoca el método del delegado listener:shouldAcceptNewConnection:, pasando un objeto NSXPCListener y una instancia de NSXPCConnection. Este método determina si la conexión debe aceptarse. Devolver YES permite que la conexión continúe, mientras que devolver NO la rechaza.

Dentro de este método, se espera que el helper valide los code-signing flags del cliente que se conecta. No realizar esta verificación permite que cualquier cliente no confiable, incluidos binarios creados por un atacante, establezca comunicación con el servicio. Este fallo puede servir como punto de entrada inicial en una cadena de explotación más amplia, permitiendo acceso no autorizado a funcionalidades privilegiadas.

El siguiente desensamblado corresponde al método listener:shouldAcceptNewConnection, que define la interfaz (protocolo) que se expondrá al cliente XPC. En este caso, la interfaz exportada es EWIHelperToolProtocol.

Lo particularmente relevante es que el método devuelve YES de forma incondicional, sin realizar ninguna validación sobre la autenticidad del cliente. Como resultado, cualquier cliente no confiable o malicioso puede establecer exitosamente una conexión con com.epson.InstallNavi.helper, eludiendo las comprobaciones de integridad y abriendo la puerta a interacciones no autorizadas con el servicio privilegiado.

char -[EWIHelperTool listener:shouldAcceptNewConnection:](struct EWIHelperTool* self, SEL sel, id listener, id shouldAcceptNewConnection)

    ; Function Prologue 

    push    rbp {__saved_rbp}
    mov     rbp, rsp {__saved_rbp}
    push    r15 {__saved_r15}
    push    r14 {__saved_r14}
    push    r13 {__saved_r13}
    push    r12 {__saved_r12}
    push    rbx {__saved_rbx}
    push    rax {var_38}
    mov     rbx, rcx ; NSXPCConnection * _newConnection
    mov     r14, rdi ; EWIHelperTool* self
    mov     r12, qword [rel _objc_retain]
    mov     rdi, rdx ; NSXPCListener *  _listener
    call    r12
    mov     r15, rax
    mov     rdi, rbx
    call    r12
    mov     r12, rax ; NSXPCConnection * _newConnection
    
    ; Defining the EWIHelperToolProtocol interface 

    mov     rdi, qword [rel _OBJC_CLASS_$_NSXPCInterface]
    mov     rdx, qword [rel protoRef_EWIHelperToolProtocol]
    mov     rsi, qword [rel selRef_interfaceWithProtocol:]
    mov     r13, qword [rel _objc_msgSend]
    call    r13

    ; Exporting the EWIHelperToolProtocol protocol via the setExportedInterface setter method

    mov     rdi, rax
    call    _objc_retainAutoreleasedReturnValue
    mov     rbx, rax
    mov     rsi, qword [rel selRef_setExportedInterface:]
    mov     rdi, r12
    mov     rdx, rax
    call    r13

    ; Exporting EWIHelperTool* object via the setExportedObject setter method

    mov     rsi, qword [rel selRef_setExportedObject:]
    mov     rdi, r12
    mov     rdx, r14
    call    r13

    ; Resumming connection

    mov     rsi, qword [rel selRef_resume]
    mov     rdi, r12
    call    r13

    ; Function epilogue

    mov     eax, 0x1
    add     rsp, 0x8
    pop     rbx
    pop     r12
    pop     r13
    pop     r14
    pop     r15
    pop     rbp
    retn

Fase 3: Obtención de la definición de EWIHelperToolProtocol

Mediante el uso de la utilidad class-dump, podemos extraer la definición del protocolo EWIHelperToolProtocol. Esta información resulta útil más adelante al crear un cliente capaz de comunicarse con el helper tool, ya que revela los métodos remotos expuestos por el servicio. Comprender esta interfaz es esencial para invocar dichos métodos y, en última instancia, lograr la ejecución de comandos.

% class-dump -C EWIHelperToolProtocol /Library/PrivilegedHelperTools/com.epson.InstallNavi.helper 

@protocol EWIHelperToolProtocol
- (void)executeCommand:(NSString *)arg1 arguments:(NSArray *)arg2 authorization:(NSData *)arg3 withReply:(void (^)(NSError *))arg4;
- (void)executeScript:(NSString *)arg1 authorization:(NSData *)arg2 withReply:(void (^)(NSError *))arg3;
- (void)copyItemAtPath:(NSString *)arg1 toPath:(NSString *)arg2 authorization:(NSData *)arg3 withReply:(void (^)(NSError *))arg4;
- (void)installPackage:(NSString *)arg1 authorization:(NSData *)arg2 withReply:(void (^)(NSError *))arg3;
- (void)getVersionWithReply:(void (^)(NSString *))arg1;
@end

Fase 4: Authorization Rights Setup (EverBetterAuthorization)

🔒 Importante
EvenBetterAuthorization es un proyecto de ejemplo desarrollado por Apple para ilustrar cómo implementar PrivilegedHelperTools usando Authorization Services. Demuestra cómo restringir el acceso a funcionalidad privilegiada expuesta mediante una XPC interface. Sin embargo, este código contiene patrones de diseño inseguros que, si se replican sin las salvaguardas adecuadas, pueden introducir riesgos serios de seguridad—en particular relacionados con el manejo incorrecto de la autorización.

A lo largo de este proceso de ingeniería reversa, destacaremos cómo estos patrones inseguros se reflejan en el helper tool vulnerable bajo análisis. La revisión de EvenBetterAuthorization se enfocará en las funciones clave y los flujos lógicos relevantes para entender el origen y el impacto de la vulnerabilidad.

El análisis comienza con el método setupAuthorizationRights:, que es responsable de registrar las authorization policies de la aplicación en la base de datos de autorización del sistema ubicada en /var/db/auth.db. Este método asegura que solo se agreguen a la base de datos los privilegios (rights) que aún no existen, estableciendo la base de cómo el helper tool aplica o falla en aplicar controles de autorización para operaciones privilegiadas.

/* @class EWIHelperCommon */
+(int)setupAuthorizationRights:(int)arg2 {
    rdx = arg2;
    if (rdx != 0x0) {
            var_28 = *__NSConcreteStackBlock;
            *(&var_28 + 0x8) = 0xffffffffc0000000;
            *(&var_28 + 0x10) = ___44+[EWIHelperCommon setupAuthorizationRights:]_block_invoke;
            *(&var_28 + 0x18) = ___block_descriptor_40_e34_v32?0"NSString"816"NSString"24l;
            *(&var_28 + 0x20) = rdx;
            rax = [EWIHelperCommon enumerateRightsUsingBlock:&var_28];
    }
    else {
            +[EWIHelperCommon setupAuthorizationRights:].cold.1();
    }
    return rax;

El método setupAuthorizationRights: itera sobre los privilegios de autorización (authorization rights) definidos usando la función enumerateRightsUsingBlock:. Durante este proceso, invoca AuthorizationRightGet para cada privilegio con el fin de comprobar si ya existe en la base de autorización. Si un privilegio no se encuentra, el método procede a llamar a AuthorizationRightSet para registrarlo.

Este registro normalmente ocurre solo durante la primera ejecución de la aplicación, momento en el cual los privilegios necesarios se escriben en /var/db/auth.db.

int ___44+[EWIHelperCommon setupAuthorizationRights:]_block_invoke(int arg0, int arg1, int arg2, int arg3) {
    var_30 = arg0;
    r13 = [arg1 retain];
    r14 = [arg2 retain];
    r15 = [arg3 retain];
    rax = objc_retainAutorelease(r13);
    r13 = rax;
    if (AuthorizationRightGet([rax UTF8String], 0x0) == 0xffff159b && AuthorizationRightSet(*(var_30 + 0x20), [objc_retainAutorelease(r13) UTF8String], r14, r15, 0x0, @"Common") != 0x0) {
            ___44+[EWIHelperCommon setupAuthorizationRights:]_block_invoke.cold.1();
    }
    else {
            [r15 release];
            [r14 release];
            rax = [r13 release];
    }
    return rax;
}

La siguiente sección ilustra los named rights que se añaden a la base de datos de autorización para cada comando o función que el helper tool está diseñado para ejecutar. Estos privilegios definen los permisos requeridos que los clientes deben obtener para invocar operaciones privilegiadas específicas a través de la interfaz XPC (EWIHelperToolProtocol):

  • Para installPackage:authorization:withReply:
void ___30+[EWIHelperCommon commandInfo]_block_invoke(void * _block) {

    var_30 = **___stack_chk_guard;
    rax = NSStringFromSelector(@selector(installPackage:authorization:withReply:));
    rax = [rax retain];
    var_198 = rax;
    var_70 = rax;
    var_A0 = @"authRightName";
    var_88 = @"com.epson.InstallNavi.helper.pkginstall";
    *(&var_A0 + 0x8) = @"authRightDefault";
    *(&var_88 + 0x8) = @"allow";
    .
    .
    <SNIP>
    .
    .
}
  • Para copyItemAtPath:toPath:authorization:withReply:
    rax = NSStringFromSelector(@selector(copyItemAtPath:toPath:authorization:withReply:));
    rax = [rax retain];
    var_178 = rax;
    *(&var_70 + 0x8) = rax;
    var_D0 = @"authRightName";
    var_B8 = @"com.epson.InstallNavi.helper.copyitem";
    *(&var_D0 + 0x8) = @"authRightDefault";
    *(&var_B8 + 0x8) = @"allow";
    .
    .
    <SNIP>
    .
    .
  • Para executeScript:authorization:withReply::
    rax = NSStringFromSelector(@selector(executeCommand:arguments:authorization:withReply:));
    rax = [rax retain];
    var_138 = rax;
    *(&var_70 + 0x18) = rax;
    var_130 = @"authRightName";
    var_118 = @"com.epson.InstallNavi.helper.executeCommand";
    *(&var_130 + 0x8) = @"authRightDefault";
    *(&var_118 + 0x8) = @"allow";
    *(&var_130 + 0x10) = @"authRightDescription";
    .
    .
    <SNIP>
    .
    .

La observación más crítica al analizar los named rights registrados por este helper tool es identificar el denominador común entre todos ellos: cada privilegio definido bajo la clave authRightDefault está configurado con el valor "allow" (kAuthorizationRuleClassAllow). Esto implica que cualquier usuario local, incluidos aquellos con bajos privilegios, puede obtener estos privilegios para ejecutar las funciones o comandos correspondientes expuestos a través de la XPC interface.

Al acceder a la base de datos de autorización, deberíamos poder observar todos los named rights definidos por el helper tool, incluyendo los tres mencionados anteriormente (com.epson.InstallNavi.helper.executeScript, com.epson.InstallNavi.helper.copyitem y com.epson.InstallNavi.helper.pkginstall), como se muestra a continuación.

% sudo sqlite3 /var/db/auth.db
Password:
SQLite version 3.37.0 2021-12-09 01:34:53
Enter ".help" for usage hints.
sqlite> SELECT name FROM rules WHERE name LIKE '%epson%';
com.epson.InstallNavi.helper.copyitem
com.epson.InstallNavi.helper.executeCommand
com.epson.InstallNavi.helper.executeScript
com.epson.InstallNavi.helper.pkginstall
sqlite>

Adicionalmente, podemos confirmar nuestros hallazgos mediante ingeniería reversa consultando la base de datos de autorización utilizando la herramienta de línea de comandos security. Al pasar el parámetro authorizationdb read junto con el nombre del privilegio específico, por ejemplo, com.epson.InstallNavi.helper.executeCommand, podemos inspeccionar cómo el helper tool define cada authorization policy:

% security authorizationdb read com.epson.InstallNavi.helper.executeCommand
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>class</key>
	<string>rule</string>
	<key>created</key>
	<real>769283819.42923605</real>
	<key>default-prompt</key>
	<dict>
		<key></key>
		<string>Epson Web Installer is trying to execute a command.</string>
	</dict>
	<key>identifier</key>
	<string>com.epson.InstallNavi</string>
	<key>modified</key>
	<real>769283819.42923605</real>
	<key>requirement</key>
	<string>identifier "com.epson.InstallNavi" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = TXAEAV5RN4</string>
	<key>rule</key>
	<array>
		<string>allow</string>
	</array>
	<key>version</key>
	<integer>0</integer>
</dict>
</plist>

Fase 5: AuthorizationCopyRights()

Ya hemos inspeccionado cómo el helper tool define sus named rights en la base de datos de autorización y confirmado que cualquier usuario local puede obtener dichos privilegios para invocar comandos expuestos a través de la interfaz XPC. Sin embargo, queda una pregunta clave: `¿en qué punto ocurre realmente la autorización?

Para responder a esto, examinemos como ejemplo la implementación de la función executeCommand:arguments:authorization:withReply:.

Al inicio de la función, la primera operación que observamos es una llamada al método checkAuthorization:command:. Este método recibe dos parámetros: la referencia de autorización (authorization reference), creada por el cliente utilizando AuthorizationMakeExternalForm(), y el selector correspondiente a la función solicitada.

/* @class EWIHelperTool */
-(int)executeCommand:(int)arg2 arguments:(int)arg3 authorization:(int)arg4 withReply:(int)arg5 {
    r13 = arg0;
    var_40 = [arg2 retain];
    var_38 = [arg3 retain];
    rbx = [arg5 retain];
    rax = [r13 checkAuthorization:arg4 command:arg1]; // [r13 checkAuthorization:AuthorizationExternalForm command:@selector(executecommand:arguments:authorization:withReply:)];
    rax = [rax retain];
    .
    .
    <SNIP>
    .
    .

El método checkAuthorization:command: puede resumirse en los siguientes pasos:

  1. Deserializar la referencia de autorización (authorization reference) utilizando AuthorizationCreateFromExternalForm(), reconstruyendo el AuthorizationRef original a partir de los datos recibidos del cliente.

  2. Determinar si existe un named right para el comando o función solicitada. Esto se realiza mediante una llamada a [EWIHelperCommon authorizationRightForCommand:].

  3. Realizar la comprobación de autorización utilizando AuthorizationCopyRights(), que evalúa si el cliente está autorizado a ejecutar la operación solicitada en función del privilegio correspondiente definido en la base de datos de autorización.


/* @class EWIHelperTool */
-(void)checkAuthorization:(int)arg2 command:(int)arg3, ... {
    r14 = arg3;
    rax = [arg2 retain];
    if (r14 == 0x0) goto loc_100003612;

loc_100003495:
    r15 = rax;
    var_30 = 0x0;
    if (rax == 0x0 || [r15 length] != 0x20) goto loc_1000034bb;

loc_1000034f7:
    rax = objc_retainAutorelease(r15);
    rax = [rax bytes];
    rax = AuthorizationCreateFromExternalForm(rax, &var_30);
    if (rax != 0x0) goto loc_1000035b0;

loc_100003523:
    *(int128_t *)(&var_60 + 0x10) = intrinsic_movaps(*(int128_t *)(&var_60 + 0x10), 0x0);
    var_60 = intrinsic_movaps(var_60, 0x0);
    var_40 = 0x1;
    rax = [EWIHelperCommon authorizationRightForCommand:r14];
    rax = [rax retain];
    rax = objc_retainAutorelease(rax);
    var_60 = [rax UTF8String];
    [rax release];
    if (var_60 == 0x0) goto loc_10000361c;

loc_100003593:
    rbx = 0x0;
    rax = AuthorizationCopyRights(var_30, &var_40, 0x0, 0x3, 0x0);
    if (rax != 0x0) {
            rbx = [[NSError errorWithDomain:**_NSOSStatusErrorDomain code:sign_extend_64(rax) userInfo:0x0] retain];
    }
    .
    .
    <SNIP>
    .
    .

Fase 6: Resumen de nuestros hallazgos

  1. El helper tool no valida la autenticidad del cliente que intenta establecer una conexión XPC. Cualquier proceso local puede interactuar con el servicio, independientemente de su origen o nivel de confianza.

  2. El helper tool define named rights en la base de datos de autorización que son accesibles para todos los usuarios, incluidos aquellos sin privilegios administrativos. Cada privilegio está configurado con la regla kAuthorizationRuleClassAllow ("allow"), lo que implica que el sistema no solicita credenciales a través del Security Agent (com.apple.security.agent) durante el proceso de autorización.

  3. Tras inspeccionar la interfaz XPC (EWIHelperToolProtocol) expuesta, identificamos varios métodos que pueden ser abusados para ejecutar comandos a nivel del sistema, habilitando de facto la ejecución arbitraria de comandos a través del servicio vulnerable.

Explotación

La explotación puede resumirse en los siguientes pasos:

  1. Crear una referencia de autorización (authorization reference) utilizando AuthorizationCreate().

  2. Serializar la referencia de autorización (authorization reference) mediante AuthorizationMakeExternalForm().

  3. Establecer una conexión XPC con el servicio objetivo (com.epson.InstallNavi.helper).

  4. Invocar el método executeCommand:arguments:authorization:withReply para ejecutar comandos a nivel del sistema.

Escalamiento de privilegios

~ % ./epson-exploit                       
2025-05-19 21:39:15.395 epson-exploit[50314:2311798] [+] EPSON L3250 Local Privilege Escalation

2025-05-19 21:39:15.408 epson-exploit[50314:2311798] [+] `AuthorizationCreate()` - Last status: `No error.`
2025-05-19 21:39:15.408 epson-exploit[50314:2311798] [+] `AuthorizationMakeExternalForm()` - Last status: `No error.`
2025-05-19 21:39:15.409 epson-exploit[50314:2311798] [+] `initWithBytes:length:` -  Last status: `No error.`
2025-05-19 21:39:15.409 epson-exploit[50314:2311798] [+] Establishing and resuming connection with target service name: `com.epson.InstallNavi.helper`
2025-05-19 21:39:15.409 epson-exploit[50314:2311798] [+] Remote Object: `<__NSXPCInterfaceProxy_EWIHelperToolProtocol: 0x600002bc40f0>`
2025-05-19 21:39:15.409 epson-exploit[50314:2311798] [+] Remote Connection: `<NSXPCConnection: 0x6000039c4000> connection to service named com.epson.InstallNavi.helper`
2025-05-19 21:39:15.409 epson-exploit[50314:2311798] [+] Obtaining version information by calling `getVersionWithReply:`
2025-05-19 21:39:15.410 epson-exploit[50314:2311804] [+] EPSON Version: `1.0.0`
2025-05-19 21:39:20.410 epson-exploit[50314:2311798] [+] Executing OS arbitrary commands as `root` by calling `executeCommand:arguments:authorization:withReply:`
2025-05-19 21:39:20.419 epson-exploit[50314:2311804] [+] Response: `(null)`
2025-05-19 21:39:25.416 epson-exploit[50314:2311798] [+] Done!
garrido@Garridos-MacBook-Air ~ % ls -l /Library/LaunchDaemons/R00t
-rw-r--r--  1 root  wheel  0 May 19 21:39 /Library/LaunchDaemons/R00t
  • Logs:

Al ejecutar el exploit y revisar los logs del sistema, podemos confirmar que authd concedió el privilegio solicitado, sin activar ningún prompt al usuario. Este comportamiento es coherente con el uso de la regla kAuthorizationRuleClassAllow, la cual omite la autenticación interactiva a través del Security Agent (com.apple.security.agent).

garrido@Garridos-MacBook-Air ~ % log stream  | grep -i epson

2025-05-19 21:38:29.383552-0400 0x233e15   Default     0x0                  186    0    authd: [com.apple.Authorization:authd] Succeeded authorizing right 'com.epson.InstallNavi.helper.executeCommand' by client '/Library/PrivilegedHelperTools/com.epson.InstallNavi.helper' [50092] for authorization created by '/Users/garrido/epson-exploit' [50308] (3,0) (engine 792)

Remediación:

  • Seguir la guía/documentación paso a paso de Apple sobre cómo implementar un esquema de autorización seguro en una aplicación factorizada. Desde la aplicación principal (cliente), realizar preautorización (pre-authorization). Esto garantiza que solo usuarios autorizados (miembros del grupo admin o root) puedan ejecutar operaciones privilegiadas.

  • La verificación del proceso cliente en la llamada shouldAcceptNewConnection debe asegurar lo siguiente:

  1. El proceso que se conecta está firmado por Apple.
  2. El proceso que se conecta está firmado por tu team ID (TXAEAV5RN4).
  3. El proceso que se conecta está identificado por su bundle ID.
  4. El proceso que se conecta tiene una versión mínima de software en la que la corrección ha sido implementada o está endurecido contra ataques de inyección.

Para identificar al cliente, utilizar el audit_token en lugar del PID, ya que este último es vulnerable a PID reuse attacks.

Adicionalmente, el cliente autorizado a conectarse debe estar compilado con hardened runtime o library validation y no debe poseer los siguientes entitlements:

  • com.apple.security.cs.allow-dyld-environment-variables
  • com.apple.security.cs.disable-library-validation
  • com.apple.security.get-task-allow

Estos entitlements permitirían que otro proceso inyecte código en la aplicación, habilitando la comunicación con el helper tool.

Además, el cliente que se conecta debe ser identificado mediante el audit token, no mediante el PID (process ID).

Aprovechar las nuevas APIs para la verificación automática de firmado de código antes de aceptar una conexión:

  • [NSXPCConnection setCodeSigningRequirement:] (disponible desde macOS 13.0)

  • xpc_connection_set_peer_code_signing_requirement (disponible desde macOS 12.0)

Referencias:

¿Ver el sitio en español?