Posts

★CVE-2023-6875: POST SMTP Mailer Authorization Bypass to Admin Account Takeover

My POST SMTP Mailer authorization bypass finding, the proof-of-concept flow, and why the impact was high.

This was my first big bug bounty hit: CVE-2023-6875, an authorization bypass in the POST SMTP Mailer WordPress plugin.

The public proof of concept is here:

UlyssesSaicha/CVE-2023-6875

I submitted the proof of concept to Wordfence during its Holiday Bug Bonanza. The PoC received an extra award, which was really cool.

Summary card for CVE-2023-6875 showing affected plugin versions and impact
CVE-2023-6875 affected POST SMTP Mailer versions up to and including 2.8.7. WordPress.org currently lists the plugin at 300,000+ active installations.

The bug

This is the original report and writeup that was sent to Wordfence:

Overview: POST SMTP Mailer is vulnerable to a type juggling vulnerability in the post-smtp/v1/connect-app REST api endpoint. This type juggling vulnerability allows an attacker to set the access token to the target website. With this access token, an attacker can use any authenticated post-smtp REST api endpoint.

Description:

The POST SMTP Mailer plugin implements the "connect-app" endpoint which allows a user to connect their website with the POST SMTP mobile app. To do this, an auth key is generated and is available from the admin dashboard. This auth key is a transient with the "post_smtp_auth_nonce" key.

When comparing the key, the plugin conducts the following check:

  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 
      );
      
    }

The problem lies in the if statement utilizing a loose comparison. If we ignore the "auth_key" parameter, the variable is evaluated to null. If the transient is not yet set, then the comparison would evaluate to the following pseudo code:

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

From here, the fcm_token would get saved, and we can use it in further api requests to authenticate with it (more on that later).

However, we can take it one step further, as if the if statement does not evaluate to true, then the following code is executed:

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

This would effectively allow us to set our token regardless of if a transient exists or not, as we can:

  1. Send a request containing an invalid $auth_key (removing and invalidating any existing transients)
  2. Send a request without an $auth_key (entering the if statement, and saving our token on the target server)

It is important to note that the token is used for all authentication with the rest api (apart from the connect-app endpoint), as the plugin developers opted for having their own api-token system as opposed to using one of the readily available options wordpress provides. Because of this, all the api endpoints have the permission_callback set to '__return_true'.

Now, with the fcm_token set, we can call the get-logs endpoint. This endpoint allows us to see all the emails that have been logged by the plugin, with their corresponding id (internal to the plugin).

By knowing the email id, we can see that individual email in its entirety. It should be noted that all emails are logged, including sensitive emails such as password reset emails. This effectively allows us unauthenticated admin account takeover (or any account, for that matter).

References

BleepingComputer covered the issue on January 11, 2024, describing POST SMTP Mailer as an email delivery plugin used by 300,000 websites and reporting that over 150k WordPress sites were at takeover risk.