Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Job handler update. #705

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 8 additions & 33 deletions agent/src/android/hooking.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { colors as c } from "../lib/color.js";
import { IJob } from "../lib/interfaces.js";
import * as jobs from "../lib/jobs.js";
import { ICurrentActivityFragment } from "./lib/interfaces.js";
import {
Expand Down Expand Up @@ -67,11 +66,7 @@ const getPatternType = (pattern: string): PatternType => {
export const lazyWatchForPattern = (query: string, watch: boolean, dargs: boolean, dret: boolean, dbt: boolean): void => {
// TODO: Use param to control interval
let found = false;
const job: IJob = {
identifier: jobs.identifier(),
implementations: [],
type: `notify-class for: ${query}`,
};
const job: jobs.Job = new jobs.Job(jobs.identifier(),`notify-class for: ${query}`);

// This method loops over all enumerate matches and then calls watch
// with the arguments specified in the parent function
Expand Down Expand Up @@ -262,11 +257,7 @@ export const watch = (pattern: string, dargs: boolean, dbt: boolean, dret: boole
if (patternType === PatternType.Klass) {

// start a new job container
const job: IJob = {
identifier: jobs.identifier(),
implementations: [],
type: `watch-class for: ${pattern}`,
};
const job: jobs.Job = new jobs.Job(jobs.identifier(),`watch-class for: ${pattern}`);

const w = watchClass(pattern, job, dargs, dbt, dret);
jobs.add(job);
Expand All @@ -275,11 +266,7 @@ export const watch = (pattern: string, dargs: boolean, dbt: boolean, dret: boole
}

// assume we have PatternType.Regex
const job: IJob = {
identifier: jobs.identifier(),
implementations: [],
type: `watch-pattern for: ${pattern}`,
};
const job: jobs.Job = new jobs.Job(jobs.identifier(),`watch-pattern for: ${pattern}`);
jobs.add(job);

return new Promise((resolve, reject) => {
Expand All @@ -299,7 +286,7 @@ export const watch = (pattern: string, dargs: boolean, dbt: boolean, dret: boole
});
};

const watchClass = (clazz: string, job: IJob, dargs: boolean = false, dbt: boolean = false, dret: boolean = false): Promise<void> => {
const watchClass = (clazz: string, job: jobs.Job, dargs: boolean = false, dbt: boolean = false, dret: boolean = false): Promise<void> => {
return wrapJavaPerform(() => {
const clazzInstance: JavaClass = Java.use(clazz);

Expand Down Expand Up @@ -337,7 +324,7 @@ const watchClass = (clazz: string, job: IJob, dargs: boolean = false, dbt: boole
};

const watchMethod = (
fqClazz: string, job: IJob, dargs: boolean, dbt: boolean, dret: boolean,
fqClazz: string, job: jobs.Job, dargs: boolean, dbt: boolean, dret: boolean,
): Promise<void> => {
const [clazz, method] = splitClassMethod(fqClazz);
// send(`Attempting to watch class ${c.green(clazz)} and method ${c.green(method)}.`);
Expand Down Expand Up @@ -400,11 +387,7 @@ const watchMethod = (
};

// Push the implementation so that it can be nulled later
if (job.implementations) {
job.implementations.push(m);
} else {
job.implementations = [ m ];
}
job.addImplementation(m);

});
});
Expand Down Expand Up @@ -534,11 +517,7 @@ export const setReturnValue = (fqClazz: string, filterOverload: string | null, n
}

return wrapJavaPerform(() => {
const job: IJob = {
identifier: jobs.identifier(),
implementations: [],
type: `set-return for: ${fqClazz}`,
};
const job: jobs.Job = new jobs.Job(jobs.identifier(), `set-return for: ${fqClazz}`);

const targetClazz: JavaClass = Java.use(clazz);

Expand Down Expand Up @@ -570,11 +549,7 @@ export const setReturnValue = (fqClazz: string, filterOverload: string | null, n
};

// record override
if (job.implementations) {
job.implementations.push(m);
} else {
job.implementations = [ m ];
}
job.addImplementation(m);

});

Expand Down
21 changes: 10 additions & 11 deletions agent/src/android/keystore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
KeyStore,
SecretKeyFactory
} from "./lib/types.js";
import { IJob } from "../lib/interfaces.js";
import * as jobs from "../lib/jobs.js";

// Dump entries in the Android Keystore, together with a flag
Expand Down Expand Up @@ -181,7 +180,7 @@ export const clear = () => {

// Watch for KeyStore.load();
// TODO: Store the keystores themselves maybe?
const keystoreLoad = (ident: string): any | undefined => {
const keystoreLoad = (ident: number): Promise<any> => {
return wrapJavaPerform(() => {
const ks: KeyStore = Java.use("java.security.KeyStore");
const ksLoad = ks.load.overload("java.io.InputStream", "[C");
Expand All @@ -193,12 +192,14 @@ const keystoreLoad = (ident: string): any | undefined => {
`called, loading a ${c.cyanBright(this.getType())} keystore.`);
return this.load(stream, password);
};

return ksLoad
});
};

// Watch for Keystore.getKey().
// TODO: Extract more information, like the key itself maybe?
const keystoreGetKey = (ident: string): any | undefined => {
const keystoreGetKey = (ident: number): Promise<any> => {
return wrapJavaPerform(() => {
const ks: KeyStore = Java.use("java.security.KeyStore");
const ksGetKey = ks.getKey.overload("java.lang.String", "[C");
Expand All @@ -211,20 +212,18 @@ const keystoreGetKey = (ident: string): any | undefined => {
`called, returning a ${c.greenBright(key.$className)} instance.`);
return key;
};

return ksGetKey;
});
};

// Android KeyStore watcher.
// Many, many more methods can be added here..
export const watchKeystore = (): void => {
const job: IJob = {
identifier: jobs.identifier(),
type: "android-keystore-watch",
};
job.implementations = [];
export const watchKeystore = async (): Promise<void> => {
const job: jobs.Job = new jobs.Job(jobs.identifier(), "android-keystore-watch");

job.implementations.push(keystoreLoad(job.identifier));
job.implementations.push(keystoreGetKey(job.identifier));
job.addImplementation(await keystoreLoad(job.identifier));
job.addImplementation(await keystoreGetKey(job.identifier));

jobs.add(job);
};
68 changes: 32 additions & 36 deletions agent/src/android/pinning.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { colors as c } from "../lib/color.js";
import { qsend } from "../lib/helpers.js";
import { IJob } from "../lib/interfaces.js";
import * as jobs from "../lib/jobs.js";
import { wrapJavaPerform } from "./lib/libjava.js";
import {
Expand All @@ -17,7 +16,7 @@ import {
// a simple flag to control if we should be quiet or not
let quiet: boolean = false;

const sslContextEmptyTrustManager = (ident: string): any => {
const sslContextEmptyTrustManager = (ident: number): Promise<any> => {
// -- Sample Java
//
// "Generic" TrustManager Example
Expand Down Expand Up @@ -83,7 +82,7 @@ const sslContextEmptyTrustManager = (ident: string): any => {
});
};

const okHttp3CertificatePinnerCheck = (ident: string): any | undefined => {
const okHttp3CertificatePinnerCheck = (ident: number): Promise<any | undefined> => {
// -- Sample Java
//
// Example used to test this bypass.
Expand Down Expand Up @@ -125,7 +124,7 @@ const okHttp3CertificatePinnerCheck = (ident: string): any | undefined => {
});
};

const okHttp3CertificatePinnerCheckOkHttp = (ident: string): any | undefined => {
const okHttp3CertificatePinnerCheckOkHttp = (ident: number): Promise<any | undefined> => {
// -- Sample Java
//
// Example used to test this bypass.
Expand Down Expand Up @@ -167,7 +166,7 @@ const okHttp3CertificatePinnerCheckOkHttp = (ident: string): any | undefined =>
});
};

const appceleratorTitaniumPinningTrustManager = (ident: string): any | undefined => {
const appceleratorTitaniumPinningTrustManager = (ident: number): Promise<any | undefined> => {
return wrapJavaPerform(() => {
try {
const pinningTrustManager: PinningTrustManager = Java.use("appcelerator.https.PinningTrustManager");
Expand Down Expand Up @@ -204,7 +203,7 @@ const appceleratorTitaniumPinningTrustManager = (ident: string): any | undefined
// blogs/2017/november/bypassing-androids-network-security-configuration/
//
// More information: https://sensepost.com/blog/2018/tip-toeing-past-android-7s-network-security-configuration/
const trustManagerImplVerifyChainCheck = (ident: string): any | undefined => {
const trustManagerImplVerifyChainCheck = (ident: number): Promise<any> => {
return wrapJavaPerform(() => {
try {
const trustManagerImpl: TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
Expand Down Expand Up @@ -241,7 +240,7 @@ const trustManagerImplVerifyChainCheck = (ident: string): any | undefined => {
// Android 7+ TrustManagerImpl.checkTrustedRecursive()
// The work in the following method is based on:
// https://techblog.mediaservice.net/2018/11/universal-android-ssl-pinning-bypass-2/
const trustManagerImplCheckTrustedRecursiveCheck = (ident: string): any | undefined => {
const trustManagerImplCheckTrustedRecursiveCheck = (ident: number): Promise<any> => {
return wrapJavaPerform(() => {
try {
const arrayList: ArrayList = Java.use("java.util.ArrayList");
Expand Down Expand Up @@ -276,7 +275,7 @@ const trustManagerImplCheckTrustedRecursiveCheck = (ident: string): any | undefi
});
};

const phoneGapSSLCertificateChecker = (ident: string): any | undefined => {
const phoneGapSSLCertificateChecker = (ident: number): Promise<any> => {
return wrapJavaPerform(() => {
try {
const sslCertificateChecker: SSLCertificateChecker = Java.use("nl.xservices.plugins.SSLCertificateChecker");
Expand All @@ -285,20 +284,20 @@ const phoneGapSSLCertificateChecker = (ident: string): any | undefined => {
`overriding SSLCertificateChecker.execute()`),
);

const SSLCertificateCheckerExecute = sslCertificateChecker.execute;

SSLCertificateCheckerExecute.overload(
"java.lang.String", "org.json.JSONArray", "org.apache.cordova.CallbackContext").implementation =
// tslint:disable-next-line:only-arrow-functions
function (str, jsonArray, callBackContext) {
qsend(quiet,
c.blackBright(`[${ident}] `) + `Called ` +
c.green(`SSLCertificateChecker.execute()`) +
`, not throwing an exception.`,
);
callBackContext.success("CONNECTION_SECURE");
return true;
};
const SSLCertificateCheckerExecute = sslCertificateChecker.execute.overload("java.lang.String",
"org.json.JSONArray", "org.apache.cordova.CallbackContext");

SSLCertificateCheckerExecute.implementation = function (str, jsonArray, callBackContext) {
qsend(quiet,
c.blackBright(`[${ident}] `) + `Called ` +
c.green(`SSLCertificateChecker.execute()`) +
`, not throwing an exception.`,
);
callBackContext.success("CONNECTION_SECURE");
return true;
};

return SSLCertificateCheckerExecute;

} catch (err) {
if ((err as Error).message.indexOf("ClassNotFoundException") === 0) {
Expand All @@ -309,25 +308,22 @@ const phoneGapSSLCertificateChecker = (ident: string): any | undefined => {
};

// the main exported function to run all of the pinning bypass methods known
export const disable = (q: boolean): void => {
export const disable = async (q: boolean): Promise<void> => {
if (q) {
send(c.yellow(`Quiet mode enabled. Not reporting invocations.`));
quiet = true;
}

const job: IJob = {
identifier: jobs.identifier(),
type: "android-sslpinning-disable",
};

job.implementations = [];
const job: jobs.Job = new jobs.Job(jobs.identifier(), "android-sslpinning-disable");

job.addImplementation(await sslContextEmptyTrustManager(job.identifier));
// Exceptions can cause undefined values if classes are not found. Thus addImplementation only adds if function was hooked
job.addImplementation(await okHttp3CertificatePinnerCheck(job.identifier));
job.addImplementation(await okHttp3CertificatePinnerCheckOkHttp(job.identifier));
job.addImplementation(await appceleratorTitaniumPinningTrustManager(job.identifier));
job.addImplementation(await trustManagerImplVerifyChainCheck(job.identifier));
job.addImplementation(await trustManagerImplCheckTrustedRecursiveCheck(job.identifier));
job.addImplementation(await phoneGapSSLCertificateChecker(job.identifier));

job.implementations.push(sslContextEmptyTrustManager(job.identifier));
job.implementations.push(okHttp3CertificatePinnerCheck(job.identifier));
job.implementations.push(okHttp3CertificatePinnerCheckOkHttp(job.identifier));
job.implementations.push(appceleratorTitaniumPinningTrustManager(job.identifier));
job.implementations.push(trustManagerImplVerifyChainCheck(job.identifier));
job.implementations.push(trustManagerImplCheckTrustedRecursiveCheck(job.identifier));
job.implementations.push(phoneGapSSLCertificateChecker(job.identifier));
jobs.add(job);
};
Loading
Loading