-
Notifications
You must be signed in to change notification settings - Fork 861
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
Modify sslpinning bypass for okhttp3 library #572
base: master
Are you sure you want to change the base?
Conversation
Thanks for the PR. In thinking about it, I think we need to explicitly also hook |
Yes I could do that.
Besides, the |
Right, but in older versions of okhttp the function signatures are different. |
I've added hook to |
I only had a quick look at this, but since the hook is just logging (and thus not throwing the exception); It should be easyish to use the information that the Java bridge already provides to hook all overloads, and then make them all do the same thing. I can take a stab at this sometime next week @kiven7299 if you don't want to. I'm on a quick lunch break, but I think something like the below should work. const overloads = Java.use("target_class")["target_method"].overloads
ovearloads.forEach(overload => {
overload.implementation = function(arguments) {
// do some logging or whatever
}
}) This is not the only hook where something like this would be useful. |
Hi @CDuPlooy, const okHttp3CertificatePinnerCheckOkHttp = (ident: string): any | undefined => {
return wrapJavaPerform(() => {
try {
const certificatePinner = Java.use("okhttp3.CertificatePinner");
certificatePinner["check$okhttp"].overloads.forEach((m) => {
// get the argument types for this overload
var calleeArgTypes = m.argumentTypes.map((arg) => arg.className);
send(color_1.colors.blackBright(`Found okhttp3.CertificatePinner.check$okhttp(${calleeArgTypes.join(", ")}), overriding ...`));
m.implementation = function () {
helpers_1.qsend(quiet, color_1.colors.blackBright(`[${ident}] `) + `Called check$okhttp ` +
color_1.colors.green(`OkHTTP 3.x CertificatePinner.check$okhttp()`) +
`, not throwing an exception.`);
}
});
return CertificatePinnerCheckOkHttp;
} catch (err) {
if ((err as Error).message.indexOf("ClassNotFoundException") === 0) {
throw err;
}
}
});
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My only feedback would be to rename that m variable to overload, such that the code is a bit more readable. Otherwise it looks great! Thank you for adding this :)
I'll test it today with a new and old version of OkHttp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to use your branch however the TS did not compile. I have two small suggestions.
The first is to change the hook so that it looks like this:
const okHttp3CertificatePinnerCheckOkHttp = (ident: string): any | undefined => {
// -- Sample Java
//
// Example used to test this bypass.
//
// String hostname = "swapi.co";
// CertificatePinner certificatePinner = new CertificatePinner.Builder()
// .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
// .build();
// OkHttpClient client = new OkHttpClient.Builder()
// .certificatePinner(certificatePinner)
// .build();
// Request request = new Request.Builder()
// .url("https://swapi.co/api/people/1")
// .build();
// Response response = client.newCall(request).execute();
return wrapJavaPerform(() => {
try {
const certificatePinner = Java.use("okhttp3.CertificatePinner");
let originalFunctions = []
certificatePinner["check$okhttp"].overloads.forEach((overload) => {
// preserve the implementations so that we can create a proper job
originalFunctions.push(overload);
// get the argument types for this overload
const calleeArgTypes = overload.argumentTypes.map((arg) => arg.className);
send(c.blackBright(`Found okhttp3.CertificatePinner.check$okhttp(${calleeArgTypes.join(", ")}), overriding ...`));
overload.implementation = function () {
qsend(quiet, c.blackBright(`[${ident}] `) + `Called check$okhttp ` +
c.green(`OkHTTP 3.x CertificatePinner.check$okhttp()`) +
`, not throwing an exception.`);
}
});
return originalFunctions;
} catch (err) {
if ((err as Error).message.indexOf("ClassNotFoundException") === 0) {
throw err;
}
}
});
};
You'll see that for many of the functionality in objection job objects are created, from the TUI you can issue jobs list
and jobs kill $TARGET
if you want to stop a specific hook from running. This works (on the java-bridge anyway) by setting the implementation to null; That's why these functions return references to the class & method pairs. I changed this a little to return an array of the overloads, which will eventually be called by jobs kill
. That routine will try to access each implementation's implementation ie:
// remove implementation replacements
if (job.implementations && job.implementations.length > 0) {
job.implementations.forEach((method) => {
// TODO: May be racy if the method is currently used.
method.implementation = null;
});
}
So basically we should be pushing back an array of implementations (one for each encountered overload).
The only other change was ensuring that we then concat this array and not push it (otherwise when the js code tries to read .implementation from the job object, it'll be undefined)
That could be done via:
job.implementations.push(sslContextEmptyTrustManager(job.identifier));
job.implementations.push(okHttp3CertificatePinnerCheck(job.identifier));
// Specifically this
job.implementations.concat(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);
@leonjza, I'm not sure this works though, as when I kill the job, the hooks are still active. I don't think this has to do with the racey behaviour, because the dummy app only makes a request if you ask it to
Finally, I've verified that with these changes, the bypass still works on your dummy app, however I'd like to test on something that uses a more recent version of OkHttp which I will do during the course of today :)
The full diff is:
147,158c147,154
< let originalFunctions = []
<
< certificatePinner["check$okhttp"].overloads.forEach((overload) => {
< // preserve the implementations so that we can create a proper job
< originalFunctions.push(overload);
<
< // get the argument types for this overload
< const calleeArgTypes = overload.argumentTypes.map((arg) => arg.className);
< send(c.blackBright(`Found okhttp3.CertificatePinner.check$okhttp(${calleeArgTypes.join(", ")}), overriding ...`));
< overload.implementation = function () {
< qsend(quiet, c.blackBright(`[${ident}] `) + `Called check$okhttp ` +
< c.green(`OkHTTP 3.x CertificatePinner.check$okhttp()`) +
---
> certificatePinner["check$okhttp"].overloads.forEach((m) => {
> // get the argument types for this overload
> var calleeArgTypes = m.argumentTypes.map((arg) => arg.className);
> send(color_1.colors.blackBright(`Found okhttp3.CertificatePinner.check$okhttp(${calleeArgTypes.join(", ")}), overriding ...`));
>
> m.implementation = function () {
> helpers_1.qsend(quiet, color_1.colors.blackBright(`[${ident}] `) + `Called check$okhttp ` +
> color_1.colors.green(`OkHTTP 3.x CertificatePinner.check$okhttp()`) +
162,163c158
<
< return originalFunctions;
---
> return CertificatePinnerCheckOkHttp;
329c324
< job.implementations.concat(okHttp3CertificatePinnerCheckOkHttp(job.identifier));
---
> job.implementations.push(okHttp3CertificatePinnerCheckOkHttp(job.identifier));
336d330
<
Hi @CDuPlooy, |
I think I've made much more error in the latest commit. My |
I think there may be some confusion here. In the example I show, With the previous change, if you only pushed one of several overloads and run the kill routine, only that overload would be stopped and the remaining hooks would still be in place with no way to kill them. |
No description provided.