概述 (Overview)
HOST: 10.10.11.120
OS: LINUX
发布时间: 2021-10-31
完成时间: 2021-11-12
机器作者: z9fr
困难程度: EASY
机器状态: 退休
MACHINE TAGS: #SourceCodeAnalysis #Command_Injection #MongoDB #CoreDump
攻击链 (Kiillchain)
通过 Nmap 对目标服务器进行开放端口枚举,发现对外暴漏两个开放 Web 服务,运行有 Node.js Web 应用程序。随后下载页面源代码压缩包至本地进行源代码审查,在 API 功能代码中找到命令执行的安全漏洞,通过编写的 Payload 成功拿到立足点。
对服务器环境信息进行收集,发现在 /opt 目录下存在 SUID 权限的二进制文件及 Code 源码。最终通过触发进程异常终止获得 core dump 文件,实现权限提升的文件读取。
枚举(Enumeration)
开始依然是用 Nmap 对目标进行端口开放枚举(参考速查手册):
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 97:af:61:44:10:89:b9:53:f0:80:3f:d7:19:b1:e2:9c (RSA)
| 256 95:ed:65:8d:cd:08:2b:55:dd:17:51:31:1e:3e:18:12 (ECDSA)
|_ 256 33:7b:c1:71:d3:33:0f:92:4e:83:5a:1f:52:02:93:5e (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: DUMB Docs
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)
3000/tcp open http Node.js (Express middleware)
|_http-title: DUMB Docs
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
扫描结果中暴漏三个开放端口,其中 80、3000 是 HTTP Server 应用,从 ssh 的版本信息中可以推断出目标服务器是 Ubuntu。
Port 80 - HTTP Server
使用浏览器访问目标服务器的 80 端口,观察响应请求,发现存在 Express
服务信息。而 Express
是一个简单的 Node.js Web 应用程序开发框架。
通过对页面进行浏览发现存在注册接口,并带有一组账户密码。观察到 API 服务是运行在 3000 端口上的。
首先使用页面上账户进行登录接口请求,返回密码错误,进一步确定 API 接口的可用性。
随后调用注册接口成功注册一组自定义的账户:
使用自定义的账户成功调用登录接口,返回账户授权的 JWT Token :
https://medium.com/@gonzalocarrascosec/fuzzing-idor-admin-takeover-5343bb8f436e
随后尝试对接口进行 Fuzzing 没有取得很好的进展,在异常错误返回的 debug 信息中可以得到服务器的有效用户为 dasith
。
Source Code Analysis
网站首页存在一个压缩包下载,解压后发现是 API 应用服务的源代码:
通过查看对应的 API 接口功能代码发现,管理员为用户 theadmin
:
这里 name 变量的值来源于对授权的 JWT 字符串解密,而用户想构造有效的 JWT 内容则需要得到保存在服务端的 Secret Key 密钥,根据公开的加密步骤代码就可以实现任意参数的构造。
在解压的文件目录中可以看到 .env
文件,这个文件一般用于保存在服务器中的 node 全局环境变量函数,我们需要的 Secret Key 密钥一般也都保存在这里。但尝试使用 TOKEN_SECRET = 'Secret'
参数进行构建 JWT 后发现验证失败,怀疑正确的密钥保存在 git 历史记录里。使用 git log -p
进行查阅成功找到正确的密钥:
commit 67d8da7a0e53d8fadeb6b36396d86cdcd4f6ec78
Author: dasithsv <dasithsv@gmail.com>
...snip...
TOKEN_SECRET = gXr67TtoQL8TShUc8XYsK2HvsBYfyQSFCFZe4MQp7gRpFuMkKjcM72CNQN4fMfbZEKx4i7YiWuNAkmuTcdEriCMm9vPAYkhpwPTiuVwVhvwE
立足点(Foothold)
在查看 /logs
接口功能代码时发现RCE漏洞。用户传递的 file 变量没有进行任何安全处理,进行字符串拼接后传递至 exec
函数里执行造成命令执行漏洞。
本地启动一个 NC 监听,随后发送反弹 shell 语句成功拿到立足点。
权限提升(Privilege Escalation)
已经通过查看代码知道应用连了本地的 mongodb 数据库,首先尝试查询用户密码并碰撞明文,用于验证是否存在密码复用的情况进行密码喷洒。
$ mongo 127.0.0.1:27017/auth-web
MongoDB shell version v3.6.8
connecting to: mongodb://127.0.0.1:27017/auth-web
Implicit session: session { "id" : UUID("d7f001b8-8e23-40b7-a02e-4dca384b03d9") }
MongoDB server version: 3.6.8
$ show dbs
admin 0.000GB
auth-web 0.000GB
config 0.000GB
local 0.000GB
$ show collections
users
$ db.users.find()
...snip...
跑了一轮字典后发现并没有取得结果,尝试寻找其他信息。在 /opt 目录下发现可疑的二进制程序,它具备 SUID 权限。并且看到存在它的 C 语言源码,尝试阅读并执行可疑的二进制程序:
代码整体逻辑是接收用户输入的文件或文件夹路径,读取它至内存中并计算它有多少行、多少个字符等。但在输出了这些信息之后,通过 getuid
降低了执行该二进制程序的权限,使其只能维持在 dasith
用户权限,这就导致了后面的 Save results a file?
保存失败。
注意到代码中存在注释信息 // Enable coredump generation
,通过搜索引擎进行查询的到帮助内容,当我的用户态由于段错误或者其他错误发生了而退出进程时一般会在 /var/crash/
目录下生成相应的core文件(前提是配置了core文件转储机制)。
使用 Ctrl+C
终止进程是不会 产生 core dump 的,需要特定的信号,可以主动使用 SIGSEGV
信号来生成 dump 文件。完整的信息可以通过运行 kill -l
得到所有信号列表。
已经成功得到 dump 文件,但因为数据是经过 base64 编码的,所以我们需要借助工具来处理它。Linux 可借助 apport-unpack
命令进行处理,它会将问题报告的字段提取到单独的文件中。
通过执行 strings tmp/CoreDump
命令成功得到 root flag。如果想获得 root 登录私钥,同理,只需要读取 /root/.ssh/id_rsa
文件即可。
复盘
观察到代码中使用了 prctl(PR_SET_DUMPABLE, 1);
函数,prctl 函数的作用是可以在一个进程或线程上进行操作。
这个我前一个月也碰到了类型的题型,因为 flag 文件被设置为
0600
权限无法直接读/proc/<PID>/exe
来获取字符串,但当时我忘记 core dump 是否可用了,知道今天整理这个 Writeup 时才后知后觉想起来。因为我做题的时候都只留过程截图,文字都是想发文章时才码的,导致有些内容笔记中搜索不到。
- PR_SET_DUMPABLE (since Linux 2.3.20)
- 设置" dumpable"属性的状态,该属性确定在传递默认行为是生成核心转储的信号时是否为调用进程生成核心转储。
熟悉 Linux 的都知道,当进程在运行状态中都可以在 /proc/<PID>/
目录中找到。因为 /proc
是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。
其中 /proc/<PID>/fd
这是个目录,包含当前进程打开的每一个文件的文件描述符(file descriptor),这些文件描述符是指向实际文件的一个符号链接。假如存在权限滥用的情景下,可以读取链接文件资源的内容字符串。