OWASP বিভাগ: MASVS-প্ল্যাটফর্ম: প্ল্যাটফর্ম ইন্টারঅ্যাকশন
ওভারভিউ
ডকুমেন্টেশন অনুযায়ী, ContentResolver
হল একটি "শ্রেণী যা অ্যাপ্লিকেশনগুলিকে বিষয়বস্তু মডেলে অ্যাক্সেস প্রদান করে" । ContentResolvers নিম্নলিখিত থেকে প্রদত্ত বিষয়বস্তু ইন্টারঅ্যাক্ট, আনয়ন বা সংশোধন করার পদ্ধতি প্রকাশ করে:
- ইনস্টল করা অ্যাপ (
content://
URI স্কিম) - ফাইল সিস্টেম (
file://
URI স্কিম) - অ্যান্ড্রয়েড (
android.resource://
URI স্কিম) দ্বারা প্রদত্ত সমর্থনকারী API
সংক্ষেপে বলতে গেলে, ContentResolver
এর সাথে সম্পর্কিত দুর্বলতাগুলি বিভ্রান্ত ডেপুটি ক্লাসের অন্তর্গত কারণ আক্রমণকারী সুরক্ষিত সামগ্রী অ্যাক্সেস করার জন্য একটি দুর্বল অ্যাপ্লিকেশনের সুবিধাগুলি ব্যবহার করতে পারে৷
ঝুঁকি: অবিশ্বস্ত ফাইল:// URI-এর উপর ভিত্তি করে অপব্যবহার
file://
URI দুর্বলতা ব্যবহার করে ContentResolver
এর অপব্যবহার URI দ্বারা বর্ণিত ফাইল বর্ণনাকারী ফেরত দেওয়ার জন্য ContentResolver
এর ক্ষমতাকে কাজে লাগায়। এই দুর্বলতা ContentResolver
API থেকে openFile()
, openFileDescriptor()
, openInputStream()
, openOutputStream()
, বা openAssetFileDescriptor()
এর মতো ফাংশনগুলিকে প্রভাবিত করে৷ একটি সম্পূর্ণ বা আংশিকভাবে আক্রমণকারী-নিয়ন্ত্রিত file://
URI দিয়ে দুর্বলতার অপব্যবহার করা যেতে পারে যাতে অভ্যন্তরীণ ডেটাবেস বা ভাগ করা পছন্দের মতো অ্যাক্সেসযোগ্য নয় এমন ফাইলগুলি অ্যাক্সেস করতে অ্যাপ্লিকেশনটিকে বাধ্য করে৷
সম্ভাব্য আক্রমণ পরিস্থিতিগুলির মধ্যে একটি হ'ল একটি দূষিত গ্যালারি বা ফাইল পিকার তৈরি করা যা একটি দুর্বল অ্যাপ ব্যবহার করলে, একটি দূষিত URI ফিরিয়ে দেবে।
এই আক্রমণের কয়েকটি রূপ রয়েছে:
- সম্পূর্ণরূপে আক্রমণকারী-নিয়ন্ত্রিত
file://
URI যা একটি অ্যাপের অভ্যন্তরীণ ফাইলগুলির দিকে নির্দেশ করে৷ -
file://
URI আক্রমণকারী-নিয়ন্ত্রিত, এটি পাথ ট্রাভার্সালের জন্য প্রবণ করে তোলে -
file://
URI একটি আক্রমণকারী-নিয়ন্ত্রিত প্রতীকী লিঙ্ক (symlink) লক্ষ্য করে যা অ্যাপের অভ্যন্তরীণ ফাইলগুলিকে নির্দেশ করে - পূর্ববর্তী ভেরিয়েন্টের মতই, কিন্তু এখানে আক্রমণকারী বারবার সিমলিংক লক্ষ্যকে একটি বৈধ লক্ষ্য থেকে একটি অ্যাপের অভ্যন্তরীণ ফাইলগুলিতে অদলবদল করে। লক্ষ্য একটি সম্ভাব্য নিরাপত্তা চেক এবং ফাইল পাথ ব্যবহারের মধ্যে একটি রেস অবস্থা শোষণ করা হয়
প্রভাব
এই দুর্বলতাকে কাজে লাগানোর প্রভাব কনটেন্ট রিসোলভার কিসের জন্য ব্যবহার করা হয় তার উপর নির্ভর করে। অনেক ক্ষেত্রে, এর ফলে একটি অ্যাপের সুরক্ষিত ডেটা অপসারিত হতে পারে বা অননুমোদিত পক্ষগুলির দ্বারা সুরক্ষিত ডেটার পরিবর্তন হতে পারে।
প্রশমন
এই দুর্বলতা প্রশমিত করতে, ফাইল বর্ণনাকারীকে যাচাই করতে নীচের অ্যালগরিদমটি ব্যবহার করুন৷ বৈধতা পাস করার পরে, ফাইল বর্ণনাকারী নিরাপদে ব্যবহার করা যেতে পারে।
কোটলিন
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
এপিআই-এর কাছে পাঠানো হয় যা অ্যাক্সেসযোগ্য হওয়ার উদ্দেশ্যে ছিল না।
এই আক্রমণের জন্য দুটি প্রধান পরিস্থিতি রয়েছে:
- অ্যাপটি তার নিজস্ব, অভ্যন্তরীণ সামগ্রীতে কাজ করে। উদাহরণস্বরূপ: আক্রমণকারীর কাছ থেকে একটি URI পাওয়ার পরে, মেল অ্যাপটি একটি বহিরাগত ছবির পরিবর্তে তার নিজস্ব অভ্যন্তরীণ সামগ্রী প্রদানকারীর থেকে ডেটা সংযুক্ত করে৷
- অ্যাপটি প্রক্সি হিসেবে কাজ করে এবং তারপর আক্রমণকারীর জন্য অন্য অ্যাপ্লিকেশনের ডেটা অ্যাক্সেস করে। উদাহরণস্বরূপ: মেল অ্যাপ্লিকেশনটি অ্যাপ X থেকে ডেটা সংযুক্ত করে যা একটি অনুমতি দ্বারা সুরক্ষিত যা সাধারণত আক্রমণকারীকে সেই নির্দিষ্ট সংযুক্তিটি দেখতে অস্বীকার করে। এটা অ্যাটাচমেন্ট করা অ্যাপ্লিকেশানের কাছে উপলব্ধ, কিন্তু প্রাথমিকভাবে এইভাবে আক্রমণকারীর কাছে এই বিষয়বস্তু রিলে করে না৷
একটি সম্ভাব্য আক্রমণের দৃশ্য হল একটি দূষিত গ্যালারি বা ফাইল পিকার তৈরি করা যা, একটি দুর্বল অ্যাপ ব্যবহার করলে, একটি দূষিত URI ফিরিয়ে দেবে।
প্রভাব
এই দুর্বলতাকে কাজে লাগানোর প্রভাব ContentResolver-এর সাথে সম্পর্কিত প্রসঙ্গের উপর নির্ভর করে পরিবর্তিত হয়। এর ফলে একটি অ্যাপের সুরক্ষিত ডেটা অপসারিত হতে পারে বা অননুমোদিত পক্ষগুলি দ্বারা সুরক্ষিত ডেটাতে পরিবর্তন হতে পারে।
প্রশমন
সাধারণ
ইনকামিং ইউআরআই যাচাই করুন। উদাহরণস্বরূপ, প্রত্যাশিত কর্তৃপক্ষের একটি অনুমোদিত তালিকা ব্যবহার করা ভাল অনুশীলন হিসাবে বিবেচিত হয়।
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 একটি অনুমতি-সুরক্ষিত সামগ্রী প্রদানকারীকে লক্ষ্য করে যা অন্য অ্যাপের অন্তর্গত যা দুর্বল অ্যাপটিকে বিশ্বাস করে।
এই আক্রমণ নিম্নলিখিত পরিস্থিতিতে প্রাসঙ্গিক:
- অ্যাপ্লিকেশনগুলির ইকোসিস্টেম যেখানে অ্যাপগুলি কাস্টম অনুমতি বা অন্যান্য প্রমাণীকরণ প্রক্রিয়া সংজ্ঞায়িত করে এবং ব্যবহার করে।
- অনুমতি প্রক্সি আক্রমণ, যেখানে একজন আক্রমণকারী একটি দুর্বল অ্যাপের অপব্যবহার করে যা রানটাইম অনুমতি ধারণ করে, যেমন 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;
}
যদি অন্যান্য বিষয়বস্তু প্রদানকারীদের ব্যবহারের জন্য অনুমতি অনুদানের প্রয়োজন না হয় - যেমন যখন অ্যাপটি ইকোসিস্টেম থেকে সমস্ত অ্যাপকে সমস্ত ডেটা অ্যাক্সেস করার অনুমতি দেয় - তাহলে স্পষ্টভাবে এই কর্তৃপক্ষের ব্যবহার নিষিদ্ধ করুন।