UnderTheWire | Cyborg

In this walkthrough, I demonstrate the methods I used to solve all of the Cyborg challenges, 0 through 15, on Under the Wire.
In: UnderTheWire, PowerShell, CTF, Cybersecurity, Code, Easy Challenge

SSH Client

If you're running Windows 11 — the latest version of Windows at the time of writing — then, you already have access to the Windows Terminal app. If for some reason you do not, I recommend installing it, as you really don't need Putty to complete these exercises.

Windows Terminal - Free download and install on Windows | Microsoft Store
The Windows Terminal is a modern, fast, efficient, powerful, and productive terminal application for users of command-line tools and shells like Command Prompt, PowerShell, and WSL. Its main features include multiple tabs, panes, Unicode and UTF-8 character support, a GPU accelerated text rendering engine, and custom themes, styles, and configurations. This is an open source project and we welcome community participation. To participate please visit https://github.com/microsoft/terminal

Also, in most recent versions of Windows, the ssh.exe client and related binaries should already be installed and ready for use. Below, I've provided some examples of the most basic SSH syntax for connecting to the challenges.

ssh username@127.0.0.1

SSH using IP address

ssh username@domain.tld

SSH using FQDN


Cyborg 0 -> 1

ℹ️
The credential for connecting is cyborg1:cyborg1
ssh -o 'StrictHostKeyChecking=no' cyborg1@cyborg.underthewire.tech

Use the StrictHostKeyChecking=no option to skip the key verification prompt

Connected to the challenge box and ready to dig in

Cyborg 1 -> 2

The password for cyborg2 is the state that the user Chris Rogers is from as stated within Active Directory.
Get-ADUser -Filter 'GivenName -eq "Chris" -and Surname -eq "Rogers"' -Properties State | Select-Object State
💡
With PowerShell, it's recommended to use the -Filter parameter where available, rather than pipe to Where-Object, as it's more efficient to have the filter apply at runtime, rather than piping all of the output to Where-Object.
kansas

Password for cyborg2

exit

Exit the challenge


Cyborg 2 -> 3

The password for cyborg3 is the host A record IP address for CYBORG718W100N PLUS the name of the file on the desktop.
ssh cyborg2@cyborg.underthewire.tech

No longer need the additional option, since the host key has already been added to the known hosts list

Resolve-DnsName 'CYBORG718W100N'
The A record and the target file
(Resolve-DnsName 'CYBORG718W100N').IPAddress.ToString() + (Get-ChildItem -File).Name

One-liner to output the password for cyborg3

172.31.45.167_ipv4

Password for cyborg3

exit

Exit the challenge


Cyborg 3 -> 4

The password for cyborg4 is the number of users in the Cyborg group within Active Directory PLUS the name of the file on the desktop.
ssh cyborg3@cyborg.underthewire.tech
Get-ADGroup -Filter 'Name -like "Cyborg"'
Get-ADGroup -Filter 'Name -like "Cyborg"' | Get-ADGroupMember
Number of group members plus the target file
(Get-ADGroup -Filter 'Name -like "Cyborg"' | Get-ADGroupMember).Count.ToString() + (gci -File).Name

One-liner to get the password for cyborg4

88_objects

Password for cyborg4

exit

Cyborg 4 -> 5

The password for cyborg5 is the PowerShell module name with a version number of 8.9.8.9 PLUS the name of the file on the desktop.
ssh cyborg4@cyborg.underthewire.tech
Get-Module -ListAvailable | Where-Object {$_.Version -like '8.9.8.9'}
💡
We must use the -ListAvailable switch, as the module is not currently imported in the session. And we must use the Where-Object filter, since the Get-Module cmdlet doesn't have a -Version parameter.
Target module and target file
(Get-Module -ListAvailable | Where-Object {$_.Version -like '8.9.8.9'}).Name + (ls -File).Name

One-line to output the password for cyborg5

bacon_eggs

Password for cyborg5

exit

Cyborg 5 -> 6

The password for cyborg6 is the last name of the user who has logon hours set on their account PLUS the name of the file on the desktop.
ssh cyborg5@cyborg.underthewire.tech
$allADUsers = Get-ADUser -Filter * -Properties *

Get all of the users in AD, all of their properties, and store them in a variable

$allADUsers | Get-Member -Name '*hours*'

Use the Get-Member cmdlet to inspect the AD user object properties where any property contains the word "hours"

$allADUsers | Where-Object {$_.logonHours} | Select Surname, logonHours

The Where-Object {$_.logonHours} syntax means that the property has to contain data for the object to be returned in the output. Then select the last name and hours.

Target user is Rowray and the target file is _timer
($allADUsers | Where-Object {$_.logonHours -and $_.Surname}).Surname.ToLower() + (ls -File).Name

One-liner to output the password for cyborg6

rowray_timer

Password for cyborg6

exit

Cyborg 6 -> 7

The password for cyborg7 is the decoded text of the string within the file on the desktop.
ssh cyborg6@cyborg.underthewire.tech
The target file is cypher.txt the encoding is base64
💡
You can easily identify a base64 encoded string by the = padding (in addition to the overall characters that comprise the encoded text).
PSToolbox/Public/ps1/ConvertFrom-Base64.ps1 at master · 0xBEN/PSToolbox
PowerShell module containing a set of generally useful tools. - 0xBEN/PSToolbox

There is no native cmdlet to convert from base64 in PowerShell, so I wrote a function that will take pipeline input and decode, but you can do it manually for the practice.

$base64Text = cat .\cypher.txt
Store the base64 string in a variable
$base64ToBytes = [System.Convert]::FromBase64String($base64Text)
Convert the base64 data to raw bytes
$decodedText = [System.Text.Encoding]::UTF8.GetString($base64ToBytes)
Using the UTF8 encoding, convert the raw bytes back to text
💡
In the notes of the challenge, it states we must not have any spaces in the password. The spaces are actually null bytes caused by the 0 bytes in the $base64ToBytes output as seen above. So, let's refactor just a bit.
$base64ToBytes = $base64ToBytes | Where-Object {$_ -ne 0}
Replace the content of the variable with the 0 bytes removed
cybergeddon

Password for cyborg7

exit

Cyborg 7 -> 8

The password for cyborg8 is the executable name of a program that will start automatically when cyborg7 logs in.
ssh cyborg7@cyborg.underthewire.tech
Get-CimInstance Win32_StartupCommand
skynet

Password for cyborg8

exit

Cyborg 8 -> 9

The password for cyborg9 is the Internet zone that the picture on the desktop was downloaded from.
ssh cyborg8@cyborg.underthewire.tech
PowerShell: Find File ... | 0xBEN | Notes
# Recursively search current user’s Downloads directory Get-ChildItem -Recurse -File ”$env:UserProf…
Get-ChildItem -File | ForEach-Object {

  $file = $_
  # Get all data streams of the file
  $item = Get-Item $file.FullName -Stream *
  if ($item.Stream -contains 'Zone.Identifier') {
    # If the Zone.Identifier stream is present
    # Value  Setting
    # ------------------------------
    # 0      My Computer
    # 1      Local Intranet Zone
    # 2      Trusted sites Zone
    # 3      Internet Zone
    # 4      Restricted Sites Zone
    $streamContents = Get-Content -Raw -Stream 'Zone.Identifier' $item.FileName
    $streamContents = $streamContents.Split("`n")
    $zoneId = $streamContents[1].Split('=')[1] -replace '\s'
    
    if ($zoneId -in @('1','2','3','4')) {
    
      $hostUrl = $streamContents | Where-Object {$_ -match '^HostUrl'}
      $referrerUrl = $streamContents | Where-Object {$_ -match '^ReferrerUrl'}
      if ($hostUrl) { $hostUrl = $hostUrl.Split('=')[1] }
      if ($referrerUrl) { $referrerUrl = $referrerUrl.Split('=')[1] }
     
      [PSCustomObject]@{
        'ZoneId' = $zoneId
        'FileName' = $file.FullName
        'DownloadUrl' = $hostUrl
        'ReferrerUrl' = $referrerUrl
      }
      
    }
    
  }
  
} | Format-List *
4

Password for cyborg9

exit

Cyborg 9 -> 10

The password for cyborg10 is the first name of the user with the phone number of 876-5309 listed in Active Directory PLUS the name of the file on the desktop.
ssh cyborg9@cyborg.underthewire.tech
$allADUsers = Get-ADUser -Filter * -Properties *
💡
We'll take a similar approach to Cyborg 5, storing the users in a variable and using Get-Member to find the object property that stores phone numbers.
$allADUsers | Select-Object GivenName, *Phone | Select-String '876-5309'

There are multiple "phone" properties, so use *Phone and filter using Select-String and the target phone number

$phoneProperties = $allADUsers | Get-Member -Name '*phone' | Select-Object -ExpandProperty Name
$phoneProperties | ForEach-Object {$phoneProp = $_ ; $allADUsers | Where-Object {$_.$phoneProp -eq '876-5309'}} | Select-Object GivenName

I'll explain this command in a series of bullet points:

  • Go one-by-one — ForEach-Object — through the list of phone properties used in AD user objects:
    • HomePhone
    • MobilePhone
    • OfficePhone
  • Then, use Where-Object on $allADUsers to see...
    • Is 876-5309 in the HomePhone property?
    • Is 876-5309 in the MobilePhone property?
    • Is 876-5309 in the OfficePhone property?
  • If any of these are true, then the user is returned in the output
  • Finally, select the user's first name
($phoneProperties | ForEach-Object {$phoneProp = $_ ; $allADUsers | Where-Object {$_.$phoneProp -eq '876-5309'}}).GivenName.ToLower() + (ls -File).Name

One-line to output the password for cyborg10

onita99

Password for cyborg10

exit

Cyborg 10 -> 11

The password for cyborg11 is the description of the Applocker Executable deny policy for ill_be_back.exe PLUS the name of the file on the desktop.
ssh cyborg10@cyborg.underthewire.tech
Get-AppLockerPolicy -Effective | Select-Object -ExpandProperty RuleCollections
AppLocker policy and the target file
(Get-AppLockerPolicy -Effective | Select-Object -ExpandProperty RuleCollections).Description + (ls -File).Name

One-liner to output the password for cyborg11

terminated!99

Password for cyborg11

exit

Cyborg 11 -> 12

The password for cyborg12 is located in the IIS log. The password is not Mozilla or Opera.
ssh cyborg11@cyborg.underthewire.tech
ls C:\inetpub\logs -Recurse -File | Select-Object FullName
Discover the log file
cat C:\inetpub\logs\logfiles\w3svc1\u_ex160413.log | Where-Object {$_ -notlike '*mozilla*' -and $_ -notlike '*opera*'}
Filter out Mozilla and Opera logs
spaceballs

Password for cyborg12

exit

Cyborg 12 -> 13

The password for cyborg13 is the first four characters of the base64 encoded full path to the file that started the i_heart_robots service PLUS the name of the file on the desktop.
ssh cyborg12@cyborg.underthewire.tech
Get-CimInstance win32_service -Filter 'Name like "i_heart_robots"'
Use Get-CimInstance to provide more output than Get-Service
Target service and target file
💡
The wording in this challenge is a little off. It sounds like we are to find the program that "started the service", when we are actually supposed to find the program that is "started by the service"
-join[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes((Get-CimInstance win32_service -Filter 'Name like "i_heart_robots"').PathName)).ToLower()[0..3] + (ls -File).Name

One-liner to output password for cyborg13

I'll explain the one-liner in series of bullet points:

  • We work our way inside-out of any brackets or enclosings
  • The inner-most enclosing is (Get-CimInstance win32_service -Filter 'Name like "i_heart_robots"').PathName — so get the path to the binary started by the service
  • The next enclosing is [System.Text.Encoding]::Unicode.GetBytes() which converts the PathName output to Unicode bytes
  • The final enclosing is [Convert]::ToBase64String() which converts the Unicode bytes to a base64 string
  • We then use the .ToLower() method on the string to convert it to lowercase and select the first four characters [0..3]
  • Finally, we -join the character array back into a single string and + concatenate with the file name
ywa6_heart

Password for cyborg13

exit

Cyborg 13 -> 14

The password cyborg14 is the number of days the refresh interval is set to for DNS aging for the underthewire.tech zone PLUS the name of the file on the desktop.
ssh cyborg13@cyborg.underthewire.tech
Get-Command 'Get-*Dns*'
Find any commands with Get- verb and *Dns* in the cmdlet name
(Get-DnsServerZoneAging -Name 'underthewire.tech').RefreshInterval.TotalDays.ToString() + (gci -File).Name

One-liner to output password for cyborg14

22_days

Password for cyborg14

exit

Cyborg 14 -> 15

The password for cyborg15 is the caption for the DCOM application setting for application ID {59B8AFA0-229E-46D9-B980-DDA2C817EC7E} PLUS the name of the file on the desktop.
ssh cyborg14@cyborg.underthewire.tech
💡
Seeing as how the challenge is asking us to enumerate DCOM, I'm almost certain there's no native cmdlet to expose this data and that we'll have to work through a lower-level interface such as CIM.
Not seeing anything here
Get-CimClass -ClassName *dcom*
Enumerate any CIM classes with the string "dcom". Win32_DCOMApplication looks promising.
Indeed, this should be right what we want
Get-CimInstance -ClassName Win32_DCOMApplication -Filter 'AppID like "%59B8AFA0-229E-46D9-B980-DDA2C817EC7E%"' | Select-Object *

Use % as a wildcard match

Caption: propshts
(Get-CimInstance -ClassName Win32_DCOMApplication -Filter 'AppID like "%59B8AFA0-229E-46D9-B980-DDA2C817EC7E%"').Caption + (ls -File).Name

One-liner to output password for cyborg15

propshts_objects

Password for cyborg15

exit
Comments
More from 0xBEN
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.