Sunday, July 26, 2020

Powershell - Evaluate Timestamps of Files in a Directory

I was looking for a script to output files in a directory that may have been changed or created in a given time frame.  The time stamps that are evaluated are the MFT File Record for the file, the creation time, last access time and last write time.  As I was building this, I found a powershell project called PowerForensics which is very nice for more in-depth project related to forensics.

Here is the script use at your own risk:

   

# Modify the directory that you are looking at and the timestamps

$currentDirectory = "c:\windows"
$files = Get-ChildItem $currentDirectory
# Change the below line to look like the below if you want to recursively look at the files in a directory
#$files = Get-ChildItem $currentDirectory -Recurse
$startDate = Get-Date 2020-03-01
$endDate = Get-Date 2020-06-30

# Only outputs results where a creation time, last access time, and last write time are within the timeframe listed above
# Added the function to look at the MFT File Record for the file being examined.  If that time is within the timeframe it 
# is also captured...
#
# Looking at the MFT File Record will indicate time stomping...

Function Color-Text {
    param ( $inDate)
    If (($inDate -ge $startDate) -and ($inDate -le $endDate)) {
        Write-Host "$($inDate) " -ForegroundColor Yellow -NoNewline
    }
    Else {
        Write-Host "$($inDate) " -NoNewline
    }
}

Function Get-ChangeTime {
    # This function is from https://gallery.technet.microsoft.com/scriptcenter/Get-MFT-Timestamp-of-a-file-9227f399
    # Modified to work in this context.

    param ( $inFile )
    $FileStream = [System.IO.File]::Open($inFile, 'Open', 'Read', 'ReadWrite')
    #$FileStream

    #[void][ntdll]
    #region Module Builder
    $Domain = [AppDomain]::CurrentDomain
    $DynAssembly = New-Object System.Reflection.AssemblyName('TestAssembly')
    $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) # Only run in memory
    $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('TimeStampModule', $False)
    #endregion Module Builder

    #region ENUMs
    $EnumBuilder = $ModuleBuilder.DefineEnum('FileInformationClass', 'Public', [UInt32])
    # Define values of the enum
    [void]$EnumBuilder.DefineLiteral('FileDirectoryInformation', [UInt32] 1)
    [void]$EnumBuilder.DefineLiteral('FileBasicInformation', [UInt32] 4)
    [void]$EnumBuilder.DefineLiteral('FileModeInformation', [UInt32] 16)
    [void]$EnumBuilder.DefineLiteral('FileHardLinkInformation', [UInt32] 46)

    #Create ENUM Type
    [void]$EnumBuilder.CreateType()
    #endregion ENUMs

   
    #region FileBasicInformation
    #Define STRUCT
    $Attributes = 'AutoLayout, AnsiClass, Class, ExplicitLayout, Sealed, BeforeFieldInit,public'
    $TypeBuilder = $ModuleBuilder.DefineType('FileBasicInformation', $Attributes, [System.ValueType], 8, 0x28)
    $CreateTimeField = $TypeBuilder.DefineField('CreationTime', [UInt64], 'Public')
    $CreateTimeField.SetOffset(0)
    $LastAccessTimeField = $TypeBuilder.DefineField('LastAccessTime', [UInt64], 'Public')
    $LastAccessTimeField.SetOffset(8)
    $LastWriteTimeField = $TypeBuilder.DefineField('LastWriteTime', [UInt64], 'Public')
    $LastWriteTimeField.SetOffset(16)
    $ChangeTimeField = $TypeBuilder.DefineField('ChangeTime', [UInt64], 'Public')
    $ChangeTimeField.SetOffset(24)
    $FileAttributesField = $TypeBuilder.DefineField('FileAttributes', [UInt64], 'Public')
    $FileAttributesField.SetOffset(32)
    #Create STRUCT Type
    [void]$TypeBuilder.CreateType()
    #endregion FileBasicInformation

    #region IOStatusBlock
    #Define STRUCT
    $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
    $TypeBuilder = $ModuleBuilder.DefineType('IOStatusBlock', $Attributes, [System.ValueType], 1, 0x10)
    [void]$TypeBuilder.DefineField('status', [UInt64], 'Public')
    [void]$TypeBuilder.DefineField('information', [UInt64], 'Public')
    #Create STRUCT Type
    [void]$TypeBuilder.CreateType()
    #endregion IOStatusBlock

    #region DllImport    $TypeBuilder = $ModuleBuilder.DefineType('ntdll', 'Public, Class')    #region NtQueryInformationFile Method    $PInvokeMethod = $TypeBuilder.DefineMethod(        'NtQueryInformationFile', #Method Name        [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes        [IntPtr], #Method Return Type        [Type[]] @([Microsoft.Win32.SafeHandles.SafeFileHandle], [IOStatusBlock], [IntPtr] ,[UInt16], [FileInformationClass]) #Method Parameters    )
    $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))    $FieldArray = [Reflection.FieldInfo[]] @(        [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),        [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')    )
    $FieldValueArray = [Object[]] @(        'NtQueryInformationFile', #CASE SENSITIVE!!        $True    )
    $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(        $DllImportConstructor,        @('ntdll.dll'),        $FieldArray,        $FieldValueArray    )
    $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)    #endregion NtQueryInformationFile Method
    [void]$TypeBuilder.CreateType()    #endregion DllImport


    $fbi = New-Object "FileBasicInformation"
    $iosb = New-Object "IOStatusBlock"
    $p_fbi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($fbi))
    
    # Pull file timestamps from file
    #[DllImport("ntdll.dll", SetLastError=$true)] 
    $iprc = [ntdll]::NtQueryInformationFile($FileStream.SafeFileHandle, $iosb, $p_fbi, 
        [System.Runtime.InteropServices.Marshal]::SizeOf($fbi), [FileInformationClass]::FileBasicInformation
    )

    

    # Check to make sure no issues occurred
    $IsOK = (($iprc -eq [intptr]::Zero) -AND ($iosb.status -eq 0))

    If ($IsOK) {
        # Pull data from unmanaged memory block into a usable object
        # The below line in the original document does notwork.  Add [System.Type] in the front and it works...
        # https://poshsecurity.com/blog/2014/2/3/powershell-error-the-specified-structure-must-be-blittable-o.html
        $fbi = [System.Runtime.InteropServices.Marshal]::PtrToStructure($p_fbi, [System.Type][FileBasicInformation])
        #$Object = [pscustomobject]@{
        #    FullName = $FileStream.Name
        #    CreationTime = [datetime]::FromFileTime($fbi.CreationTime)
        #    LastAccessTime = [datetime]::FromFileTime($fbi.LastAccessTime)
        #    LastWriteTime = [datetime]::FromFileTime($fbi.LastWriteTime)
        #    ChangeTime = [datetime]::FromFileTime($fbi.ChangeTime)
        #}
        #$Object.PSTypeNames.Insert(0,'System.Io.FileTimeStamp')
        return [datetime]::FromFileTime($fbi.ChangeTime)
    } Else {
        return "$($Item): $(New-Object ComponentModel.Win32Exception)"
    }
    #region Perform Cleanup
    $FileStream.Close()
    # Deallocate memory
    If ($p_fbi -ne [intptr]::Zero) {
        [System.Runtime.InteropServices.Marshal]::FreeHGlobal($p_fbi)
    }

}


ForEach ($file in $files) {
    $creationTime = $file.CreationTime
    $lastAccessTime = $file.LastAccessTime
    $lastWriteTime = $file.LastWriteTime
    Try {
        If ($file.Attributes -notmatch 'Directory') {
            $changeTime = Get-ChangeTime -inFile $file.FullName
        }
        Else {
            # Arbitrary change time if one is not found by the function...
            $changeTime = (Get-Date).AddYears(-20)
        }
    }
    Catch {
        # Arbitrary change time if one is not found by the function...
        $changeTime = (Get-Date).AddYears(-20)
    }


    # Only output what matches the criteria above
    # If the MFT time of creation matches the timeframe it is output
    If ((($creationTime -ge $startDate) -and ($creationTime -le $endDate)) -or (($lastAccessTime -ge $startDate) -and ($lastAccessTime -le $endDate)) -or (($lastWriteTime -ge $startDate) -and ($lastWriteTime -le $endDate)) -or (($changeTime -ge $startDate) -and ($changeTime -le $endDate))) {
        Write-Host "-- File Information --"
        Write-Host "Name: " -NoNewLine  -ForegroundColor Green
        Write-Host "$($file.FullName)"
        Write-Host "Attributes: " -NoNewline -ForegroundColor Green
        Write-Host "$($file.Attributes)" 
        Write-Host "Length: " -NoNewline -ForegroundColor Green
        Write-Host "$($file.Length)"
        If ($file.Attributes -notmatch 'Directory') {
            $changeTime = Get-ChangeTime -inFile $file.FullName
            Write-Host "MFT Change Time: " -NoNewline -ForegroundColor Green
            Write-Host (Color-Text -inDate $changeTime)
        }
        Write-Host "Create: " -NoNewline -ForegroundColor Green
        Write-Host (Color-Text -inDate $creationTime) -NoNewline
        Write-Host "Last Access Time: " -NoNewline -ForegroundColor Green
        Write-Host (Color-Text -inDate $lastAccessTime) -NoNewline
        Write-Host "Last Write Time: " -NoNewline -ForegroundColor Green
        Write-Host (Color-Text -inDate $lastWriteTime)
        If ($file.Attributes -notmatch 'Directory') {
            $md5 = (Get-FileHash -LiteralPath $file.FullName -Algorithm MD5 -ErrorAction SilentlyContinue).Hash
            $sha1 = (Get-FileHash -LiteralPath $file.FullName -Algorithm SHA1 -ErrorAction SilentlyContinue).Hash
            $sha256 = (Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
            Write-Host "-- Hashes --"
            Write-Host "MD5: " -NoNewline -ForegroundColor Green
            Write-Host $md5 
            Write-Host "SHA1: " -NoNewline -ForegroundColor Green
            Write-Host $sha1
            Write-Host "SHA256: " -NoNewline -ForegroundColor Green
            Write-Host $sha256
        }
        Write-Host "`r`n`r`n"
    }
    
}


Saturday, July 18, 2020

Python3 Script Obfuscator

A while ago I wrote a python script obfuscator for python2.  Today I needed this and rewrote it for python3.


#!/usr/bin/python3

import base64
import zlib
import bz2

def applyb64(i):
 print("base64 Encoding the Information")
 headerInfo = '#!/usr/bin/python3\n'
 headerInfo += 'import base64;exec(base64.b64decode("'
 encodedInfo = base64.b64encode(i.encode("utf-8"))
 footerInfo = '"))'
 outputInfo = headerInfo + str(encodedInfo, "utf-8") + footerInfo
 return outputInfo

def applyZLIB(i):
 print("zlib Compress the Information")
 compressionLevel = input("Select compression level (1-9): ")
 headerInfo = '#!/usr/bin/python3\n'
 headerInfo += 'import zlib, base64;z=base64.b64decode("'
 encodedInfo = base64.b64encode(zlib.compress(i.encode("utf-8"), int(compressionLevel)))
 footerInfo = '");y=zlib.decompress(z);exec(y)'
 outputInfo = headerInfo + str(encodedInfo, "utf-8") + footerInfo
 return outputInfo

def applyBZ2(i):
 print("bz2 Compress the Information")
 compressionLevel = input("Select compression level (1-9): ")
 headerInfo = '#!/usr/bin/python3\n'
 headerInfo += 'import bz2, base64;w=base64.b64decode("'
 encodedInfo = base64.b64encode(bz2.compress(i.encode("utf-8"), int(compressionLevel)))
 footerInfo = '");r=bz2.decompress(w);exec(r)'
 outputInfo = headerInfo + str(encodedInfo, "utf-8") + footerInfo
 return outputInfo

def applyXOR(i):
 print("XOR Information")
 hexValue = input("XOR INT Value: ")
 headerInfo = '#!/usr/bin/python3\n'
 headerInfo += 'import base64;j=bytearray(base64.b64decode("'
 bArray = bytearray(i.encode("utf-8"))
 for b in range(len(bArray)):
  bArray[b] ^= int(hexValue)
 encodedInfo = base64.b64encode(bArray)
 footerInfo = '"));\n'
 footerInfo += 'for c in range(len(j)): j[c] ^= ' + hexValue + '\n'
 footerInfo += 'exec(str(j, "utf-8"))'
 outputInfo = headerInfo + str(encodedInfo, "utf-8") + footerInfo
 return outputInfo


def executeRecipe(r):
 outputPython = "outFile.py"
 print 
 filename = input("Filename to apply recipe: ")
 print
 info = ''
 f = open(filename, "r")
 for line in f:
  if '#!/usr/bin/python3' not in line:
   info += line
 f.close()
 for recipe in r:
  if recipe == "b64":
   outputRecipe = applyb64(info)
   info = outputRecipe
  elif recipe == "XOR":
   outputRecipe = applyXOR(info)
   info = outputRecipe
  elif recipe == "zlib":
   outputRecipe = applyZLIB(info)
   info = outputRecipe
  elif recipe == "bz2":
   outputRecipe = applyBZ2(info)
   info = outputRecipe
 f = open(outputPython, "w")
 f.write(outputRecipe)
 f.close()
 



def main():
 recipes = []
 selection = 'a'
 print
 print("Build a Encoded/Compressed File from a Recipe you Build")
 while selection != 'q' and selection != 'Q':
  print("Select which task to fulfill:")
  print("1. Base64 Encode")
  print("2. XOR")
  print("3. zlib Compress")
  print("4. bz2 Compress")
  print
  print("D. Display Recipe")
  print("E. Execute Recipe")
  print("Q. Quit")
  selection = input("> ")
  if selection == '1':
   print
   print("base64")
   recipes.append("b64")
   print
  elif selection == '2':
   print
   print("XOR")
   recipes.append("XOR")
   print
  elif selection == '3':
   print
   print("zlib")
   recipes.append("zlib")
   print
  elif selection == '4':
   print
   print("bz2")
   recipes.append("bz2")
   print
  elif selection == "D" or selection == "d":
   print
   print("Recipe:")
   for recipe in recipes:
    print(recipe)
   print
  elif selection == "E" or selection == "e":
   print
   print("Execute the Recipe")
   executeRecipe(recipes)
   print
   recipes = []


if __name__ == "__main__":
    main()
  


Thursday, May 21, 2020

Create the Base for a Word Search

The below script was built to generate random characters from the alphabet. The result can be used to create a word search. I ultimately placed the results in Excel and then over-layed the word search.


#!/usr/bin/python3

import random

charArray = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
charStr = ''

for i in range(1,50):
    for j in range(1,50):
        charStr = charStr + charArray[random.randint(0,29)] + ","
    print(charStr[:-1])
    charStr = ''

Monday, May 11, 2020

List of Service Principal Names (SPNs) amongst AD Users

Here is a simple powershell script to list the service principal names 
among user accounts in a Windows domain. Understanding why the SPNs
exist and how they could be abused is important. $info = Get-ADUser -Filter * -Properties ServicePrincipalNames ForEach ($user in $info) { $samAccountName = $user.SamAccountName If ($user.ServicePrincipalNames -ne $null) { ForEach ($spn in $user.ServicePrincipalNames) { "$($samAccountName) $spn" } } }

Saturday, May 9, 2020

Ansible Playbook for Installing Vectr.io

Recently, I was introduced to Ansible and Vectr.io.  I chose to create a playbook to install Vectr.io on Ubuntu 18.04.  I also created a playbook to install docker.  Below are the playbooks that I created, you will need to modify for your environment.  


Docker Install Playbook

---
- name: This playbook adds the docker packages if necessary to Ubuntu 18.04 nd 20.04
# No docker repo for 20.04 of focal fosse - Using docker-ce from 18.04
hosts: all
become: 'yes'

tasks:
- name: Add Docker GPG apt Key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present

- name: Install Docker Dependencies
apt:
pkg:
- curl
- unzip
- apt-transport-https
- ca-certificates
- software-properties-common
- python3-pip
- virtualenv
- python3-setuptools
update_cache: yes
state: latest

- name: Add Docker Repository
raw: add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
args:
executable: /bin/bash

- name: Install Docker Packages
apt:
pkg:
- docker-ce
- docker-ce-cli
- docker-compose
update_cache: yes
state: latest

- name: Install Docker Module for Python
pip:
name: docker

- name: Enable the docker service (May not be necessary)
systemd:
name: docker
enabled: yes
masked: no

- name: Verify the docker service is running
systemd:
name: docker
state: started


Vectr.io Install Playbook

---
- name: This playbook installs Vectr through Docker
# https://docs.vectr.io/Installation/
# Dependent on docker being installed on Ubuntu
# apt install python-pip python-dev if not installed
# pip install setuptools on ansible server
# Dependent on ansible server having docker compose installed pip install docker-compose
# You will get a CAS error until you set your /etc/hosts file to the IP hosting the docker container with vectr.local
# Connect with https://vectr.local:8081
hosts: all
# Comment the below line if you have root privileges
become: 'yes'

vars:
currentUser: "thepcn3rd"
VECTR_HOSTNAME: "vectr.local"
VECTR_DATA_KEY: "SomethingS1mple0"
CAS_ENCRYPT_MONGO_KEY: "SomethingS1mpl30"
MONGO_INITDB_ROOT_PASSWORD: "Someth1ngSimple"

tasks:
- name: Add /opt/vectr Directory
file:
path: /opt/vectr
state: directory
owner: "{{ currentUser }}"
group: "{{ currentUser }}"
mode: '0755'

- name: Download Vectr release 5.5.7 into /opt/vectr Directory
get_url:
url: https://github.com/SecurityRiskAdvisors/VECTR/releases/download/ce-5.5.7/sra-vectr-runtime-5.5.7-ce.zip
dest: /opt/vectr/vectr.zip

- name: Change permissions on the Downloaded File
file:
path: /opt/vectr/vectr.zip
owner: "{{ currentUser }}"
group: "{{ currentUser }}"
mode: '0644'

- name: Extract vectr.zip file
unarchive:
src: /opt/vectr/vectr.zip
dest: /opt/vectr
remote_src: yes

- name: Change permissions on Extracted Files
file:
path: /opt/vectr
state: directory
recurse: yes
owner: "{{ currentUser }}"
group: "{{ currentUser }}"
mode: '0644'

- name: Change permissions on Directory
file:
path: /opt/vectr
state: directory
owner: "{{ currentUser }}"
group: "{{ currentUser }}"
mode: '0755'


- name: Change .env file for Vectr prior to deployment VECTR_HOSTNAME
replace:
path: /opt/vectr/.env
regexp: '^VECTR_HOSTNAME=.+'
replace: 'VECTR_HOSTNAME={{ VECTR_HOSTNAME }}'

- name: Change .env file for Vectr prior to deployment VECTR_DATA_KEY
replace:
path: /opt/vectr/.env
regexp: '^VECTR_DATA_KEY=.+'
replace: 'VECTR_DATA_KEY={{ VECTR_DATA_KEY }}'

- name: Change .env file for Vectr prior to deployment CAS_ENCRYPT_MONGO_KEY
replace:
path: /opt/vectr/.env
regexp: '^CAS_ENCRYPT_MONGO_KEY=.+'
replace: 'CAS_ENCRYPT_MONGO_KEY={{ CAS_ENCRYPT_MONGO_KEY }}'

- name: Change .env file for Vectr prior to deployment MONGO_INITDB_ROOT_PASSWORD
replace:
path: /opt/vectr/.env
regexp: '^MONGO_INITDB_ROOT_PASSWORD=.+'
replace: 'MONGO_INITDB_ROOT_PASSWORD={{ MONGO_INITDB_ROOT_PASSWORD }}'

- name: Docker compose the vectr files we have prepared
raw: "docker-compose -f /opt/vectr/docker-compose.yml up -d"
args:
executable: /bin/bash




Thursday, April 30, 2020

Hidden Accounts in Active Directory

While taking a SANS course we went through an exercise to learn how adversary's hide accounts through modifying the access controls.  During the class I created this script to iterate through the access controls placed on the objects in Active Directory and compares it to what is seen with the commands of Get-ADUser, Get-ADComputer and Get-ADGroup.  This script identified the hidden account.

Below is the powershell for the script.  Use with caution, only used in a test environment.




# Script built to detect suspicous entries in AD access control lists
# A paper written by Will Schroeder and Lee Christensen about "An Ace up the Sleeve" highlights hidding objects with denied privileges
# This script analyzes what is in the access control lists and then finds objects that are not listed in Get-ADUsers, Get-ADComputers and Get-ADGroups

# Version 0.2
# Fixes - In the test lab I could go off of the .Name object of an Account, Group or Computer.  In a production environment the SamAccountName is necessary
#       - Removed the compList because the SamAccountName of a computer has a $
#       - The list of computers changed in the evaluation from compList to computerList due to the change above


Import-Module ActiveDirectory

# Pull in the list of user accounts output from Get-ADUser
$userList = (Get-ADUser -Filter *).SamAccountName

# Pull in the list of groups output from Get-ADGroup
$groupList = (Get-ADGroup -Filter *).SamAccountName

# Pull in the list of computers from Get-ADComputer
$computerList = (Get-ADComputer -Filter *).SamAccountName      


$domain = $env:USERDOMAIN

# Capture interesting accounts
$interestingAccounts = @()

# Initial concept was to only look at the Organizational Units - Expanded to All Objects in the Domain
#$OUs = Get-ChildItem -Recurse -LiteralPath "AD:\DC=sec699-32,DC=lab" | Where ObjectClass -eq organizationalUnit

# Pull all of the Objects in the Domain into a Variable
$adObjects = Get-ChildItem -Recurse -LiteralPath "AD:\DC=sec699-32,DC=lab" 
""
"Display Suspicious Access Privileges of an Object that is not Listed by Get-ADUser, Get-ADComputer or Get-ADGroup"
ForEach ($object in $adObjects) {
    # Pull the access of each object
    $accessRights = (Get-ACL -Path $object.PSpath).Access
    # Iterate over each Access Right
    ForEach ($accessRight in $accessRights) {
        # If the IdentityReference in the Access Right contains the domain continue
        If ($accessRight.IdentityReference -like "*$($domain)*") {
            # Split the domain name from the account, group or computername
            $domainName, $identity = $accessRight.IdentityReference.ToString().Split("\")
            # Check to see if the account, group or computername exists in the lists pulled above from Get-ADUser, Get-ADComputer and Get-ADGroup
            If (($identity -notin $userList) -and ($identity -notin $groupList) -and ($identity -notin $computerList)) {
                # If the identity does not exist add it to the interesting accounts array
                If ($identity -notin $interestingAccounts) {
                    $interestingAccounts += $identity
                }
                # Display the AD Object that is Interesting and that contains an identity that does not exist by default viewing

                # Uncomment the below line to see the verbose output of each object
                #
                #"ADObject:$($object.name) ObjectClass:$($object.ObjectClass) DN:$($object.DistinguishedName) Account:$($accessRight.IdentityReference) Access:$($accessRight.AccessControlType) $($accessRight.ActiveDirectoryRights)"
            }
        }
    }
}
"`n`n"
"Interesting Accounts, Groups or Computers Identified"
$interestingAccounts





Test Authentication from Linux Console using python3 pexpect

Working with the IT420 lab, you will discover that we need to discover a vulnerable user account.  The following python3 script uses the pex...