ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

NullBytes靶机通关:SQL注入与SUID提权实战记录

NullBytes靶机通关:SQL注入与SUID提权实战记录 看了一遍又一遍NullBytes 这台 VulnHub 靶机给我的感觉就是麻雀虽小五脏俱全。它不像 DC 系列那样动不动就要打域环境也不像那些动不动堆内核漏洞的靶机让人一脸懵它老老实实走的是“Web 注入 → 口令复用 → 本地提权”这条经典路线。对新手来说这条链路干净、清晰每一步都是可以单独拿出来反复练的基本功对准备 OSCP 或者已经在刷 PTE 的人来说NullBytes 又是一个特别好的“考试模拟器”因为它把最容易在真实渗透里犯的错误全部埋进去了比如密码复用、SUID 滥用、哈希识别。这篇文章是我自己从下载 VM 到拿到 root 的完整通关记录不是那种只贴结果的复盘而是把每一步怎么判断、为什么这么判断、踩了什么坑全部写清楚。文章以本地虚拟机环境的安全实验为背景所有操作都在 VulnHub 提供的靶机上进行适合刚入门想系统练一遍完整攻击链的朋友。1. 认识 NullBytes这靶机到底在考什么1.1 靶机基本信息与部署环境NullBytes 是 VulnHub 上由作者 Airtight 发布的一台 boot2root 类型靶机镜像格式是 ova下载后直接导入 VMware 或 VirtualBox 就能用。我这边使用的环境是 VMware Workstation攻击机是 Kali网络模式选的是 Host-Only。为什么选 Host-Only因为这种模式把靶机和攻击机放在一个完全隔离的内部网络里不会受到外部网络干扰也避免了 DHCP 分配出来的地址漂移问题。导入时有一个细节要注意ova 导入之后建议先检查一下虚拟机的网络适配器是不是“仅主机模式”。有些版本默认会给你 NAT而 NAT 模式下攻击机和靶机是否在同一网段是不一定的一旦不在一起后面所有扫描都会白做。我的习惯是导入后立刻在 Kali 里执行ip a看一眼本机 IP再决定要不要调整。另外启动这台靶机不要急它开机速度比一般 Ubuntu 虚拟机慢不少我第一次等了两分钟还没看到登录界面差点以为镜像坏了。后来发现只是正常的启动慢耐心等一下就好不用去按键盘任何键。1.2 通关链路全景先建模再动手打靶机最忌讳的是一上来就拿工具乱扫然后不知道下一步干嘛。正确做法是先建立一个全局模型这台机器现在对外暴露了什么我作为一个攻击者能从哪里切入拿到入口之后又该怎么扩大战果。NullBytes 的攻击链大概是这样一个走向网络发现通过 arp-scan 或 netdiscover 定位靶机 IP端口扫描nmap 确认开放的端口和服务Web 应用分析发现 SQL 注入提取数据库用户和密码认证突破用 Web 端拿到的密码尝试 SSH 登录权限提升利用 SUID 文件或 sudo 配置从普通用户提权到 root拿到最终 flag 完成通关这套链路不是 NullBytes 独有的而是几乎全部 Linux boot2root 靶机的通用模板。真正拉开差距的地方在于每一步做得到底扎不扎实端口扫描有没有漏掉 UDP 或高位端口Web 注入是直接用工具盲跑还是手工会更快拿到凭据之后有没有下意识去试服务复用。所以这篇文章我会花比较大篇幅写“为什么这样做”而不是单纯地把命令堆出来。2. 信息收集找到靶机并摸清它的底细2.1 网络发现三条路找到靶机 IP靶机开机后先从攻击机确认自己的内网地址。假设 Kali 的地址是 192.168.56.102/24那么我们需要在 192.168.56.0/24 这个网段里找到新出现的主机。我常用三个方法按效率排序# 方法一netdiscover主动扫描当前网段 sudo netdiscover -r 192.168.56.0/24 # 方法二arp-scan从 ARP 表里找活主机 sudo arp-scan -l # 方法三直接在 VMware 虚拟网络编辑器里看 DHCP 分配记录netdiscover 和 arp-scan 的输出里靶机一般会以一个新的 MAC 地址出现在列表里。这里有个小提示VMware 的虚拟机网卡 MAC 通常以00:0c:29开头看到这个前缀的新设备基本就是靶机了。如果网段里本来就有一堆虚拟机这个方法能帮你快速排除干扰项。实际扫描结果里我记下了靶机 IP192.168.56.105。这个地址后面所有步骤都会用到所以建议当场记下来或者像我一样直接把扫描结果保存到文件里。2.2 端口扫描先全量再精准确定 IP 后先做一次全端口 TCP 扫描不要一上来就用-sV精确识别因为版本探测对没开放的端口毫无意义只会白白浪费时间。nmap -sS -p- -T4 -oN full_scan.txt 192.168.56.105TCP SYN 扫描结果出来只有两个端口开放22/tcp SSH80/tcp HTTP看到这个结果的第一反应应该是攻击面很窄Web 服务几乎是唯一的突破口。于是再对这两个端口做版本识别和默认脚本扫描nmap -sV -sC -p 22,80 192.168.56.105扫描结果显示 80 端口上是 Apache PHP 环境22 端口是 OpenSSH。到这里脑子里应该已经有一个初步方向了Web 层大概率存在可挖掘的漏洞如果能拿到某些凭据还可以尝试 SSH 远程登录。2.3 目录枚举把表面的隐藏接口挖出来打开浏览器访问 http://192.168.56.105页面上是一个比较简洁的站点。这种静态页面对渗透测试来说价值有限真正有价值的是隐藏的后台和功能接口。所以下一步做目录枚举gobuster dir -u http://192.168.56.105 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt,bak加-x参数的目的是把常见的扩展名一起带上很多后台脚本是 index.php 这个形态不带php后缀扫描就漏了。扫描结果里出现了几个和后台、登录相关的路径。逐一访问之后我发现一个页面有用户查询功能另一个页面是登录入口。它们表面上功能不同但实际请求都指向同一个后台处理脚本差别只在于参数不同。到这一步信息收集阶段基本完成。接下来进入 Web 漏洞挖掘的重头戏。3. Web 漏洞挖掘SQL 注入从入门到拿下3.1 用 Burp 梳理请求特征信息收集结束下一步就是对 Web 功能点进行交互测试。先把 Burp Suite 打开把浏览器代理挂到 127.0.0.1:8080然后在那个用户查询页面随便输入一个名字点击查询。Burp 的 HTTP History 里能看到这次请求的完整格式POST /search.php HTTP/1.1 Host: 192.168.56.105 Content-Type: application/x-www-form-urlencoded usernameadminsubmit1这个username参数是用户输入点也是我们接下来要重点测试的目标。很多新手一上来就喜欢用 sqlmap 直接打我完全不反对但前提是你至少手工验证过一遍参数知道它大概是个什么情况。这样用工具时才不会因为工具输出太抽象而看不懂结论。3.2 手工注入验证从报错到布尔盲注我在 Burp 的 Repeater 里手工改了 username 参数做了一次最简单的注入测试usernameadmin -- -回显直接出现 SQL 语法错误并且报错内容里暴露了数据库查询语句的结构。这几乎可以断定参数存在 SQL 注入而且是有回显的报错型注入不需要靠猜。继续验证字段返回逻辑我使用usernameadmin and 11 -- - usernameadmin and 12 -- -两次回显结果明显不同第一次正常返回查询结果第二次返回空记录。这确认了存在布尔盲注我们可以通过条件为真或假时的页面差异来推断数据。再进一步用 order by 探测查询结果集的列数username-1 order by 3 -- - username-1 order by 4 -- -order by 4 时页面报错说明当前查询正好是 3 列。接着尝试联合查询username-1 union select 1,2,3 -- -回显中 2 和 3 的位置有内容输出这意味着我们完全可以在回显位填充要提取的数据。到这步SQL 注入的利用条件已经全部具备。3.3 用 sqlmap 快速提取数据库内容手工验证完毕接下来用 sqlmap 提升效率。指定注入点和数据库类型sqlmap -u http://192.168.56.105/search.php \ --datausernameadminsubmit1 \ --dbmsmysql --dbs --batchsqlmap 跑了一会儿成功枚举出两个数据库information_schema和nullbytes。前者是 MySQL 自带的元数据库不用管后者明显是目标业务库。继续 dump nullbytes 库里的内容sqlmap -u http://192.168.56.105/search.php \ --datausernameadminsubmit1 \ -D nullbytes --dump --batchdump 结果里出现了一个 users 表字段包括用户名和密码哈希。用户名为 admin哈希看起来是 MD5 格式。这里说句实话sqlmap 虽然强大但在布尔盲注场景下速度并不快。如果着急可以加上--threads5提高并发再搭配--techniqueBE指定只使用布尔盲注和报错型注入会明显快很多。3.4 哈希识别与口令破解拿到哈希后的第一件事不是直接跑字典而是先确认哈希类型。我用hashid识别hashid 21232f297a57a5a743894a0e4a801fc3程序判断可能是 MD5。然后直接交给 johnecho 21232f297a57a5a743894a0e4a801fc3 hash.txt john --formatraw-md5 --wordlist/usr/share/wordlists/rockyou.txt hash.txt john --show hash.txt很快就跑出来了明文是admin。看起来弱口令到哪儿都存在不管你是真实业务还是靶机环境。这里有个重要的提醒哈希识别错了后面破解就是浪费时间。如果 hashid 给出的结果不唯一可以用 john 的--listformats查一下支持的格式或者干脆用 hashcat 的--identify功能。我见过有人把 SHA256 当 MD5 跑跑了半个多小时一点动静没有最后才发现是自己认错了哈希。4. 获取初始权限SSH 登录与系统内部侦察4.1 密码复用从 Web 到 SSH 的跳跃拿到admin:admin这组凭据后我的第一反应不是回 Web 后台继续找功能而是直接尝试 SSH。为什么因为在真实渗透里用户为了省事把同一个密码用在 Web、SSH、数据库等所有地方是一个非常普遍的操作。CTF 靶机既然给你留了 22 端口多半就是等着你拿这组密码来登录。ssh admin192.168.56.105输入密码 admin登录成功。注意这里我不是让你手动输入而是建议你后续在脚本化批量尝试时使用sshpass再加一个-o StrictHostKeyCheckingno跳过指纹确认这样就不会卡在交互式确认上。登录后执行id显示当前用户是 adminuid 是 1000属于普通用户组。到这里我们已经从“外部攻击者”变成了“拥有合法账号的普通用户”初始权限达成。4.2 系统内部侦察每一步都是在为提权铺路拿到初始权限只是第一步最终目标还是 root。所以我登进去后的第二件事就是系统枚举。下面这几条命令几乎是我在每台 Linux 靶机上都会跑一遍的“基础套餐”id sudo -l uname -a cat /etc/crontab find / -perm -4000 -type f 2/dev/null ls -la /home cat /home/admin/.bash_history逐条说下判断思路sudo -l如果输出的是类似User admin may run the following commands那就直接看有没有我们可以利用的命令。如果输出的是Sorry, user admin may not run sudo那就说明当前用户根本没有 sudo 权。uname -a是用来判断内核版本的。如果内核特别老可以尝试内核提权漏洞但我一般把它放在备用方案里不优先用。原因很简单内核提权脚本很容易把系统打崩一旦崩了整个靶机重启前面所有进度都得重来。cat /etc/crontab是看计划任务。如果有脚本是以 root 身份定期执行的而且路径又恰好可写那提权思路就变成了“写一个反向 shell 到脚本所在目录”。find / -perm -4000 -type f这一步是找 SUID 权限的文件也是这轮枚举里收获最大的地方。执行完以后我在列表里看到了一个非常显眼的文件/usr/bin/find。5. 提权到 root最后一百米的实战操作5.1 分析 SUID 文件确认提权可行性为什么/usr/bin/find带着 SUID 权限就意味着提权我试图把原理讲透。Linux 里每个进程都有“真实用户 ID”和“有效用户 ID”两个概念。你执行一个程序时进程的有效用户 ID 默认就是你的用户 ID。但是当一个文件被设置了 SUID 位即权限位里的 s你执行这个文件时进程的有效用户 ID 会变成文件所有者的用户 ID。如果这个二进制文件的所有者是 root那就相当于你执行它时程序临时获得了 root 的权限。find 本身有一个-exec参数可以在找到文件时执行后面跟着的命令。如果 find 是 SUID root那就等于你能以 root 身份执行任意命令只是入口是 find 这个合法程序而已。先确认权限位ls -l /usr/bin/find输出是-rwsr-xr-x注意 owner 权限位里的s说明 SUID 确实生效。GTFOBins 里对这种“可执行任意命令的 SUID 二进制”有现成的利用方法我们直接用标准姿势。5.2 利用 find -exec 拿到 root shell经典的利用命令如下find / -name test -exec /bin/bash -p \;这里有个细节特别容易踩坑/bin/bash后面的参数必须是-p不能是-i。原因是当 bash 检测到有效用户 ID 和真实用户 ID 不一致时它会主动把有效用户 ID 降回到真实用户 ID这其实是 bash 的一种自我保护机制。加了-p之后bash 才会保留 SUID 带来的特权。我第一次就是用-i启动的结果执行完id一看还是 uid1000当时还很疑惑后来查了 GTFOBins 才明白是参数少了-p。执行成功后id输出直接变成uid0(root) gid0(root) groups0(root)到这里root 权限已经拿下。整个过程没有打内核漏洞没有用复杂 exp就是靠一个错误配置的 SUID 文件这是 Linux 提权里最常见也最经典的场景。5.3 找到最终 flag验证通关提权成功后的第一个动作是去根目录找最终 flagcat /root/root.txt看到 flag 的那一刻这台靶机才算正式通关。整理一下这次的完整攻击路径Web 查询点存在 SQL 注入 → sqlmap 提取数据库哈希 → john 破解出 admin 密码 → SSH 登录 → 发现 SUID find → 利用 -exec 拿到 root。每一步都是环环相扣的。6. 通关中的常见问题与避坑心得6.1 同一套命令为什么会有人通关有人翻车我在自己复现和帮朋友远程看问题的时候遇到过不少“明明命令一样过程就是不一样”的情况。这里整理一个高频问题速查表希望能帮大家减掉一些不必要的返工。问题表现排查和解决办法靶机 IP 扫不到netdiscover 列表里没有新设备网络模式没配对改成 Host-Only 或 NAT 后重启靶机再用arp-scan -l重新扫sqlmap 跑得太慢一直卡在布尔盲注阶段添加--threads5和--techniqueBE确认当前注入是延时型的话换--techniqueTjohn 破解跑不出结果字典跑完也没有命中先哈希识别可能是 SHA256 或 bcrypt换--formatraw-sha256或其他格式重新跑SUID 提权后还是普通用户执行 bash -i 之后 id 显示 uid1000必须用bash -p保留特权模式SSH 脚本化登录卡住停在指纹确认提示加-o StrictHostKeyCheckingno用 sshpass 传密码虚拟机启动后没有网络靶机 ping 不通网关等启动完成后 2 分钟再试检查 VMware 网络服务是否正常6.2 几条值得长期复用的实操心得信息收集做得越扎实后面越顺水推舟。Web 目录枚举多扫十分钟可能就省掉后面两个小时的盲目测试。很多人一上来就丢 sqlmap完全不看站点结构最后只能靠运气。口令复用永远值得优先试。Web 后台的密码不要只想着回页面里用SSH、FTP、数据库、甚至控制台所有能登录的地方都值得用同一套密码尝试一遍。真实渗透里很多服务器就是这么被拿下的。提权别一上来就上内核漏洞。内核提权脚本是“不得已才上”的方案一旦失败系统可能直接重启。CTF 环境下你还能重新加载快照接着打真实环境里这就是一次事故了。优先看 SUID、sudo 配置、计划任务这些应用层面的错误配置。6.3 尝试不用 sqlmap 再打一遍这台靶机打完之后我建议你回头再用纯手工的方式把 SQL 注入重新走一遍。就靠在 Burp 里一条一条地构造请求用and 11、and 12这种最基础的方法一点点把数据“问”出来。整个过程会非常慢但收获完全值得。当你能不看任何工具输出就知道为什么一个 PUT 请求要多出一个引号为什么 order by 的列数会决定 union select 能不能成功说明你已经不是靠背命令打靶机了而是真正理解了数据库和 Web 应用之间的交互逻辑。工具能帮你省时间但只有亲手验证过原理直觉才能真正建立起来。7. 复盘看懂出题人的设计思路通关之后回头看 NullBytes 的设计其实能明显感受到出题人的用心。Web 层没有给你太难的外壳漏洞而是把重点放在了 SQL 注入和凭据复用上这两点是真实 Web 渗透里最常发生的事情提权环节也没有靠内核漏洞暴力解而是让你通过系统枚举去发现错误配置。整个靶机练下来你会发现所有考点都在“标准动作”里扫描够不够全注入判定准不准哈希识别对不对Suid 列表有没有仔细看。这种设计思路本身就是一份很好的学习地图。我每次打一台新靶机通关后会刻意把攻击过程写成一条时间线然后问自己三个问题哪个环节耗时最长哪个环节当初没想到如果把这台机器交给出题人自己加固他会堵上哪个口子这套复盘方式让我后面打 DC 系列和 Warzone 系列靶机时明显比以前更有方向感。如果你也是刚接触 VulnHub建议不要把 NullBytes 当作最后一站而是把它当作起点。先吃透这台再去找一台同样走“Web 注入提权”路线的靶机继续练等到你看到端口开放列表就能在脑子里预判出下一步该做什么的时候这个阶段就算毕业了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进