Pages

Sunday, August 21, 2016

PowerShell: Send a Toolkit & Receive/Analyze evidence over a continuous Client-Server communication during an IR engagement !

It is a normal day in the office, and your SOC team alerts you that they have spotted a suspicious cross-process injection to a legitimate process/application which is now making calls to a suspicious IP. Let's say you are following the six main steps for Incident Handling which include Preparation, Identification, Containment, Eradication, Recovery, and Lessons Learned ( Presented in the Blue Team handbook: Incident Response Edition by Don Murdoch, and taught by the SANS Institute) and you have already identified the scope of the attack and are in the middle of step #3 (Containment - collecting volatile and non-volatile forensic evidence of the compromised system). 

You have a couple of options to successfully collect/analyze artifacts and also a few obstacles:


OPTIONSOBSTACLES

  • Enterprise level software installed on your endpoints that allow you to extract artifacts/evidence.


  • Software needs to be installed everywhere or have licenses available = Expensive
  • Software needs to parse your hard drive in order to let you pick any file for download = Time (30 mins to 1 hour) - what if you only want to pull one file, you will have to wait !!

  • Netcat? + dumping memory + sending memory dump back to the office + Dump Process/EXE/Module/VAD or analyze volatile data


  • Netcat most likely is not available in your network
  • You need to customize your netcat commands every time you send a new file (not Dynamic enough)
  • Dumping memory + Sending it back remotely to the Incident handler's PC in order to dump/analyze artifacts from memory = Yup we know how much time that will take !




So, you might think, these are old news, but believe me, I have seen companies still doing IR that way. What if you can use a TCP Client-Server infrastructure (Console-Agent communications such as the ones used by security enterprise software) for FREE, using built-in capabilities installed by default on every single Windows endpoint (PowerShell), perform live analysis, dump only specific artifacts you need from memory without dumping the whole memory, and sending and receiving files continuously over the same session established to the infected endpoint.

In this post, I will be primarily showing how you can send/receive files during an IR engagement by adding extra functionalities to the TCP Client-Server Infrastructure I built over the course of a few past posts:

As usual, I will start with some requirements and some definitions about the new classes I will be using during this post in order to make things easier for you guys to understand. Also, I will be providing as many details as I can and a few references for those interested on reading more about this topic.

Requirements:
  • At least Windows 7
  • At least PowerShell V2
  • PowerShell ISE
  • Reading the articles defined above (TCP Client-Server Infrastructure)


FileStream .NET Class (System.IO.FileStream)

Provides a Stream for a file, supporting both synchronous and asynchronous read and write operations.

Constructor Description
FileStream(String, FileMode)    Initializes a new instance of the FileStream class with the specified path and creation mode.


Method
Description
Seek(offset, origin)Sets the current position of this stream to the given value.
Read(Array,(byte[]),offset, count)     Reads a block of bytes from the stream and writes the data in a given buffer.


  • FileMode Enumeration
    Specifies how the operating system should open a file
    Mode
    Description
    AppendOpens the file if it exists and seeks to the end of the file, or creates a new file. 
    Open Specifies that the operating system should open an existing file. (used when reading a file before sending it over).
    OpenOrCreate Specifies that the operating system should open a file if it exists; otherwise a new file should be created.


  • SeekOrigin Enumeration
    Specifies the position in a stream to use for seeking.
    Mode
    Description
    BeginSpecifies the beginning of a stream 
    Current Specifies the current position within a stream
    EndSpecifies the end of a stream



Quick Overview TCP Client-Server (over SSL) Code

ServerClient


  • Start the listener on the specific port.
  • Start accepting connection request.
  • Connection request is accepted and TCP Client .NET Object is created to hangle the remote communication.
  • Network Stream is obtained from the TCP client object and is used to send results and receive data.
  • SSL Stream object is initialized from the current Network Stream.
  • the X509 Certificate is imported to the server. (From base64 to X509 Format) 
  • Server authenticates as a server defining the X509 Certificate & preferred Certificate requirements
  • While loop is created to send and receive commands.
  • If connection is interrupted of terminated, the loop finishes and the server shutdown gracefully.



  • Connect to the server on the specific port where it is listening on.
  • Connection is accepted
  • Network Stream is obtained from the TCP Object client initialized at the beginning to sent the connection request.
  • SSL Stream object is initialized from the current Network Stream.
  • Client authenticates to the server by calling for the name of the server (Name on the Server's Certificate )
  • While loop is created to send commands and receive server responses.
  • If connection is interrupted or terminated, the loop finishes and the client shutdown gracefully. 








Quick Overview TCP Client-Server (over SSL) Code + Send/Receive Capabilities

ServerClient


  • Start the listener on the specific port.
  • Start accepting connection request.
  • Connection request is accepted and TCP Client .NET Object is created to hangle the remote communication.
  • Network Stream is obtained from the TCP client object and is used to send results and receive data.
  • SSL Stream object is initialized from the current Network Stream.
  • the X509 Certificate is imported to the server. (From base64 to X509 Format) 
  • Server authenticates as a server defining the X509 Certificate & preferred Certificate requirements
  • While loop is created to send and receive commands.
    • If client sends the "SEND-FILE" command", it means that the file exists locally on the client and it was sent without any errors. The server then executes the function "SEND-FILE" to create a new file on the server and append all the bytes sent by the client . Send-File = Receive-File for the server.
    • If client sends the "RECEIVE-FILE" command, the server checks if the file that the client is looking for exists locally on the server. If the file exists, the server sends the message "File Exists" to the client and the client creates a new file locally. The client waits for the server to run its function "RECEIVE-FILE" which sends over all the bytes from the file requested by the client. If the file does not exist locally on the server, the server sends the exception message and goes back to reading mode waiting for the client to send a command to keep the communication going ("Shell Style").
  • If connection is interrupted of terminated, the loop finishes and the server shutdown gracefully.



  • Connect to the server on the specific port where it is listening on.
  • Connection is accepted
  • Network Stream is obtained from the TCP Object client initialized at the beginning to sent the connection request.
  • SSL Stream object is initialized from the current Network Stream.
  • Client authenticates to the server by calling for the name of the server (Name on the Server's Certificate )
  • While loop is created to send commands and receive server responses.
    • If client types the "Send-File" command", the client verifies if the file that it is about the send exists locally on the client. if so, it sends the "SEND-FILE" command over to the server. The server then executes its "SEND-FILE" to create a new file on the server and append all the bytes sent by the client . Send-File = Receive-File for the server.
    • If client sends the "RECEIVE-FILE" command, the server checks if the file that the client is looking for exists locally on the server. If the file exists, the server sends the message "File Exists" to the client and the client creates a new file locally. The client waits for the server to run its function "RECEIVE-FILE" which sends over all the bytes from the file requested by the client. If the file does not exist locally on the server, the server sends the exception message and goes back to reading mode waiting for the client to send a command to keep the communication going ("Shell Style").
  • If connection is interrupted or terminated, the loop finishes and the client shutdown gracefully. 







The new functions "Send-File"  & "Receive-File" that will be added to the Server and Client are almost the same for both. So I will go over each one only once, and then just explain in two lines the differences. It will be up to you how you want to modify them on each script (server-client). In addition, the only reason why I keep the Server and Client scripts separate is for learning purposes. I like to see how each change applies to the server and the client individually. You can always put both scripts together and create switches to run specific parts of the script whenever the script is being run as a server or client. 



Server Already monitoring for commands sent by client
  • So far on the server script from PowerShell: TCP Client-Server with .NET Classes, Code snippet #3, Lines #13-17, we are monitoring for the command "break", sent by the client, to stop the connection, dispose/close the TcpNetworkStream, and stop the listener gracefully.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
            if ($TcpNetworkstream.DataAvailable) {continue}
            else{    
                
                $ScriptBlock = [ScriptBlock]::Create($EncodingType.GetString($Bytesreceived))
                
                if ($ScriptBlock -match "break") {
                    $sendback = $encodingtype.GetBytes(("`n[!!!] Closing Connection with ["+(hostname)+"]. Press ENTER to continue.."))
                    $SslStream.Write($sendback, 0, $sendback.Length)  
                    $SslStream.Flush()
                    break
                }

  • Now, we have to monitor for "Send-File" & "Receive-File" sent by the client.
  • Since, on the code snippet above, on line #6 we are using an IF statement to look for the command "break", we will be using "IFELSE" also to look for other commands.




"Send-file" Syntax (Servers Interpretation)

send-file -local <Path of local file sent by client> -remote <Desired path/location of the file sent by the client>


Server monitoring for "send-file"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
                elseif($ScriptBlock -match "send-file"){      
                    try {
                        invoke-command -ScriptBlock $ScriptBlock
                        $parameters = $null
                        $parameters = $ScriptBlock -split " "
                        $SendingFile = ($parameters[2] | Out-String).TrimEnd()
                        $Receivefile = ($parameters[4] | Out-String).TrimEnd()
                   
                        if ($SendingFile -and $Receivefile -match ".zip"){
                            [string]$splitzip = $Receivefile -split ".zip"
                            [string]$leaf = Split-Path $splitzip -leaf
                            $unzfile = ($env:USERPROFILE + "\" + $leaf)
                            try {
                                invoke-unzip
                                Remove-Item $Receivefile
                                $ScriptBlock = [scriptblock]::Create("get-item $unzfile")
                            }
                            catch {$sendback = $encodingType.GetBytes($_.Exception.Message)}
                        }
                        else{ 
                            $ScriptBlock = [scriptblock]::Create("get-item $Receivefile")
                        }
                    }
                    catch {$sendback = $encodingType.GetBytes($_.Exception.Message)}                        
                }


  • Line #1: Uses the Elseif conditional to look for another command sent by the client. In this case it is looking for "send-File".
  • Line #2-23: Notice that I use a Try block. This is to monitor errors that occur within the Try block. You will see a lot of these throughout the whole code. PowerShell then searches for a Catch block to handle the errors found or occurred in the Try block.
  • Line #3: Invokes the content of variable $scriptblock which is the whole Send-file function with its respective parameters. This new function will be defined in a little bit. For now, lets pretend that the function Send-File ran in our server already. In addition, I want to point out that the even though the function is called Send-File in the server, it will actually be used to Receive the file sent by the client. Believe me, it will make things much easier when you start looking into the Client side since it also has Send-file and Receive-File functions in order to send and retrieve files from the server.
  •  Line #4-5: Defines a new variable called $parameters which will be an array formed by elements of the "Send-File" command and its parameters. As you can see on the Send-File syntax above, there are are 5 words separated by a space. if we split the full command captured by the variable "Scriptblock", we will have an array of 5 elements starting with $parameters[0] = send-file. We can separate all the words by spaces with the the folllowing: -split " "
  • Line #6-7: Sets new variables ($SendingFile & $Receivefile) which will hold specific elements of the array. $ReceiveFile holds element number five of the $parameteres array. In other words we will be taking the value of element $parameters[4] = Desired Path for new remote file sent by the client. I make sure to trim the end of the value $parameters[4] to avoid any extra space at the end.(Element Five of the full command is the Path where the client expects to store the file that it is sending over to the server). $SendingFile holds element three of the array which is $parameters[2] = Path of local file sent by client. 
  • Line #9: Starts and IF statement to verify if a ZIP file was sent over to the server 
  • Line #10: If so, sets a new variable named $splitzip which will hold the full path where the zip file was copied to in the server without its .zip extension. I do this because I want to get to the original name of the zipped file. Then,I will create a folder somewhere else and decompress the zip file to it keeping the same original folder name.
  • Line #11: Now I want to get rid of the folder's full path. I set a new variable named $leaf and extract the name of the file.
  • Line #12: Sets a new variable named $unzifile and it will hold the full path of the new decompressed folder.
  • Line #13: Starts a Try block to monitor for errors.
  • Line #14: Starts the unzipping functionality by invoking a custom invoke-unzip function. This function will be defined also in a little bit.
  • Line #15: Once the zip file gets decompressed, the server deletes the zip file where it was first copied to.
  • Line #16: Overwrites whatever was in the variable $Scriptblock and sets it to a command that will check if the new decompressed folder exists. I do this after receiving and decompressing the file because the nature of the server is to receive a command from the client, set it to variable $scriptblock, execute it, and then send the results back to the client. Therefore, in this case, we received the Send-file command, we ran the send-file function, we check if the file is zipped, decompress the file, and then we check if the decompressed folder/file exists.
  • Line #18: A Catch block is used to handle any errors while unzipping the file sent to the server. It adds the exceptions to variable $sendback which will be then sent back to the client.
  • Line #20: If the file sent to the server is not a ZIP file then it just simply uses the Else statement to finish the send-file function.
  • Line #21: Overwrites whatever was in the variable $Scriptblock and sets it to a command that will check if the new file/folder exists (copied over to the server successfully). 
  • Line #24: Closes the main Catch block and handles any exceptions/errors occurred while processing the send-file command/function. Errors get added to the $sendback variable which will hold results sent back to the client 




Receive-file Syntax (Servers interpretation)

receive-file -local <Local path/location where the client will store the file sent by the client> -remote <Path of file in the server requested by the client>


Server monitoring for "receive-file"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
               elseif($Scriptblock -match "receive-file"){
                    try {
                        Invoke-command $Scriptblock
                        continue
                    }
                    catch {
                    Write-verbose $_.Exception.Message
                    $sendback = $encodingType.GetBytes($_.Exception.Message)
                    $SslStream.Write($sendback, 0, $sendback.Length)  
                    $SslStream.Flush()
                    $Bytesreceived = $null
                    continue
                    }                       
                }


  • Line #1: Uses the Elseif conditional to look for another command sent by the client. In this case it is looking for "receive-File".
  • Line #2-5: Uses a Try block to monitor for errors while processing the receive-file function.
  • Line #4: If the receive-file function runs successfully, then go back to reading mode.
  • Line# 6-13: A Catch Block  is used to handle errors occurred while processing the the receive-file  function. 
  • Line #8-11: When the receive-file function fails, it sends the exception back to the client.
  • Line #12: After sending the exception to the client, the server goes back to reading mode (waiting for commands to be executed)




Your server's logic to execute commands and send results back should look like this:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
    try{
        while ($TCPClient.Connected){
            $Bytesreceived = $null
            $Read = $SslStream.Read($Receivebuffer, 0, $Receivebuffer.Length)    
            if($Read -eq 0){break}                  
            else{            
                [Array]$Bytesreceived += $Receivebuffer[0..($Read -1)]
                [Array]::Clear($Receivebuffer, 0, $Read)
            }
                      
            if ($TcpNetworkstream.DataAvailable) {continue}
            else{    
                $ScriptBlock = [ScriptBlock]::Create($EncodingType.GetString($Bytesreceived))
                if ($ScriptBlock -match "break") {
                    $sendback = $encodingtype.GetBytes(("`n[!!!] Closing Connection with ["+(hostname)+"]. Press ENTER to continue.."))
                    $SslStream.Write($sendback, 0, $sendback.Length)  
                    $SslStream.Flush()
                    break
                }
                elseif($ScriptBlock -match "send-file"){      
                    try {
                        invoke-command -ScriptBlock $ScriptBlock
                        $parameters = $null
                        $parameters = $ScriptBlock -split " "
                        $SendingFile = ($parameters[2] | Out-String).TrimEnd()
                        $Receivefile = ($parameters[4] | Out-String).TrimEnd()
                   
                        if ($SendingFile -and $Receivefile -match ".zip"){
                            [string]$splitzip = $Receivefile -split ".zip"
                            [string]$leaf = Split-Path $splitzip -leaf
                            $unzfile = ($env:USERPROFILE + "\" + $leaf)
                            try {
                                invoke-unzip
                                Remove-Item $Receivefile
                                $ScriptBlock = [scriptblock]::Create("get-item $unzfile")
                            }
                            catch {$sendback = $encodingType.GetBytes($_.Exception.Message)}
                        }
                        else{ 
                            $ScriptBlock = [scriptblock]::Create("get-item $Receivefile")
                        }
                    }
                    catch {$sendback = $encodingType.GetBytes($_.Exception.Message)}                        
                }
                elseif($Scriptblock -match "receive-file"){
                    try {
                        Invoke-command $Scriptblock
                        continue
                    }
                    catch {
                    Write-verbose $_.Exception.Message
                    $sendback = $encodingType.GetBytes($_.Exception.Message)
                    $SslStream.Write($sendback, 0, $sendback.Length)  
                    $SslStream.Flush()
                    $Bytesreceived = $null
                    continue
                    }                       
                }

                $Global:Error.Clear()
      
                try {
                    $results = $ScriptBlock.Invoke() | Out-String
                    $sendback = $encodingtype.GetBytes($results)
                }
                catch{ 
                    write-verbose "NOT VALID COMMAND"
                    foreach ($Err in $Global:Error) {
                        $sendback = $encodingType.GetBytes($Err.Exception.Message) 
                    }
                }
     
                Write-Verbose "results: $results"

                $sendback += $encodingtype.GetBytes(("`n["+(hostname)+"] PS " + (Get-Location).Path) +'> ')
                $SslStream.Write($sendback, 0, $sendback.Length)  
                $SslStream.Flush()
                $results = $null
                $Bytesreceived = $null
            }
        }
    }
    catch {Write-Verbose "[!!!]TCP connection is broken, exiting.."} 






"Send-file" Syntax

send-file -local <Path of local file> -remote <Desired path for new remote file>


Client not monitoring for its own commands yet

1
2
3
4
5
                $sendback = $EncodingType.GetBytes((read-host) + "`n")

                $SslStream.Write($sendback, 0, $sendback.Length)
                $SslStream.Flush()
                $Bytesreceived = $null


  • So far on the client script from PowerShell: TCP Client-Server with .NET ClassesCode snippet #3Lines #14-17, we are just capturing commands typed by the user to be sent to the server and executed remotely. We are not monitoring for anything in specific. Once a command is sent, the client goes back to reading/waiting mode to receive the results of its commands being executed remotely.
  • Now we will start monitoring  for "Send-File" and "Receive-File".
    • After Line#1 from the snippet above, we will use if,elseif and else statements to monitor for if(Send-file), elseif(Receive-file) and else(Lines#3-4 of the snippet above in order to execute anything else besides those two new functions)
  • Remember to add $Bytesreceived = $null to your code to make sure you always clear your bytes cache.



Client monitoring for "send-file"

1
2
3
4
5
6
7
8
                $ScriptBlock = $null
                $ScriptBlock = [scriptblock]::Create($EncodingType.GetString($sendback))

                if ($Scriptblock -match "send-file"){               
                    try {Invoke-command -ScriptBlock $Scriptblock}
                    catch {Write-Warning $_.Exception.Message; invoke-space}
                          
                }


  • Line #1-2: It will grab the commands stored in variable $sendback (Clients Input) and sets it to variable $scriptblock in a form of strings. This will allow us to start monitoring for specific strings.
  • Line #4-5: Uses the If conditional to check if the client sent the "send-file" command to the server. If so, it invokes the content of variable $scriptblock which is the whole send-file function with its respective parameters. This new function will be defined in a little bit. 
  • Line #5-6: Try and Catch blocks used to monitor and handle errors.
  • Line #6: If something goes wrong while running the send-file function, error messages are displayed on the clients console in a warning format. Also, I had to create a small function named "invoke-space" to automatic send space commands to the server when it is needed to refresh the two-way communication. For example, when using the send-file function, if we fat finger a command or the local file does not exist, there is no need to send the send-file  command to the server. Instead, we could send a space (" ") in order to refresh the communication.




Receive-file Syntax (Client's Interpretation)

receive-file -local <Path of remote evidence> -remote <Desired Path for new local file>


Client monitoring for "receive-file"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
               elseif($ScriptBlock -match "receive-file"){
                    $check = $null
                    $SslStream.Write($sendback, 0, $sendback.Length)
                    $SslStream.Flush()
                    
                    $Read = $SslStream.Read($Receivebuffer, 0, $Receivebuffer.Length)
                    if( $Read -eq 0){break}                  
                    else{            
                        [Array]$Bytesreceived = $Receivebuffer[0..($Read -1)]
                        [Array]::Clear($Receivebuffer, 0, $Read)
                    }
                    $check = $EncodingType.GetString($Bytesreceived).TrimEnd("`r")
                    if ($check -match "Exception"){
                        write-warning $check
                    }
                    else{ 
                        try {Invoke-command -ScriptBlock $Scriptblock}
                        catch {Write-Warning $_.Exception.Message}
                    }
                    invoke-space                       
                }


  • Line #1: Uses the Elseif conditional to look for another command typed by the client. In this case it is looking for "receive-File".
  • Line #2: Variable $check will be used to check the servers response to determine whether the file exists remotely or not. Set to $null to clear any values associated with it if there is any.
  •  Line #3-4:  Sends the receive-file command with its respective parameters to the server.
  • Line #6-11: Sets client on Reading mode to monitor for the servers response to determine whether the file exists remotely or not. Results are added to $Bytesreceived.
  • Line #12: Variable $check gets the content of $Bytesreceived in string format.
  • Line #13-15: If the server sends a message that contains the word "exception", then the client shows the warning message on its console.
  • Line #16-19: Else, (meaning if the file that the client requested exists on the remote server) start retrieving the file. The client runs the content of the variable Scriptblock again which contains the whole receive-file function with its respective parameters defined in the first place. Try and Catch block are used also to monitor and handle exceptions/errors while executing the receive-file function.
  • Line #20: Again, our client sends a space command to refresh the communication with the server. Due to the nature of this script. the client needs to refresh the communications after sending/executing the receive-function.




Your client's logic to execute commands and receive results should look like this:

  • One thing to remember is the $bytesreceived variable I mentioned earlier. Look how I positioned on Line#3. This will avoid to write extra cached results to the clients console.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
   try {
        while ($TCPClient.Connected){         
            $Bytesreceived = $null
            $Read = $SslStream.Read($Receivebuffer, 0,$Receivebuffer.Length)
                     
            if($Read -eq 0){break}                  
            else{
                [Array]$Bytesreceived += $Receivebuffer[0..($Read -1)]
                [Array]::Clear($Receivebuffer, 0, $Read)
            }

            if ($TcpNetworkStream.DataAvailable) {continue}
            else {
                write-host -NoNewline $EncodingType.GetString($Bytesreceived).TrimEnd("`r")
                
                $sendback = $EncodingType.GetBytes((read-host) + "`n")

                $ScriptBlock = $null
                $ScriptBlock = [scriptblock]::Create($EncodingType.GetString($sendback))

                if ($Scriptblock -match "send-file"){               
                    try {Invoke-command -ScriptBlock $Scriptblock}
                    catch {Write-Warning $_.Exception.Message; invoke-space}
                          
                }
                elseif($ScriptBlock -match "receive-file"){
                    $check = $null
                    $SslStream.Write($sendback, 0, $sendback.Length)
                    $SslStream.Flush()
                    
                    $Read = $SslStream.Read($Receivebuffer, 0, $Receivebuffer.Length)
                    if( $Read -eq 0){break}                  
                    else{            
                        [Array]$Bytesreceived = $Receivebuffer[0..($Read -1)]
                        [Array]::Clear($Receivebuffer, 0, $Read)
                    }
                    $check = $EncodingType.GetString($Bytesreceived).TrimEnd("`r")
                    if ($check -match "Exception"){
                        write-warning $check
                    }
                    else{ 
                        try {Invoke-command -ScriptBlock $Scriptblock}
                        catch {Write-Warning $_.Exception.Message}
                    }
                    invoke-space                       
                }
                else {
                    $SslStream.Write($sendback, 0, $sendback.Length)
                    $SslStream.Flush()
                }
            }  
        }
    }
    catch{ Write-Warning "`n[!!!]TCP connection is broken, exiting.."}






"Send-File" Function (Client)

  • I remember reading about PowerCat last year from Jesse Davis (@Secabstraction), and he showed a clean way to send a file over to a server in his code here. He basically also re-wrote Netcat with PowerShell (My inspiration for this project).
  • The code below is  almost the exact code he used to accomplish this.
  • Remember this function is used by server and client with only a few differences that I will explain in a bit. 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    function send-file
    {   
        [CmdletBinding()]
        Param (
        [Parameter()]
        [string]$local,

        [Parameter()]
        [string]$remote
        )

        $FileStream = New-Object IO.FileStream @($local, [IO.FileMode]::Open)

        Write-host "`n[***]Attempting to send " $local "to" $destination -ForegroundColor Cyan
        Write-Verbose "[***] Local file exists.."
        Write-Verbose "[***] sending file.."
        
        $SslStream.Write($sendback, 0, $sendback.Length)
        $SslStream.Flush()
                
        $destination = ("\\" + $serverip.IPAddressToString + "\" + $remote)

        if ($BytesLeft = $FileStream.Length) {
                  
            $FileOffset = 0
            if ($BytesLeft -gt 4608) { # Max packet size for Ncat

                $BytesToSend = New-Object Byte[] 4608
                        
                while ($BytesLeft -gt 4608) {
                    [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin)
                    [void]$FileStream.Read($BytesToSend, 0, 4608)
                                
                    $FileOffset += 4608
                    $BytesLeft -= 4608
                    $SslStream.Write($BytesToSend, 0, $BytesToSend.Length)         
                } 

                # Send last packet
                $BytesToSend = New-Object Byte[] $BytesLeft
                [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin)
                [void]$FileStream.Read($BytesToSend, 0, $BytesLeft)

                $SslStream.Write($BytesToSend, 0, $BytesToSend.Length)
            }
            else { # Only need to send one packet
                $BytesToSend = New-Object Byte[] $BytesLeft
                [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin)
                [void]$FileStream.Read($BytesToSend, 0, $BytesLeft)

                $SslStream.Write($BytesToSend, 0, $BytesToSend.Length)
            }
            write-verbose "[***] Done sending bytes.."
            $FileStream.Flush()
            $FileStream.close()
            $FileStream.Dispose()
                
            write-host ("`nFile " + $local + " was sent successfully to " + $destination) -ForegroundColor Cyan
            
            if ($local -match ".zip"){
                write-host ("`n[***]Unzipping " + $destination + " Please Wait!") -ForegroundColor Cyan
            }
        }      
    }

  • Line #1:  Starts the Send-file function. Remember that this for the server will be named Receive-File. This is because the essence of the send-file  function is that it is used to send a file and not receive a file. The server will use this code with the name of Receive-file when the client wants to receive/extract a file, the server uses the same code as the client's Send-File, but with the name Receive-File because the client sends the command Receive-file.  Remember, the server executes every command that the client sends.
  • Line #3-9: Defines the parameters available for the function. CmdletBinding() at the beginning is necessary to provide automatic features such as error handling. If you fat finger a parameter, the function will throw and error and tell you that the parameter does not exist. Without this feature, the function will just crash and break your loop and terminate your Client or Server.
  • Line #12: Opens the file that the client wants to send and defines it as a FileStream object. Parameters:
    • Path: A relative or absolute path for the file that current FileStream object will encapsulate.
    • mode: A constant that determines how to open or create the file
  • Line #18-19: Sends the Send-file command with its respective parameters to the server. This is executed at this point of the script because we want to make sure that there is not a problem opening the file that the client wants to send or if it exists in the first place. Remember, we have  a Try block monitoring for errors occurring while executing this function. 
  • Line #23: Starts an If conditional to just make sure the $FileStream was initialized and it was able to capture the Local file's total length ($true). It defines the variable $Bytesleft with the length value of the local file.
  • Line #25: Sets the $Fileoffset variable to Zero. This will increase by the number of bytes already sent over to make sure that we are reading from the right offset while reading bytes of a document in a while loop (When the file is bigger than the buffer used to send bytes).
  • Line #26-28: Starts an If conditional in order to check if the length of the local file is greater than 4608 (Value of the buffer being defined after). If so, the buffer ($bytesToSend) of the send-file operation that will be used to send bytes over is defined and set to 4608.
  • Line #30: Starts a while loop in order to seek, read and send the local file's bytes over to the remote host. The condition for the while loop is the buffer to be greater than 4608.
  • Line #31: It uses the seek method from the $FileStream object in order to set the current position ($fileoffset) of the stream to an specific value. Parameters:
    • offset: The point relative to origin which to begin seeking. In this case, it is set to the current value of the variable $offset (First iteration is 0).
    •  Origin: Specifies the beginning, the end, or the current position as a reference point for offset, using a value of type SeekOrigin. In this case, it is set to Begin in order to start reading from the specific offset as if it was the beginning of a stream.
  • Line #32: It uses  the read method  from the $FileStream Object in order to read a block of bytes from the stream and write the data to a specific buffer ($bytesToSend). Parameters:
    • Array: The specific byte array which will be used to store the chunk of bytes read from a determined range (offset + count -1). In this case, it will be the buffer  variable $BytesToSend.
    • Offset: The byte offset in array at which the read bytes will be placed. Always zero since it will be a new array every iteration.
    • Count: The maximum number of bytes to read. In this case, it will be the buffer's length.
  • Line #34-36: Increases the $FileOffset by 4608 for each iteration in order to start reading the next chunk of bytes from the file. Also, it reduces the value of variable $bytesLeft which stores the total length of the local file by 4608 in order to make sure we stop the while loop when the value of $bytesleft is less than 4608. Then, it uses the SslStream we are using to send data over to the server and start sending bytes over and over as long as the while loop is true.
  • Line #40-44: Once the while loop stops, it sends the last chunk of bytes over.
  • Line #46-51: Else the length value of the local file is not greater than 4608, seek, read and write bytes over from the local file to the remote host.
  • Line #54-56: Clears the buffer for the $FileStream and causes any buffered data to be written to the local file.Releases all resources used by $FileStream. Also, it shows a message on the client's console.
  • Line #60-61: It the file sent over was a zip file, then it shows a message that the the client will be waiting some time before it gets control of the console again.





"Send-File" Function (Server)

  • Remember that this function is renamed Receive-file so that when the client sends the command receive-file to the server, it(server) executes the send-file code and sends a file over to the client. 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
    function receive-file
    {   
        [CmdletBinding()]
        Param (
        [Parameter()]
        [string]$local,

        [Parameter()]
        [string]$remote
        )
            
        $FileStream = New-Object IO.FileStream @($remote,[IO.FileMode]::Open)
        
        $destination = ("\\" + $remoteclient + "\" + $local)
                        
        Write-verbose ("Attempting to send " + $remote + " to " + $destination)
        Write-Verbose "[***] Local file exists.."
        Write-Verbose "[***] sending file.."
        
        $sendback = $EncodingType.GetBytes("file exists")
        $SslStream.Write($sendback, 0, $sendback.Length)
        $SslStream.Flush() 

        if ($BytesLeft = $FileStream.Length) {
                     
            $FileOffset = 0
            if ($BytesLeft -gt 4608) { # Max packet size for Ncat

                $BytesToSend = New-Object Byte[] 4608
                        
                while ($BytesLeft -gt 4608) {
                    [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin)
                    [void]$FileStream.Read($BytesToSend, 0, 4608)
                                
                    $FileOffset += 4608
                    $BytesLeft -= 4608
                    $SslStream.Write($BytesToSend, 0, $BytesToSend.Length)         
                } 

                # Send last packet
                $BytesToSend = New-Object Byte[] $BytesLeft
                [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin)
                [void]$FileStream.Read($BytesToSend, 0, $BytesLeft)

                $SslStream.Write($BytesToSend, 0, $BytesToSend.Length)
            }
            else { # Only need to send one packet
                $BytesToSend = New-Object Byte[] $BytesLeft
                [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin)
                [void]$FileStream.Read($BytesToSend, 0, $BytesLeft)

                $SslStream.Write($BytesToSend, 0, $BytesToSend.Length)
            }         
            write-verbose "[***] Done sending bytes.."
            $FileStream.Flush()
            $FileStream.close()
            $FileStream.Dispose()
                
            Write-Verbose ("`nFile " + $remote + " was sent successfully to " + $destination)
        }      
    }






"Receive-File" Function (Client)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function Receive-file
    {
        Param (
        [Parameter()]
        [string]$local,

        [Parameter()]
        [string]$remote
        )
        
        if (Test-Path $local){remove-item $local -Force}
        else {write-verbose "Local file does not exist. Creating a new one..."}

        $FileStream = New-Object IO.FileStream @($local, [IO.FileMode]::Append)       
        $BytesToReceive = New-Object Byte[] 4608

        while ($true)
        {
            $Fileread = $SslStream.Read($BytesToReceive, 0, $BytesToReceive.Length)
            
            if($Fileread -eq 0){break}                  
            else{          
                [Array]$Filebytesreceived = $BytesToReceive[0..($Fileread -1)]
                [Array]::Clear($BytesToReceive, 0, $Fileread)
            }
                          
            if ($Fileread -eq 4608) {$FileStream.Write($Filebytesreceived, 0, $Filebytesreceived.Length); continue}
            else{                        
                $FileStream.Write($Filebytesreceived, 0, $Filebytesreceived.Length)
                $FileStream.Flush()
                $FileStream.Dispose()
                break
            }
            $FileStream.Flush()
            $FileStream.Dispose()
            break
        }
        Write-Verbose "[***] $remote has been retrieved succesfully"
        get-item $local                      
    }


  • Line #1:  Starts the Receive-file function. Remember that this for the server will be named Send-File. Following the same logic as when we renamed the send-file(server) function to receive-file(server). When the client executes receive-file we want our server to send us a file. Therefore, if we just leave the name as it is right now in the server, the server will not send a file to the client. 
  • Line #3-9: Defines the parameters available for the function. CmdletBinding() at the beginning is necessary to provide automatic features such as error handling. If you fat finger a parameter, the function will throw and error and tell you that the parameter does not exist. Without this feature, the function will just crash and break your loop and terminate your Client or Server.
  • Line #11-12: Starts an If/Else statement to test if the file that will be created to receive bytes from a remote file exists. If so, the file gets deleted locally. If not, it shows a message saying that the file does not exist and that a new one will be created.
  • Line #14: It initializes a new instance of the $FileStream class with the path of a specific desired path to create a file and append data to it. Parameters:
    • Path: A relative or absolute path for the file that current FileStream object will encapsulate. In this case it is the variable $local
    • mode: A constant that determines how to open or create the file. In this case we will create a file and append data to it. 
  • Line #15:  Sets buffer $BytesToReceive to 4608 which will be used to receive the data from the remote connection. Remember we set our send-file buffer to the same number.
  • Line #17: Starts a while loop which will run as long as the there are more bytes to run and when the last chunk of bytes are written to the new file. 
  • Line #19-26: Starts reading/retrieving data being sent via the shared stream (SSL). If there is nothing to read, it breaks the loop. If there is data to read, it reads it and stores it to a variable named $FileBytesreceived. Next, it uses the method Clear() from the Class Array in order to reset each element type's default value. In this case the array that will be reset is the $Receivebuffer array since it contains the data that was read. The parameters for the clear() method are the following:
    • Array: The array whose elements need to be cleared
    • index: Starting index of the range of elements to be clear
    • length: the number of elements to be clear (We can use the variable $Fileread since it contains the number of bytes successfully read. It does not clear the values of variable $fileread
  • Line #27-32: If the length of $fileread  is equal to 4608 (Buffer), it writes the bytes that it has read so far to the $FileStream, and starts another iteration in the loop. Else, it writes the last or only chunk of bytes to the $FileStream, and finally it breaks the loop.
  • Line #34-36: Clears the buffer for the $FileStream and causes any buffered data to be written to the local file. It also releases all resources used by $FileStream. Breaks the loop.
  • Line #39: locally the client checks if the file was created or exists locally now.
  • Line #40: Last bracket to close the Receive-file  function.




"Receive-File" Function (Server)

  • Remember that this function is renamed Send-file so that when the client sends the command Send-file to the server, it(server) executes the Receive-file code and stores the file on the server.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    function send-file
    {
        Param (
        [Parameter()]
        [string]$local,

        [Parameter()]
        [string]$remote
        )
        
        if (Test-Path $Remote){remove-item $Remote -Force}
        else {write-verbose "Local file does not exist. Creating a new one..."}

        $FileStream = New-Object IO.FileStream @($Receivefile, [IO.FileMode]::Append)
        $BytesToReceive = New-Object Byte[] 4608

        while ($true)
        {
            try {$Fileread = $SslStream.Read($BytesToReceive, 0, $BytesToReceive.Length)}
            catch {write-verbose $_.Exception.Message} 
              
            if($Fileread -eq 0){break}                  
            else{            
                [Array]$Filebytesreceived = $BytesToReceive[0..($Fileread -1)]
                [Array]::Clear($BytesToReceive, 0, $Fileread)
            }
                      
            if ($Fileread -eq 4608) {$FileStream.Write($Filebytesreceived, 0, $Filebytesreceived.Length); continue}
            else{                        
                $FileStream.Write($Filebytesreceived, 0, $Filebytesreceived.Length)
                $FileStream.Flush()
                $FileStream.Dispose()
                break
            }

            $FileStream.Flush()
            $FileStream.Dispose()
            break
        }      
        Write-Verbose "[***] $Remote has been created succesfully"                 
    }





"Invoke-Zip" Function (Server)
  • This goes on the server since it will be decompressing zip files.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    function invoke-unzip
    {
        write-verbose "[+++] Unzipping file.."
        [string]$RemoteFolderPath = ($env:USERPROFILE + "\")
        [int32]$copyOption = 20
        $shell = New-Object -ComObject shell.application
        $zip = $shell.Namespace($Receivefile)
            
        foreach($item in $zip.items()){
            $shell.Namespace($RemoteFolderPath).copyhere($item, $copyOption) | Out-Null
        }    
    }



"Invoke-space" Function (Client)

  • Used to refresh communications (specially when there is an error locally, and we just want to refresh the our two way communication.

1
2
3
4
5
6
    function invoke-space
    {
        $sendback = $EncodingType.GetBytes(' ')
        $SslStream.Write($sendback, 0, $sendback.Length)
        $SslStream.Flush() 
    }





Testing New Functionalities


Pre-Test requirements

  • Make sure you copy the functions after defining your parameters on each script. Also, make sure you check all your new variables being added to your code. 


Scenario:
  • You need to do the following:
    • Send your Toolkit over the compromised endpoint
    • Perform Live Memory Analysis
    • Dump process from memory without dumping the whole memory and bring it back to your local computer along with other evidence for analysis. 


  • Send your Toolkit over the compromised endpoint

Figure 1. Sending ToolKit





  • Perform Live Memory Analysis

Figure 2. Live Memory Analysis (Pslist - Rekall)






  • Dump process from memory without dumping the whole memory and bring it back to your local computer along with other evidence for analysis. 

Figure 3. Dumping process from Memory





Figure 4. Bringing Evidence back 





Figure 5. Break Connection. Stop Listener, close TCPClient and Streams gracefully





I hope this article was helpful to those testing and using open-source during IR engagements and that have been using other tools that make the process really time consuming and inefficient. This post is just the foundation of collection of artifacts. I have other scripts that I already use to integrate Power-Forensics during my investigations over this Client-server infrastructure. On my next post, I will be showing how to collect locked files ($MFT, Registry Hives, etc.) with Power-Forensics remotely over this type of infrastructure. 

These scripts are now available on my Github Repo:

https://github.com/VVard0g/CyberWardogLab 

Once again, most of the code that I present can be customized and be put under one script. You will just have to use switches for client and server functionalities. I just like to keep them separate for POC and learning purposes. 

Feedback is always appreciated !! 


Updates:

11/11/2016 - Updated Code. Made Send and Receive monitoring parts a little bit easier. 
01/16/2017 - Uploaded scripts to Github and cleaned some of the code



References:


https://msdn.microsoft.com/en-us/library/system.io.filestream(v=vs.110).aspx FileStream Class
https://msdn.microsoft.com/en-us/library/883dhyx0(v=vs.110).aspx SeekOrigin
https://msdn.microsoft.com/en-us/library/system.io.filemode(v=vs.110).aspx File Mode
https://github.com/secabstraction/PowerCat (Inspiration for this project)