-
-
Notifications
You must be signed in to change notification settings - Fork 435
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
Expand fix for potential Privilege Escalation via Content Provider (CVE-2018-9492) #2482
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package io.sentry.android.core; | ||
|
||
import android.content.ContentProvider; | ||
import android.content.ContentValues; | ||
import android.database.Cursor; | ||
import android.net.Uri; | ||
import io.sentry.android.core.internal.util.ContentProviderSecurityChecker; | ||
import org.jetbrains.annotations.ApiStatus; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
/** | ||
* A ContentProvider that does NOT store or provide any data for read or write operations. | ||
* | ||
* <p>This does not allow for overriding the abstract query, insert, update, and delete operations | ||
* of the {@link ContentProvider}. Additionally, those functions are secure. | ||
*/ | ||
@ApiStatus.Internal | ||
abstract class EmptySecureContentProvider extends ContentProvider { | ||
|
||
private final ContentProviderSecurityChecker securityChecker = | ||
new ContentProviderSecurityChecker(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You will notice that I am not initializing this with a reference to securityChecker.checkPrivilegeEscalation(); It looks nice and is okay because there is no harm in saving a hard reference to the ContentProvider as long as we don't leak it. However, this would come at the price of having to assign the value of this in EmptySecureContentProvider {
private ContentProviderSecurityChecker securityChecker;
@CallSuper
@Override
public boolean onCreate() {
securityChecker = new ContentProviderSecurityChecker(this);
return false;
}
} So.. I think that we don't need to go through all that trouble just so that we don't have to pas in a reference to the ContentProvider when calling |
||
|
||
@Override | ||
public final @Nullable Cursor query( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the |
||
@NotNull Uri uri, | ||
@Nullable String[] strings, | ||
@Nullable String s, | ||
@Nullable String[] strings1, | ||
@Nullable String s1) { | ||
securityChecker.checkPrivilegeEscalation(this); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that there is no need to add this check to other overridable See my comment in my previous PR; #2466 (comment) |
||
return null; | ||
} | ||
|
||
@Override | ||
public final @Nullable Uri insert(@NotNull Uri uri, @Nullable ContentValues contentValues) { | ||
securityChecker.checkPrivilegeEscalation(this); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that there is no need to add this check to other overridable See my similar comment in my previous PR; #2466 (comment) |
||
return null; | ||
} | ||
|
||
@Override | ||
public final int delete(@NotNull Uri uri, @Nullable String s, @Nullable String[] strings) { | ||
securityChecker.checkPrivilegeEscalation(this); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that there is no need to add this check to other overridable See my similar comment in my previous PR; #2466 (comment) |
||
return 0; | ||
} | ||
|
||
@Override | ||
public final int update( | ||
@NotNull Uri uri, | ||
@Nullable ContentValues contentValues, | ||
@Nullable String s, | ||
@Nullable String[] strings) { | ||
securityChecker.checkPrivilegeEscalation(this); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that there is no need to add this check to other overridable See my similar comment in my previous PR; #2466 (comment) |
||
return 0; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,16 @@ | ||
package io.sentry.android.core; | ||
|
||
import android.content.ContentProvider; | ||
import android.content.ContentValues; | ||
import android.content.Context; | ||
import android.content.pm.ProviderInfo; | ||
import android.database.Cursor; | ||
import android.net.Uri; | ||
import io.sentry.Sentry; | ||
import io.sentry.SentryLevel; | ||
import io.sentry.android.core.internal.util.ContentProviderSecurityChecker; | ||
import org.jetbrains.annotations.ApiStatus; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
@ApiStatus.Internal | ||
public final class SentryInitProvider extends ContentProvider { | ||
public final class SentryInitProvider extends EmptySecureContentProvider { | ||
|
||
@Override | ||
public boolean onCreate() { | ||
|
@@ -45,38 +41,8 @@ public void attachInfo(@NotNull Context context, @NotNull ProviderInfo info) { | |
super.attachInfo(context, info); | ||
} | ||
|
||
@Override | ||
public @Nullable Cursor query( | ||
@NotNull Uri uri, | ||
@Nullable String[] strings, | ||
@Nullable String s, | ||
@Nullable String[] strings1, | ||
@Nullable String s1) { | ||
new ContentProviderSecurityChecker().checkPrivilegeEscalation(this); | ||
return null; | ||
} | ||
|
||
@Override | ||
public @Nullable String getType(@NotNull Uri uri) { | ||
return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense to validate the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. According to the ContentProvider.getType, documentation...
There is no "privilege escalation" exploit here because there is no privilege required to use this function at all. This is public function that is able to be used without any permissions even if not exported. I would really like to keep my code changes minimal so as to minimize the risk of regression 🙏 |
||
} | ||
|
||
@Override | ||
public @Nullable Uri insert(@NotNull Uri uri, @Nullable ContentValues contentValues) { | ||
return null; | ||
} | ||
|
||
@Override | ||
public int delete(@NotNull Uri uri, @Nullable String s, @Nullable String[] strings) { | ||
return 0; | ||
} | ||
|
||
@Override | ||
public int update( | ||
@NotNull Uri uri, | ||
@Nullable ContentValues contentValues, | ||
@Nullable String s, | ||
@Nullable String[] strings) { | ||
return 0; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,6 @@ | |
|
||
import android.annotation.SuppressLint; | ||
import android.content.ContentProvider; | ||
import android.net.Uri; | ||
import android.os.Build; | ||
import io.sentry.NoOpLogger; | ||
import io.sentry.android.core.BuildInfoProvider; | ||
|
@@ -30,20 +29,21 @@ public ContentProviderSecurityChecker(final @NotNull BuildInfoProvider buildInfo | |
* <p>See https://www.cvedetails.com/cve/CVE-2018-9492/ and | ||
* https://github.com/getsentry/sentry-java/issues/2460 | ||
* | ||
* <p>Call this function in the {@link ContentProvider#query(Uri, String[], String, String[], | ||
* String)} function. | ||
* <p>Call this function in the {@link ContentProvider}'s implementations of the abstract | ||
* functions; query, insert, update, and delete. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated documentation. |
||
* | ||
* <p>This should be invoked regardless of whether there is data to query or not. The attack is | ||
* not contained to the specific provider but rather the entire system. | ||
* <p>This should be invoked regardless of whether there is data to read/write or not. The attack | ||
* is not contained to the specific provider but rather the entire system. | ||
* | ||
* <p>This blocks the attacker by only allowing the app itself (not other apps) to "query" the | ||
* provider. | ||
* <p>This blocks the attacker by only allowing the app itself (not other apps) to interact with | ||
* the ContentProvider. If the ContentProvider needs to be able to interact with other trusted | ||
* apps, then this function or class should be refactored to accommodate that. | ||
* | ||
* <p>The vulnerability is specific to un-patched versions of Android 8 and 9 (API 26 to 28). | ||
* Therefore, this security check is limited to those versions to mitigate risk of regression. | ||
*/ | ||
@SuppressLint("NewApi") | ||
public void checkPrivilegeEscalation(ContentProvider contentProvider) { | ||
public void checkPrivilegeEscalation(@NotNull ContentProvider contentProvider) { | ||
final int sdkVersion = buildInfoProvider.getSdkInfoVersion(); | ||
if (sdkVersion >= Build.VERSION_CODES.O && sdkVersion <= Build.VERSION_CODES.P) { | ||
|
||
|
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.
This PR comes with this additional benefit of making it clear to contributors and consumers that Sentry does not actually have any "real" ContentProviders =)