Beyond
This is the beyond page that an additional post enumeration and assessment are conducted as SYSTEM after compromising the target system.
Scheduled Tasks
PS C:\Users\Administrator> Get-ScheduledTask | where {$_.TaskPath -notlike "\Microsoft*" } | ft TaskName,TaskPath,State
TaskName TaskPath State
-------- -------- -----
Clean builds and build definitions \ Ready
Restore Git repos \ Ready
Clean builds and build definitions
ps c:\Users\Administrator> cmd /c schtasks /QUERY /TN "\Clean builds and build definitions" /V /FO LIST
folder: \
hostname: WORKER
taskname: \Clean builds and build definitions
next run time: 2023-11-24 03:41:00
status: Ready
logon mode: Interactive/Background
last run time: 2023-11-24 03:40:00
last result: 0
author: WORKER\Administrator
task to run: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ep bypass c:\Users\restorer\source\repos\Clean-Builds.ps1
start in: N/A
comment: N/A
scheduled task state: Enabled
idle time: Disabled
power management: Stop On Battery Mode, No Start On Batteries
run as user: WORKER\restorer
delete task if not rescheduled: Disabled
stop task if runs x hours and x mins: 72:00:00
schedule: Scheduling data is not available in this format.
schedule type: One Time Only, Minute
start time: 08:24:00
start date: 2020-08-13
end date: N/A
days: N/A
months: N/A
repeat: Every: 0 Hour(s), 1 Minute(s)
repeat: Until: Time: None
repeat: Until: Duration: Disabled
repeat: Stop If Still Running: Disabled
executing c:\Users\restorer\source\repos\Clean-Builds.ps1
every minute as the restorer
account
Clean-Builds.ps1
PS C:\Users\Administrator> cat c:\Users\restorer\source\repos\Clean-Builds.ps1
# Reference: https://docs.microsoft.com/en-us/rest/api/azure/devops/?view=azure-devops-server-rest-5.0
#
######################################################################################################
$projects = @{}
#$tfsBaseUrl = "http://devops.worker.htb/ekenas/" # This url is for external access, only used for testing
$tfsBaseUrl = "http://127.0.0.1:8080/ekenas/" # This url is for internal access, used by the local restorer account
$pat = "7xqg2p3sxh65mysw3qthykd4iwgonjm2qwv4sm2zawe4hcbsb2pa" # This PAT (Full Access) has been created by the Administrator Account in the Azure Devops Portal
# It needs to be renewed every 12 month since it will expire after this timeout
# If it is not renewed after 12 months the builds won't be cleaned by this script since the PAT is rejected
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($pat)"))
$header = @{authorization = "Basic $token"}
$now = Get-Date
# -------------------------------------------------------------------------------------------------------------------------------------------------
# In case of users getting locked out, increase these values to allow the definitions and builds to exist for a longer time before wiping them
# Note that when deleting a build definition, all builds executed on that definition will be deleted as well. This is mainly a concern for the
# PartsUnlimited Project, since it is only there a user can create definitions.
Set-Variable DEFINITION_MAX_AGE_MINUTES -Option Constant -Value 1
Set-Variable BUILD_MAX_AGE_MINUTES -Option Constant -Value 3
# -------------------------------------------------------------------------------------------------------------------------------------------------
# =================================================================================================================================================
function Get-Projects {
$projectsUrl = "$($tfsBaseUrl)_apis/projects?api-version=5.0"
$projects = Invoke-RestMethod -Uri $projectsUrl -Method Get -ContentType "application/json" -Headers $header
return $projects.value
}
function Get-BuildDefinitions {
param (
[string]$projectName
)
$definitionUrl = "$($tfsBaseUrl)/$($projectName)/_apis/build/definitions?includeLatestBuilds=true&api-version=5.0"
$definitions = Invoke-RestMethod -Uri $definitionUrl -Method Get -ContentType "application/json" -Headers $header
$definitionsMarkedForDeletion = @()
$definitions.value | ForEach-Object {
$createdDate = [datetime]::Parse($_.createdDate)
$timeDifference = $now - $createdDate
# Definition that has been executed at least once will be marked for deletion
if ($_.latestCompletedBuild -ne $null) {
$definitionsMarkedForDeletion += $_
}
# Definition is older than specified DEFINITION_MAX_AGE_MINUTES minutes will be marked for deleteion
if ($timeDifference.TotalMinutes -ge $DEFINITION_MAX_AGE_MINUTES) {
$definitionsMarkedForDeletion += $_
}
}
return $definitionsMarkedForDeletion
}
function Get-Builds {
param (
[string]$projectName
)
# Get Builds
$buildUrl = "$($tfsBaseUrl)/$($projectName)/_apis/build/builds?api-version=5.0"
$builds = Invoke-RestMethod -Uri $buildUrl -Method Get -ContentType "application/json" -Headers $header
$buildsMarkedForDeletion = @()
$builds.value | ForEach-Object {
$queueTime = [datetime]::Parse($_.queueTime)
$timeDifference = $now - $queueTime
# Build that has been executed will be marked for deletion
if ($_.finishTime -ne $null) {
$buildsMarkedForDeletion += $_
}
# Build is older than specified value minutes will be marked for deleteion
if ($timeDifference.TotalMinutes -ge $BUILD_MAX_AGE_MINUTES) {
$buildsMarkedForDeletion += $_
}
}
return $buildsMarkedForDeletion
}
function Delete-BuildDefinitions {
param (
[Parameter()]
[string]$projectName,
[Parameter()][PSCustomObject[]]
$definitions
)
if ($definitions.Count -gt 0)
{
Write-Host "Deleting the following definitions in $projectName"
Write-Host "--------------------------------------------------"
$definitions | ForEach-Object {
Write-Host "`t" $_.name
$deleteDefinitionUrl = "$($tfsBaseUrl)/$($projectName)/_apis/build/definitions/$($_.id)?api-version=5.0"
$result = Invoke-RestMethod -Uri $deleteDefinitionUrl -Method Delete -ContentType "application/json" -Headers $header
}
}
}
function Delete-Builds {
param (
[Parameter()]
[string]$projectName,
[Parameter()]
[PSCustomObject[]]$builds
)
if ($builds.Count -gt 0)
{
Write-Host "Deleting the following builds in $projectName"
Write-Host "---------------------------------------------"
$builds | ForEach-Object {
Write-Host "`t" $_.id
$deleteBuildUrl = "$($tfsBaseUrl)/$($projectName)/_apis/build/builds/$($_.id)?api-version=5.0"
$result = Invoke-RestMethod -Uri $deleteBuildUrl -Method Delete -ContentType "application/json" -Headers $header
}
}
}
# ==================================================================================================================================================
$definitions = Get-BuildDefinitions "PartsUnlimited"
$projects = Get-Projects
$projects | ForEach-Object {
$projectName = $_.name
$builds = Get-Builds $projectName
Delete-Builds $projectName $builds
}
# We have to delete the builds before deleting the definitions due to the fact that builds can be seen in the Agent log otherwhise
Delete-BuildDefinitions "PartsUnlimited" $definitions
Restore Git repos
ps c:\Users\Administrator> cmd /c schtasks /QUERY /TN "\Restore Git repos" /V /FO LIST
folder: \
hostname: WORKER
taskname: \Restore Git repos
next run time: 2023-11-24 03:44:00
status: Ready
logon mode: Interactive/Background
last run time: 2023-11-24 03:34:01
last result: 0
author: WORKER\Administrator
task to run: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ep bypass c:\Users\restorer\source\repos\Restore-Repos.ps1
start in: N/A
comment: N/A
scheduled task state: Enabled
idle time: Disabled
power management: Stop On Battery Mode, No Start On Batteries
run as user: WORKER\restorer
delete task if not rescheduled: Disabled
stop task if runs x hours and x mins: 72:00:00
schedule: Scheduling data is not available in this format.
schedule type: One Time Only, Minute
start time: 08:24:00
start date: 2020-08-13
end date: N/A
days: N/A
months: N/A
repeat: Every: 0 Hour(s), 10 Minute(s)
repeat: Until: Time: None
repeat: Until: Duration: Disabled
repeat: Stop If Still Running: Disabled
executing c:\Users\restorer\source\repos\Restore-Repos.ps1
every 10 minutes as the restorer
account
Restore-Repos.ps1
PS C:\Users\Administrator> cat c:\Users\restorer\source\repos\Restore-Repos.ps1
# Restore-repos.ps1
$Repos = @("alpha","dimension","lens","solid-state","spectral","story","twenty","cartoon","SmartHotel360")
$Repos | foreach {
Write-Host "Processing $_"
cd C:\Users\restorer\source\repos\$_
cmd /c 'git.exe push origin --force'
cmd /c 'git.exe ls-remote --heads origin | grep.exe -ve "master$" | cut.exe -d / -f3 | xargs.exe git.exe push origin --delete'
cd ..
}
Administrator
user
ps c:\Users\Administrator> tree /F /A
tree /F /A
Folder PATH listing
Volume serial number is 32D6-9041
c:.
| azure-devops.exe
|
+---3D Objects
+---Contacts
+---Desktop
| root.txt
|
+---Documents
| Add-Restorer-Tasks.ps1
| Deny-Write.ps1
| Revoke-IIS-PrimaryTokenPrivilege.ps1
| UserRights.psm1
|
+---Downloads
+---Favorites
+---Links
+---Music
+---Pictures
+---Saved Games
+---Searches
\---Videos
Add-Restorer-Tasks.ps1
PS C:\Users\Administrator> cat Documents\Add-Restorer-Tasks.ps1
# Resources
#https://www.windows-commandline.com/schedule-tasks-command-line/
Import-Module .\UserRights.psm1
Grant-UserRight -Account restorer -Right SeBatchLogonRight
##############################################################################################################################################################
# These values can be changed to increase/decrease the cleaning operations
# A to HIGH value will make it likelly to cause spoilers, due to the fact that one htb user might see what another has been up to.
# A to LOW value might meen the operation might not have time to finish before the cleaning kicks in.
# I have added some checks in the build and build definitions cleaning script (Clean-Builds.ps1) that checks:
# 1. if a build has finished before removing it
# 2. if a build definition has been "executed" once before removing it
# Due to the fact of the above checks I have set the cleaning timeout to a lower value for the builds and build definitions than the git repo restore
##############################################################################################################################################################
$GIT_RESTORE_TIMEOUT = 10
$CLEAN_BUILDS_TIMEOUT = 1
##############################################################################################################################################################
# Tasks to restore git repos
schtasks /create /RU restorer /RP IHopeThisPassword1sSafeEnough /SC minute /MO $GIT_RESTORE_TIMEOUT /TN "Restore Git repos" /TR "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ep bypass c:\Users\restorer\source\repos\Restore-Repos.ps1"
# Task to clean builds and build definitions
schtasks /create /RU restorer /RP IHopeThisPassword1sSafeEnough /SC minute /MO $CLEAN_BUILDS_TIMEOUT /TN "Clean builds and build definitions" /TR "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ep bypass c:\Users\restorer\source\repos\Clean-Builds.ps1"
Generating those 2 scheduled tasks above
Deny-Write.ps1
ps c:\Users\Administrator> cat Documents\Deny-Write.ps1
$inheritanceflag = [system.security.accesscontrol.inheritanceflags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
$propagationflag = [system.security.accesscontrol.propagationflags]:: None
$accesscontroltype = $objtype = [system.security.accesscontrol.accesscontroltype]::Deny
$DenyWriteRule = New-Object System.Security.AccessControl.FileSystemAccessRule("IIS APPPOOL\DEFAULTAPPPOOL", "write", $InheritanceFlag, $PropagationFlag, $AccessControlType)
$directories = get-childitem w:\ -Directory
$directories | foreach {
$acl = Get-Acl $_.fullname
$acl.SetAccessRule($DenyWriteRule)
$acl | Set-Acl $_.fullname
}
denies the iis apppool\defaultapppool
account from accessing the w:
drive
Revoke-IIS-PrimaryTokenPrivilege.ps1
PS C:\Users\Administrator> cat Documents\Revoke-IIS-PrimaryTokenPrivilege.ps1
Import-Module .\UserRights.psm1
# Note: The SeAssignPrimaryTokenPrivilege seems to be restored for "iis apppool\defaultapppool" after a little while/reboot
Revoke-UserRight "iis apppool\defaultapppool" -Right SeAssignPrimaryTokenPrivilege
Revoke-UserRight "iis apppool\.NET v4.5" -Right SeAssignPrimaryTokenPrivilege
Revoke-UserRight "iis apppool\.NET v4.5 Classic" -Right SeAssignPrimaryTokenPrivilege
Attempts to revoke the SeAssignPrimaryTokenPrivilege from the following accounts;
iis apppool\defaultapppool
iis apppool\.NET v4.5
iis apppool\.NET v4.5 Classic
This obviously failed
UserRights.psm1
This appears to be an external resource. mostly identical to this
restorer
User
PS C:\Users\restorer> ls
Directory: C:\Users\restorer
Mode LastWriteTime Length Name
---- ------------- ------ ----
d-r--- 2018-09-15 09:12 Desktop
d-r--- 2020-07-07 17:53 Documents
d-r--- 2018-09-15 09:12 Downloads
d-r--- 2018-09-15 09:12 Favorites
d-r--- 2018-09-15 09:12 Links
d-r--- 2018-09-15 09:12 Music
d-r--- 2018-09-15 09:12 Pictures
d----- 2018-09-15 09:12 Saved Games
d----- 2020-07-07 17:53 source
d-r--- 2018-09-15 09:12 Videos
-a---- 2020-07-08 19:46 419 Note.txt
Notes.txt
ps c:\Users\restorer> cat Note.txt
Interesting, very interesting...
You reached this location and that was not intented.
This account is has been created to prevent cheating.
It will actually restore the git repos and clean up build logs and definitions
on the Azure Devops Server in an attempt to prevent spoilers.
Please - pretty please - do not modify anything here since that might spoil solutions for others.
Kind Regards
- ekenas
Interesting..
source\repos
PS C:\Users\restorer> ls source
Directory: C:\Users\restorer\source
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2020-07-22 01:11 repos
PS C:\Users\restorer> cd source/repos ; ls
Directory: C:\Users\restorer\source\repos
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2020-07-07 17:59 alpha
d----- 2020-07-07 17:59 cartoon
d----- 2020-07-07 17:59 dimension
d----- 2020-07-07 17:59 lens
d----- 2020-07-09 16:32 SmartHotel360
d----- 2020-07-07 17:59 solid-state
d----- 2020-07-07 17:59 spectral
d----- 2020-07-07 17:59 story
d----- 2020-07-07 17:59 twenty
-a---- 2020-07-22 01:10 5786 Clean-Builds.ps1
-a---- 2020-07-09 16:16 342 Clone-Repos.ps1
-a---- 2020-07-09 16:16 397 Restore-Repos.ps1
This is where all the sources live
Clone-Repos.ps1
ps c:\Users\restorer\source\repos> cat Clone-Repos.ps1
# Clone-repos.ps1
# Repos in SmartHotel360 project
$SmartHotel360Repos = @("alpha","cartoon","dimension","lens","solid-state","spectral","story","twenty","SmartHotel360")
$SmartHotel360Repos | foreach {
Write-Host "Processing $_"
git clone http://restorer:IHopeThisPassword1sSafeEnough@127.0.0.1:8080/ekenas/SmartHotel360/_git/$_
}
restorer
:IHopeThisPassword1sSafeEnough
really?