
CVE-2026-0073 एक RCE है जिसका CVSS गंभीरता स्कोर 8.3 है, और यहाँ हम समझाएँगे कि यह कैसे काम करता है।
CVE-2026-0073 एक RCE है जिसका CVSS गंभीरता स्कोर 8.3 है, और यहाँ हम समझाएंगे कि यह कैसे काम करता है।
सबसे पहले, एकमात्र चीज़ जो संभवतः दिलचस्प हो सकती है, वह है बग के बारे में अधिक जानना।
मेरा पहला स्रोत है : https://www.tenable.com/cve/CVE-2026-0073
यह स्रोत हमें बताता है कि auth.cpp फ़ाइल में adbd_tls_verify_cert नामक एक फ़ंक्शन है जिसमें एक फ्लो है जो एक अनप्रमाणित RCE की अनुमति देता है।
अब मुख्य उद्देश्य auth.cpp का स्रोत कोड प्राप्त करना है।
चूंकि मैं इसे किसी आधिकारिक रिपॉजिटरी में नहीं ढूंढ पाया, मुझे Project-Awaken द्वारा बनाई गई यह रिपॉजिटरी मिली : https://github.com/Project-Awaken/android_packages_modules_adb/blob/12/daemon/auth.cpp
और वहाँ है! हमारा रहस्यमय फ़ंक्शन :
int adbd_tls_verify_cert(X509_STORE_CTX* ctx, std::string* auth_key) {
if (!auth_required) {
// Any key will do.
LOG(INFO) << __func__ << ": auth not required";
return 1;
}
bool authorized = false;
X509* cert = X509_STORE_CTX_get0_cert(ctx);
if (cert == nullptr) {
LOG(INFO) << "got null x509 certificate";
return 0;
}
bssl::UniquePtr<EVP_PKEY> evp_pkey(X509_get_pubkey(cert));
if (evp_pkey == nullptr) {
LOG(INFO) << "got null evp_pkey from x509 certificate";
return 0;
}
IteratePublicKeys([&](std::string_view public_key) {
// TODO: do we really have to support both ' ' and '\t'?
std::vector<std::string> split = android::base::Split(std::string(public_key), " \t");
uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
const std::string& pubkey = split[0];
if (b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
LOG(ERROR) << "Invalid base64 key " << pubkey;
return true;
}
RSA* key = nullptr;
if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) {
LOG(ERROR) << "Failed to parse key " << pubkey;
return true;
}
bool verified = false;
bssl::UniquePtr<EVP_PKEY> known_evp(EVP_PKEY_new());
EVP_PKEY_set1_RSA(known_evp.get(), key);
if (EVP_PKEY_cmp(known_evp.get(), evp_pkey.get())) {
LOG(INFO) << "Matched auth_key=" << public_key;
verified = true;
} else {
LOG(INFO) << "auth_key doesn't match [" << public_key << "]";
}
RSA_free(key);
if (verified) {
*auth_key = public_key;
authorized = true;
return false;
}
return true;
});
return authorized ? 1 : 0;
}
लेकिन फ़ंक्शन का दिलचस्प हिस्सा निम्नलिखित है :
bool verified = false;
bssl::UniquePtr<EVP_PKEY> known_evp(EVP_PKEY_new());
EVP_PKEY_set1_RSA(known_evp.get(), key);
if (EVP_PKEY_cmp(known_evp.get(), evp_pkey.get())) {
LOG(INFO) << "Matched auth_key=" << public_key;
verified = true;
} else {
LOG(INFO) << "auth_key doesn't match [" << public_key << "]";
}
क्यों? क्योंकि verified एक बूलियन के रूप में सेट है जो कनेक्शन की अनुमति देता है या नहीं, और इसे केवल "if" स्टेटमेंट के साथ बदला जाता है। समस्या? क्योंकि EVP_PKEY_cmp(known_evp.get(), evp_pkey.get()) हमेशा 1 या 0 नहीं लौटाता, बल्कि -1 और -2 भी लौटाता है क्योंकि EVP_PKEY_cmp() 1 लौटाता है यदि कुंजियाँ मेल खाती हैं, 0 यदि वे मेल नहीं खातीं, -1 यदि कुंजी प्रकार अलग हैं और -2 यदि ऑपरेशन समर्थित नहीं है।
इसलिए यह विभिन्न कुंजियों के प्रमाणपत्रों को मान्य करता है या यदि ऑपरेशन समर्थित नहीं है, भले ही कुंजी मेल न खाती हो।
अपने सत्र को मान्य करने के लिए हमें इन दो विकल्पों में से एक को मजबूर करना होगा।
इस दोष का शोषण करने का प्रयास करने के लिए, हमें एक TLS सत्र शुरू करना होगा।
यह केवल तभी काम करता है जब हमलावर और लक्षित डिवाइस दोनों TLS का उपयोग करने में सक्षम हों, हालाँकि TLS का उपयोग करने का निर्णय केवल लक्षित डिवाइस द्वारा लिया जाता है।
यदि लक्षित डिवाइस TLS की अनुमति देता है, तो आप एक TLS सत्र में शामिल हो सकते हैं।
यह सब यहाँ होता है :
आइए इसे काम में लाने का प्रयास करें