Publicaciones

★CVE-2023-6875: Omisión de autorización en POST SMTP Mailer hasta toma de control de cuenta admin

Mi hallazgo de omisión de autorización en POST SMTP Mailer, el flujo del proof of concept y por qué el impacto era alto.

Este fue mi primer bug bounty grande: CVE-2023-6875, una omisión de autorización en el plugin POST SMTP Mailer para WordPress.

El proof of concept público está acá:

UlyssesSaicha/CVE-2023-6875

Envié el proof of concept a Wordfence durante su Holiday Bug Bonanza. El PoC recibió un premio extra, lo cual estuvo muy bueno.

Tarjeta resumen de CVE-2023-6875 con versiones afectadas del plugin e impacto
CVE-2023-6875 afectaba a POST SMTP Mailer hasta la versión 2.8.7 inclusive. WordPress.org actualmente lista el plugin con más de 300,000 instalaciones activas.

El bug

Este es el reporte y writeup original que fue enviado a Wordfence:

Resumen: POST SMTP Mailer es vulnerable a una vulnerabilidad de type juggling en el endpoint REST API post-smtp/v1/connect-app. Esta vulnerabilidad de type juggling permite que un atacante configure el access token del sitio objetivo. Con este access token, un atacante puede usar cualquier endpoint REST API autenticado de post-smtp.

Descripción:

El plugin POST SMTP Mailer implementa el endpoint "connect-app", que permite conectar un sitio web con la app móvil de POST SMTP. Para esto, se genera una auth key que está disponible desde el panel de administración. Esta auth key es un transient con la key "post_smtp_auth_nonce".

Al comparar la key, el plugin realiza el siguiente chequeo:

  public function connect_app( WP_REST_Request $request ) {
    
    $nonce = get_transient( 'post_smtp_auth_nonce' );
    $auth_key = $request->get_header( 'auth_key' );
    $fcm_token = $request->get_header( 'fcm_token' );
    $device = $request->get_header( 'device' );
    $server_url = $request->get_header( 'server_url' );
    
    if( $auth_key == $nonce ) {
      
      $data = array(
        $fcm_token  =>  array(
          'auth_key'        =>  $auth_key,
          'fcm_token'       =>  $fcm_token,
          'device'        =>  $device,
          'enable_notification' =>  1
        )
      );
      
      update_option( 'post_smtp_mobile_app_connection', $data );
      update_option( 'post_smtp_server_url', $server_url );
      
      wp_send_json_success( 
        array(
          'fcm_token' =>  $fcm_token
        ), 
        200 
      );
      
    }

El problema está en que el if statement usa una comparación débil. Si ignoramos el parámetro "auth_key", la variable se evalúa como null. Si el transient todavía no está seteado, entonces la comparación evaluaría al siguiente pseudo código:

[ $auth_key (null) == $nonce (false) ]=> true

A partir de ahí, el fcm_token queda guardado y podemos usarlo en otros requests a la API para autenticarnos con él (más sobre eso más adelante).

Sin embargo, podemos llevarlo un paso más lejos: si el if statement no evalúa como true, se ejecuta el siguiente código:

    delete_transient( 'post_smtp_auth_nonce' );
    
    wp_send_json_error( 
      array(
        'error' =>  'Refresh QR Code page, and scan again.'
      ), 
      200 
    );

Esto nos permitiría efectivamente configurar nuestro token sin importar si existe un transient o no, ya que podemos:

  1. Enviar un request con un $auth_key inválido (eliminando e invalidando cualquier transient existente)
  2. Enviar un request sin $auth_key (entrando en el if statement y guardando nuestro token en el servidor objetivo)

Es importante notar que el token se usa para toda la autenticación con la REST API (excepto el endpoint connect-app), ya que los desarrolladores del plugin optaron por tener su propio sistema de api-token en lugar de usar alguna de las opciones disponibles que WordPress ya provee. Por eso, todos los endpoints de la API tienen el permission_callback seteado en '__return_true'.

Ahora, con el fcm_token seteado, podemos llamar al endpoint get-logs. Este endpoint nos permite ver todos los emails que fueron logueados por el plugin, con su id correspondiente (interno al plugin).

Conociendo el id del email, podemos ver ese email individual en su totalidad. Cabe destacar que todos los emails se loguean, incluyendo emails sensibles como emails de restablecimiento de contraseña. Esto efectivamente nos permite una toma de control no autenticada de una cuenta admin (o de cualquier cuenta, para el caso).

Referencias

BleepingComputer cubrió el caso el 11 de enero de 2024, describiendo POST SMTP Mailer como un plugin de entrega de email usado por 300,000 sitios web y reportando que más de 150k sitios WordPress estaban en riesgo de toma de control.