OWASP বিভাগ: MASVS-PLATFORM: প্ল্যাটফর্ম মিথস্ক্রিয়া
সংক্ষিপ্ত বিবরণ
ডকুমেন্টেশন অনুসারে, ContentResolver হলো এমন একটি ক্লাস যা অ্যাপ্লিকেশনগুলোকে কন্টেন্ট মডেলে অ্যাক্সেস প্রদান করে। ContentResolver-গুলো নিম্নলিখিত উৎস থেকে প্রাপ্ত কন্টেন্টের সাথে ইন্টারঅ্যাক্ট, ফেচ বা মডিফাই করার জন্য মেথড সরবরাহ করে:
- ইনস্টল করা অ্যাপস (
content://URI স্কিম) - ফাইল সিস্টেম (
file://ইউআরআই স্কিম) - অ্যান্ড্রয়েড কর্তৃক প্রদত্ত সমর্থিত এপিআই (
android.resource://ইউআরআই স্কিম)।
সংক্ষেপে, ContentResolver সম্পর্কিত দুর্বলতাগুলো ‘বিভ্রান্ত ডেপুটি’ শ্রেণীর অন্তর্ভুক্ত, কারণ আক্রমণকারী একটি দুর্বল অ্যাপ্লিকেশনের বিশেষাধিকার ব্যবহার করে সুরক্ষিত কন্টেন্ট অ্যাক্সেস করতে পারে।
ঝুঁকি: অবিশ্বস্ত file:// URI-এর উপর ভিত্তি করে অপব্যবহার
file:// ` URI ব্যবহার করে ContentResolver এর অপব্যবহার দুর্বলতাটি, URI দ্বারা বর্ণিত ফাইল ডেসক্রিপ্টর ফেরত দেওয়ার ContentResolver এর ক্ষমতাকে কাজে লাগায়। এই দুর্বলতাটি ContentResolver API- এর openFile() , openFileDescriptor() , openInputStream() , openOutputStream() , বা openAssetFileDescriptor() এর মতো ফাংশনগুলোকে প্রভাবিত করে। সম্পূর্ণ বা আংশিকভাবে আক্রমণকারীর নিয়ন্ত্রণে থাকা file:// URI ব্যবহার করে এই দুর্বলতার অপব্যবহার করা যেতে পারে, যার মাধ্যমে অ্যাপ্লিকেশনটিকে এমন সব ফাইল অ্যাক্সেস করতে বাধ্য করা হয় যেগুলো অ্যাক্সেসযোগ্য হওয়ার কথা ছিল না, যেমন—অভ্যন্তরীণ ডেটাবেস বা শেয়ার্ড প্রেফারেন্স।
সম্ভাব্য আক্রমণ পরিস্থিতিগুলোর মধ্যে একটি হলো এমন একটি ক্ষতিকারক গ্যালারি বা ফাইল পিকার তৈরি করা, যা কোনো দুর্বল অ্যাপ দ্বারা ব্যবহৃত হলে একটি ক্ষতিকারক URI ফেরত দেবে।
এই আক্রমণের কয়েকটি প্রকারভেদ রয়েছে:
- সম্পূর্ণরূপে আক্রমণকারী-নিয়ন্ত্রিত
file://URI যা একটি অ্যাপের অভ্যন্তরীণ ফাইলগুলিকে নির্দেশ করে। -
file://URI-এর একটি অংশ আক্রমণকারীর নিয়ন্ত্রণে থাকে, ফলে এটি পাথ ট্র্যাভার্সালের জন্য ঝুঁকিপূর্ণ। -
file://URI, যা আক্রমণকারী-নিয়ন্ত্রিত একটি সিম্বলিক লিঙ্ক (symlink)-কে লক্ষ্য করে এবং অ্যাপটির অভ্যন্তরীণ ফাইলগুলিতে নির্দেশ করে। - পূর্ববর্তী ভ্যারিয়েন্টের মতোই, কিন্তু এখানে আক্রমণকারী বারবার সিমলিংকের টার্গেটকে একটি বৈধ টার্গেট থেকে অ্যাপের অভ্যন্তরীণ ফাইলে পরিবর্তন করে। এর লক্ষ্য হলো একটি সম্ভাব্য নিরাপত্তা যাচাই এবং ফাইল পাথ ব্যবহারের মধ্যেকার রেস কন্ডিশনের সুযোগ নেওয়া।
প্রভাব
ContentResolver-টি কী কাজে ব্যবহার করা হচ্ছে, তার ওপর নির্ভর করে এই দুর্বলতার অপব্যবহারের প্রভাব ভিন্ন ভিন্ন হয়। অনেক ক্ষেত্রে, এর ফলে কোনো অ্যাপের সুরক্ষিত ডেটা পাচার হয়ে যেতে পারে অথবা অননুমোদিত পক্ষ দ্বারা সুরক্ষিত ডেটাতে পরিবর্তন আনা হতে পারে।
প্রশমন
এই দুর্বলতা প্রশমিত করতে, ফাইল ডেসক্রিপ্টরটি যাচাই করার জন্য নিচের অ্যালগরিদমটি ব্যবহার করুন। যাচাইকরণ সফলভাবে সম্পন্ন হলে, ফাইল ডেসক্রিপ্টরটি নিরাপদে ব্যবহার করা যাবে।
কোটলিন
fun isValidFile(ctx: Context, pfd: ParcelFileDescriptor, fileUri: Uri): Boolean {
// Canonicalize to resolve symlinks and path traversals.
val fdCanonical = File(fileUri.path!!).canonicalPath
val pfdStat: StructStat = Os.fstat(pfd.fileDescriptor)
// Lstat doesn't follow the symlink.
val canonicalFileStat: StructStat = Os.lstat(fdCanonical)
// Since we canonicalized (followed the links) the path already,
// the path shouldn't point to symlink unless it was changed in the
// meantime.
if (OsConstants.S_ISLNK(canonicalFileStat.st_mode)) {
return false
}
val sameFile =
pfdStat.st_dev == canonicalFileStat.st_dev &&
pfdStat.st_ino == canonicalFileStat.st_ino
if (!sameFile) {
return false
}
return !isBlockedPath(ctx, fdCanonical)
}
fun isBlockedPath(ctx: Context, fdCanonical: String): Boolean {
// Paths that should rarely be exposed
if (fdCanonical.startsWith("/proc/") ||
fdCanonical.startsWith("/data/misc/")) {
return true
}
// Implement logic to block desired directories. For example, specify
// the entire app data/ directory to block all access.
}
জাভা
boolean isValidFile(Context ctx, ParcelFileDescriptor pfd, Uri fileUri) {
// Canonicalize to resolve symlinks and path traversals
String fdCanonical = new File(fileUri.getPath()).getCanonicalPath();
StructStat pfdStat = Os.fstat(pfd.getFileDescriptor());
// Lstat doesn't follow the symlink.
StructStat canonicalFileStat = Os.lstat(fdCanonical);
// Since we canonicalized (followed the links) the path already,
// the path shouldn't point to symlink unless it was changed in the meantime
if (OsConstants.S_ISLNK(canonicalFileStat.st_mode)) {
return false;
}
boolean sameFile =
pfdStat.stDev == canonicalFileStat.stDev && pfdStat.stIno == canonicalFileStat.stIno;
if (!sameFile) {
return false;
}
return !isBlockedPath(ctx, fdCanonical);
}
boolean isBlockedPath(Context ctx, String fdCanonical) {
// Paths that should rarely be exposed
if (fdCanonical.startsWith("/proc/") || fdCanonical.startsWith("/data/misc/")) {
return true;
}
// Implement logic to block desired directories. For example, specify
// the entire app data/ directory to block all access.
}
ঝুঁকি: অবিশ্বস্ত কন্টেন্ট:// URI-এর উপর ভিত্তি করে অপব্যবহার
` content:// ` URI ব্যবহার করে ContentResolver এর অপব্যবহার তখন ঘটে, যখন আক্রমণকারীর সম্পূর্ণ বা আংশিকভাবে নিয়ন্ত্রিত কোনো URI, এমন কোনো কন্টেন্টের ওপর কাজ করার জন্য ContentResolver API-তে পাঠানো হয়, যা অ্যাক্সেসযোগ্য হওয়ার জন্য উদ্দিষ্ট ছিল না।
এই আক্রমণের দুটি প্রধান পরিস্থিতি রয়েছে:
- অ্যাপটি তার নিজস্ব অভ্যন্তরীণ কন্টেন্টের ওপর ভিত্তি করে কাজ করে। উদাহরণস্বরূপ: কোনো আক্রমণকারীর কাছ থেকে একটি URI পাওয়ার পর, মেইল অ্যাপটি কোনো বাহ্যিক ছবির পরিবর্তে তার নিজস্ব অভ্যন্তরীণ কন্টেন্ট প্রোভাইডার থেকে ডেটা সংযুক্ত করে।
- অ্যাপটি একটি প্রক্সি হিসেবে কাজ করে এবং এরপর আক্রমণকারীর জন্য অন্য কোনো অ্যাপ্লিকেশনের ডেটা অ্যাক্সেস করে। উদাহরণস্বরূপ: মেইল অ্যাপ্লিকেশনটি অ্যাপ X থেকে এমন ডেটা সংযুক্ত করে যা এমন একটি পারমিশন দ্বারা সুরক্ষিত, যা সাধারণত আক্রমণকারীকে সেই নির্দিষ্ট অ্যাটাচমেন্টটি দেখতে দেয় না। এটি অ্যাটাচমেন্টকারী অ্যাপ্লিকেশনটির কাছে উপলব্ধ থাকে, কিন্তু প্রাথমিকভাবে নয়, ফলে এই কন্টেন্টটি আক্রমণকারীর কাছে পৌঁছে যায়।
একটি সম্ভাব্য আক্রমণের কৌশল হলো এমন একটি ক্ষতিকারক গ্যালারি বা ফাইল পিকার তৈরি করা, যা কোনো দুর্বল অ্যাপ দ্বারা ব্যবহৃত হলে একটি ক্ষতিকারক URI ফেরত দেবে।
প্রভাব
ContentResolver-এর সাথে সংশ্লিষ্ট প্রেক্ষাপটের উপর নির্ভর করে এই দুর্বলতা কাজে লাগানোর প্রভাব ভিন্ন ভিন্ন হয়। এর ফলে কোনো অ্যাপের সুরক্ষিত ডেটা পাচার হয়ে যেতে পারে অথবা অননুমোদিত পক্ষ দ্বারা সুরক্ষিত ডেটাতে পরিবর্তন আনা হতে পারে।
প্রশমন
সাধারণ
আগত URI-গুলো যাচাই করুন। উদাহরণস্বরূপ, প্রত্যাশিত কর্তৃপক্ষের একটি অনুমোদিত তালিকা (allowlist) ব্যবহার করা একটি উত্তম অনুশীলন হিসেবে বিবেচিত হয়।
URI এমন একটি নন-এক্সপোর্টেড বা অনুমতি-সুরক্ষিত কন্টেন্ট প্রোভাইডারকে টার্গেট করে যা ঝুঁকিপূর্ণ অ্যাপের অন্তর্গত।
যাচাই করুন, URI-টি আপনার অ্যাপকে লক্ষ্য করে কিনা:
কোটলিন
fun belongsToCurrentApplication(ctx: Context, uri: Uri): Boolean {
val authority: String = uri.authority.toString()
val info: ProviderInfo =
ctx.packageManager.resolveContentProvider(authority, 0)!!
return ctx.packageName.equals(info.packageName)
}
জাভা
boolean belongsToCurrentApplication(Context ctx, Uri uri){
String authority = uri.getAuthority();
ProviderInfo info = ctx.getPackageManager().resolveContentProvider(authority, 0);
return ctx.getPackageName().equals(info.packageName);
}
অথবা যদি লক্ষ্যযুক্ত প্রদানকারী রপ্তানি করা হয়:
কোটলিন
fun isExported(ctx: Context, uri: Uri): Boolean {
val authority = uri.authority.toString()
val info: ProviderInfo =
ctx.packageManager.resolveContentProvider(authority, 0)!!
return info.exported
}
জাভা
boolean isExported(Context ctx, Uri uri){
String authority = uri.getAuthority();
ProviderInfo info = ctx.getPackageManager().resolveContentProvider(authority, 0);
return info.exported;
}
অথবা যদি URI-টিতে সুস্পষ্ট অনুমতি দেওয়া হয় - এই যাচাইটি এই অনুমানের উপর ভিত্তি করে করা হয় যে, ডেটা অ্যাক্সেস করার জন্য সুস্পষ্ট অনুমতি দেওয়া হলে URI-টি ক্ষতিকর নয়:
কোটলিন
// grantFlag is one of: FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION
fun wasGrantedPermission(ctx: Context, uri: Uri?, grantFlag: Int): Boolean {
val pid: Int = Process.myPid()
val uid: Int = Process.myUid()
return ctx.checkUriPermission(uri, pid, uid, grantFlag) ==
PackageManager.PERMISSION_GRANTED
}
জাভা
// grantFlag is one of: FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION
boolean wasGrantedPermission(Context ctx, Uri uri, int grantFlag){
int pid = Process.myPid();
int uid = Process.myUid();
return ctx.checkUriPermission(uri, pid, uid, grantFlag) == PackageManager.PERMISSION_GRANTED;
}
URI-টি একটি অনুমতি-সুরক্ষিত ContentProvider-কে লক্ষ্য করে, যা অন্য একটি অ্যাপের অন্তর্গত এবং সেই অ্যাপটি দুর্বল অ্যাপটিকে বিশ্বাস করে।
এই আক্রমণটি নিম্নলিখিত পরিস্থিতিগুলির ক্ষেত্রে প্রাসঙ্গিক:
- অ্যাপ্লিকেশনগুলির এমন ইকোসিস্টেম যেখানে অ্যাপগুলি নিজস্ব অনুমতি বা অন্যান্য প্রমাণীকরণ পদ্ধতি নির্ধারণ ও ব্যবহার করে।
- পারমিশন প্রক্সি অ্যাটাক হলো এমন একটি পদ্ধতি, যেখানে আক্রমণকারী READ_CONTACTS-এর মতো কোনো রানটাইম পারমিশন ধারণকারী একটি দুর্বল অ্যাপের অপব্যবহার করে সিস্টেম প্রোভাইডার থেকে ডেটা পুনরুদ্ধার করে।
URI-এর অনুমতি দেওয়া হয়েছে কিনা তা পরীক্ষা করুন:
কোটলিন
// grantFlag is one of: FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION
fun wasGrantedPermission(ctx: Context, uri: Uri?, grantFlag: Int): Boolean {
val pid: Int = Process.myPid()
val uid: Int = Process.myUid()
return ctx.checkUriPermission(uri, pid, uid, grantFlag) ==
PackageManager.PERMISSION_GRANTED
}
জাভা
// grantFlag is one of: FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION
boolean wasGrantedPermission(Context ctx, Uri uri, int grantFlag){
int pid = Process.myPid();
int uid = Process.myUid();
return ctx.checkUriPermission(uri, pid, uid, grantFlag) == PackageManager.PERMISSION_GRANTED;
}
যদি অন্যান্য কন্টেন্ট প্রোভাইডার ব্যবহারের জন্য অনুমতির প্রয়োজন না হয় — যেমন যখন অ্যাপটি ইকোসিস্টেমের সমস্ত অ্যাপকে সমস্ত ডেটা অ্যাক্সেস করার অনুমতি দেয় — তাহলে স্পষ্টভাবে এই ক্ষমতাগুলোর ব্যবহার নিষিদ্ধ করুন।