معلومات التحدي

هذا التحدي مصمم لمتخصصي الأمن السيبراني الذين يرغبون في تطوير مهاراتهم في تحليل وتأمين تطبيقات Android. ستتعلم من خلاله تقنيات متقدمة لاكتشاف الثغرات الأمنية وكيفية معالجتها.

المستوى

متوسط

المدة المتوقعة

1-2 ساعات

الأدوات المطلوبة

Android Studio, ADB, Burp Suite, Frida

النقاط

150 نقطة

سيناريو التحدي

أنت محلل أمني في شركة تقنية، وقد طُلِب منك تحليل تطبيق Android خاص بالخدمات المصرفية للكشف عن أي ثغرات أمنية محتملة. التطبيق يحتوي على عدة نقاط ضعف يجب اكتشافها وتقديم تقرير عنها.

التحدي الأول: ثغرة تخزين البيانات غير الآمن

متوسط

اكتشف أن التطبيق يقوم بتخزين بيانات حساسة (كلمات المرور، tokens) في SharedPreferences وهي غير آمنة. حدد مكان التخزين غير الآمن واقترح حلاً مناسباً.

// جزء من كود التطبيق
public void saveCredentials(String username, String password) {
  SharedPreferences prefs = getSharedPreferences("user_data", MODE_PRIVATE);
  SharedPreferences.Editor editor = prefs.edit();
  editor.putString("username", username);
  editor.putString("password", password);
  editor.apply();
  Log.d("App", "تم حفظ بيانات الاعتماد");
}

تلميح

ابحث عن كيفية تخزين البيانات الحساسة في تطبيقات Android والأماكن الآمنة المناسبة لذلك.

الحل

المشكلة: التطبيق يستخدم SharedPreferences لتخزين بيانات حساسة وهي غير آمنة ويمكن الوصول إليها بسهولة على الأجهزة التي تمتلك صلاحية root.

الحل: استخدام Android Keystore System لتخزين البيانات الحساسة بشكل آمن:

public void saveCredentials(String username, String password) {
  try {
    KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
    keyStore.load(null);
    
    // إنشاء مفتاح تشفير إذا لم يكن موجوداً
    if (!keyStore.containsAlias("app_key")) {
      KeyGenerator keyGenerator = KeyGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
      KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
        "app_key", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
        .setRandomizedEncryptionRequired(true);
      keyGenerator.init(builder.build());
      keyGenerator.generateKey();
    }
    
    // تشفير البيانات قبل تخزينها
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    SecretKey key = (SecretKey) keyStore.getKey("app_key", null);
    cipher.init(Cipher.ENCRYPT_MODE, key);
    
    byte[] encryptedUsername = cipher.doFinal(username.getBytes());
    byte[] encryptedPassword = cipher.doFinal(password.getBytes());
    
    // تخزين البيانات المشفرة في SharedPreferences
    SharedPreferences prefs = getSharedPreferences("user_data", MODE_PRIVATE);
    SharedPreferences.Editor editor = prefs.edit();
    editor.putString("username", Base64.encodeToString(encryptedUsername, Base64.DEFAULT));
    editor.putString("password", Base64.encodeToString(encryptedPassword, Base64.DEFAULT));
    editor.putString("iv", Base64.encodeToString(cipher.getIV(), Base64.DEFAULT));
    editor.apply();
    
    Log.d("App", "تم حفظ بيانات الاعتماد بشكل آمن");
  } catch (Exception e) {
    Log.e("App", "خطأ في حفظ البيانات: " + e.getMessage());
  }
}

التحدي الثاني: ثغرة Certificate Pinning

صعب

اكتشف أن التطبيق لا يستخدم Certificate Pinning مما يجعله عرضة لهجمات Man-in-the-Middle. كيف يمكنك تنفيذ هذه الحماية؟

// جزء من كود الاتصال بالخادم
OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
  .url("https://api.bankapp.com/login")
  .build();

client.newCall(request).enqueue(new Callback() {
  @Override
  public void onResponse(Call call, Response response) throws IOException {
    // معالجة الاستجابة
  }
  
  @Override
  public void onFailure(Call call, IOException e) {
    // معالجة الفشل
  }
});

تلميح

ابحث عن تقنيات Certificate Pinning في Android وكيفية تنفيذها باستخدام OkHttp أو مكتبات أخرى.

الحل

المشكلة: التطبيق لا يستخدم Certificate Pinning مما يسمح لهجمات Man-in-the-Middle.

الحل: تنفيذ Certificate Pinning باستخدام OkHttp:

// إنشاء CertificatePinner مع بصمات الشهادات الموثوقة
CertificatePinner certificatePinner = new CertificatePinner.Builder()
  .add("api.bankapp.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
  .add("api.bankapp.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")
  .build();

OkHttpClient client = new OkHttpClient.Builder()
  .certificatePinner(certificatePinner)
  .build();

Request request = new Request.Builder()
  .url("https://api.bankapp.com/login")
  .build();

client.newCall(request).enqueue(new Callback() {
  @Override
  public void onResponse(Call call, Response response) throws IOException {
    // معالجة الاستجابة
  }
  
  @Override
  public void onFailure(Call call, IOException e) {
    // معالجة الفشل
  }
});

ملاحظة: يجب استبدال البصمات (sha256) ببصمات الشهادات الفعلية لخادمك.

التحدي الثالث: ثغرة Exporting Components

متوسط

اكتشف أن التطبيق يصدر Activities و Services بدون قيود مناسبة، مما يسمح للتطبيقات الأخرى بالوصول إليها. كيف يمكنك تأمين هذه المكونات؟

// ملف AndroidManifest.xml
<activity
  android:name=".LoginActivity"
  android:exported="true">
  <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="bankapp" />
  </intent-filter>
</activity>

<service
  android:name=".PaymentService"
  android:exported="true" />

تلميح

ابحث عن مفهوم exported components في Android وكيفية تأمينها باستخدام الأذونات والتحقق من التطبيق المستدعي.

الحل

المشكلة: المكونات مصدرة بدون قيود مما يسمح لأي تطبيق بالوصول إليها.

الحل: تأمين المكونات باستخدام الأذونات والتحقق من التطبيق المستدعي:

// ملف AndroidManifest.xml المعدل
<permission
  android:name="com.bankapp.permission.ACCESS_PAYMENT_SERVICE"
  android:protectionLevel="signature" />

<activity
  android:name=".LoginActivity"
  android:exported="true">
  <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="bankapp" />
  </intent-filter>
</activity>

<service
  android:name=".PaymentService"
  android:exported="true"
  android:permission="com.bankapp.permission.ACCESS_PAYMENT_SERVICE" />

مع إضافة التحقق البرمجي في المكونات:

// في onCreate للـ Activity أو onStartCommand للـ Service
if (getCallingPackage() != null && !isValidCaller(getCallingPackage())) {
  finish(); // أو stopSelf() للـ Service
  return;
}

private boolean isValidCaller(String packageName) {
  // التحقق من أن التطبيق المستدعي موثوق
  return "com.trusted.app".equals(packageName);
}