Snyk CTF Challenge: Feb 28th 2025.


Please check out my other posts about other CTF’s I have participated on such as UTCFT 2025 Challenge: March 14th

This was the first reverse engineering challenge. It was labeled easy. The first thing I did was use the strings command to view all the strings in the binary. The string command will print all the printable characters that are in the file.

/lib64/ld-linux-x86-64.so.2: This is the dynamic linker, responsible for loading shared libraries on a 64-bit Linux system. Its presence suggests the program being analyzed is dynamically linked.

Next I tired to enter, “unlock_me_123” after running the file. The program then gave the flag. I did not even have to enter the password as the flag was in different lines above the “Welcome to the first RE challenge!” line.

This image shows a terminal screen displaying the output of a command-line program. The program greets the user with "Welcome to the first RE challenge!" and prompts for a password. The user enters "unlock_me_123", which is accepted as correct. The program then reveals a flag, highlighted in a red box, which reads "flag{850de1a29ab50b6e5ad958334b68d5bf}". The command executed appears to be ./string-me-along.

Plantly

There is also a pop-up dialog box from "challenge.ctf.games:32568" stating "Snyk Rocks!" with an "OK" button. The browser tab shows "Not secure" and the URL "challenge.ctf.games:32568/receipt". This suggests the page is part of a Capture The Flag (CTF) challenge, likely related to web security or exploitation. The "Not secure" warning and the unusual custom request format might be clues for the challenge.

With this challenge I was able to get stored XSS but there was no way to use the stored XSS to get the flag.txt. After messing with XSS I realized that its probably vulnerable to Server Side Template Injection, all though when I tired to use the payload “{{7*7}}” the site was giving 500 HTTP status code.

This image shows a web form where a user is entering a custom order request. The input field contains the text `{{7*7}}`, which is highlighted in a red box. This input is likely intended for a custom order and might be processed by a system that interprets such expressions. A green button labeled "Add Custom Order to Cart" is visible below the input field.

{{7*7}} is usually good way to test if a site is vulnerable to SSTI or Server Side Template Injection. If the site is vulnerable the site will display “49”. As seen in the image below.

This image shows a "Plantly Receipt" with an order date of "2025-03-01". The receipt lists one "Custom Order" for $0.0 and a "Custom Request: 49", which is highlighted in a red box. The subtotal, tax, and total are all $0.00. The receipt concludes with a thank you message.

We can see that the site is vulnerable to SSTI. Now we have to find a way to read the file, “flag.txt”. On Linux or Unix systems if you want to view a file contents you can use the “cat” command.

{{ self.__init__.__globals__.__builtins__.__import__('os').popen('cat flag.txt').read() }}

I found the code above here. Instead of using the “id” command I modified the code and put “cat flag.txt”. The first half of the code is needed to run the second half of the code. The code will import the “os” module and then run the command, “cat flag.txt”.

This image displays a list of "Items Purchased" from what appears to be a receipt or order summary. It shows two "Custom Order" items, each priced at $0.0. Following these, there are two "Custom Request" entries. The first custom request shows system user information: `uid=0(root) gid=0(root) groups=0(root)`. The second custom request, highlighted in a red box, contains a flag: `flag{982e3b7286ee603d8539f987b65b90d4}`.

Now we are cooking with GAS! We got the flag.

Unfurler

There was a little confusion on this challenge. Near the start of the competition the CTF infrastructure went down which the people that where running the CTF extended the time on the CTF.

I was messing around with this challenge I knew fairly quickly that it was SSRF.

SSRF stands for Server-side request forgery, one of the effects of SSRF is that in some instances you can port scan the local network where the site is hosted.

After the infrastructure went down the administrator of the CTF in the Discord chat said that scanning or any type of intense scanning is not allowed. So I thought it was not allowed to perform a port scan via SSRF. I misunderstood.

After messing around with the URL I found out that you can enter a site like google and the site will visit the URL given. But you cant do something like: “http://google.com/beer_me”.

In this challenge they gave us the source code of the application, I took peak and saw that when the instance of the site is up it will randomly pick a port between “1024” and “4999”. As seen in the code below:

This image shows a JavaScript code snippet for setting up an Express.js server. It imports the `express` and `morgan` libraries, and a custom `adminRoutes` module. The code initializes an Express application, configures it with middleware for URL-encoded and JSON request parsing, and sets up HTTP request logging. It then defines a function `getRandomPort()` to generate a random port number between 1024 and 4999 (excluding 5000). Finally, it starts the server on a random port on the localhost interface (`127.0.0.1`) and logs the server's address to the console. The comment suggests the random port is a security measure to obscure the admin panel's location.

I coded this simple Ruby script to find the random port that was hosting the admin panel.

The code will submit a post request to the site with the requesting access to a localhost while enumerating the ports through 1024 and 4999. If the body includes the word “Admin” or the response code is “200” it will print the port.

require 'httparty'
for i in 1024..4999
  r = HTTParty.post("http://challenge.ctf.games:31208/unfurl",
    :body => {"url": "http://localhost:#{i}"})
  if r.body.include?("Admin") or r.code.to_i == 200
    puts "Found http://localhost:#{i}"
    exit
  else
    puts "#{i}: Not found"
  end
end

The image below shows the script in action that it detected that the admin panel was “http://localhost:2204.

This image shows a terminal output displaying a series of "Not found" messages for ports 2195 through 2203. The final line indicates success, stating "Found http://localhost:2204", meaning a service or resource was located on port 2204 of the local machine.

After finding the admin panel I needed to find a way to access the flag.txt which the file is hosted on the server.

Since we have access to the source code of the application, I immediately looked at the source code.

I looked for any files named admin as that is where the I need to be to get the flag. Below is the section of the admin panel that I needed to exploit to get the flag

This image displays a JavaScript code snippet that defines a route handler for executing commands on a server. The code first checks if the request is coming from localhost (either `127.0.0.1` or `::1`). If not, it denies access with a 403 Forbidden error. If the request is from localhost and includes a `cmd` query parameter, the code attempts to execute the command using `exec`. It handles potential errors during command execution by returning a 500 Internal Server Error. If successful, it logs the command, displays the command's output (stdout or stderr) in an HTML response, and provides a link back to the admin panel. The code includes comments acknowledging its lack of security and the intention to bind it only to localhost.

Notice that the coder made it so that to access the admin panel you have to have the IP of a localhost.

The way that SSRF works is that you trick the server into making the request for us to the admin panel that is only accessible on the website’s local network.

The request will look like it was coming from localhost when in reality we made the request. I found that “/execute” immediately caught my eye and figured this would be the way to access the flag.txt file hosted on the server.

On Linux systems to access a file the “cat” command is used. So I used the following payload to read the “flag.txt” file: “http://localhost:2204/execute?cmd=cat flag.txt“.

This image shows a web page titled "Open Source Link Unfurler" which is designed to fetch metadata from URLs. The page has an input field pre-filled with `http://localhost:2204/execut` and an "Unfurl" button. The results section indicates that no title, description, or image was found. However, the "Raw HTML" section reveals a flag, highlighted in a red box, which reads `flag{e1c96cccca8777b15bd0b0c7795d018ed}`. A link to "Back to Admin Panel" is also present. This suggests the page is part of a challenge where a command injection vulnerability was exploited to retrieve the flag.

Bomshakalaka!!! I was so close to getting this flag during the competition! With SSRF you could even grabbed the file with “file://flag.txt” but this did not work with this challenge.

A Powerful Shell

In essence, this script decodes a Base64 string, treats it as a PowerShell command, executes it using powershell.exe with specific arguments, captures its output, and then displays that output. This is a common technique for obfuscating or layering malicious code.

The red box in the image above shows a base64 encoded string. Base64 is NOT encrypted, it is considered encoding. I used a site call https://www.base64decode.org/ to decode the text.

This image shows a snippet of PowerShell code. It decodes a Base64 string into a variable named `$decoded`. Then, it converts these decoded bytes into a UTF-8 string and assigns it to the `$flag` variable. The script checks if a specific environment variable, `$env:MAGIC_KEY`, is equal to the string `'Sup3rS3cr3t!'`. If the condition is true, it outputs the `$flag`. Otherwise, it outputs the message "Nice try! But you need the magic key!". This code is designed to reveal a flag only when a specific "magic key" environment variable is set correctly.

The image above shows the decoded script. It has another base64 encoded string.

This image displays a flag, which is a common element in Capture The Flag (CTF) cybersecurity challenges. The flag is formatted as `flag{45d23c1f6789badc1234567890123456}`.

After decoding the string, we got the flag! For future referencing two or one equal sign at the end could mean its base64 encoded, while it does not always mean that it’s base64 encoded.

Lessons Learned

  • Next time I will use ChatGpt to better understand the code of languages I am unfamiliar with and to create payloads to exploit vulnerabilities.
  • Use Google, a couple of times hints where posted in the Discord chat that I overlooked that could of aided me in solving the challenges.
  • Keep on trying, even if you are lost. After the challenges people posted their solutions to many of the challenges. I found out after the competition that I was very close into solving a few of the the challenges. If I kept on trying or looked at it a different way I would of solved them.
  • Try to find a team a couple days before the competition; while I had a team mate, they stopped doing stuff mid day and for the rest of the time I was working alone. Maybe ask some friends from college or people from the 2600 club I am apart of.
  • Read up on common web attacks a couple of days before the event, have a list ready to go. There was a couple of challenges where I knew what type of attack was needed but I did not know how to execute the attack to get the flag.
  • I need to learn more about Ghidra and how to use it in the reverse engineering challenges.