A nice easy challenge to start off the week! I found this to be fun and engaging even though its labeled as “very easy”. A good example of how to take multiple vulnerabilities and leverage them into an RCE.


Before downloading any files, I like to see what I’m working with. This is just my personal preference, but I typically attack the web challenges but first interacting with the website; then review the deployment stack (Dockerfile, config, etc) for anything useful; finally review the source code. This was especially helpful when trying to solve petpet rcbee.


Nothing too crazy. Appears to be a single page app (no links or navigation). Contains a simple form that POSTs to / with the text to neonify. Running a quick test with Hello World does as it’s expected.

This is probably going to be some type of template injection. However, entering drt.sh returns a Malicious Input Detected. It appears that there is some validation on the backend, and a simple . breaks it. Might not be as simple as I originally thought.

Deployment Stack

I like to check out the Dockerfile and see what other (if any) software is installed inside the container. I also find it important to check which flag file to look for. There’s a chance it could be flag, flag.txt, or flag_XXXX where XXXX is a randomized set of characters.

Source Code

Time to dive into some code and see what we’re working with!

shit… its in ruby.

oh come on!

I’m not a fan. Guess I should have figured that one out with the four giant rubies in front of me :facepalm:

ruby icon from challenge

After scouring through the source code, I noticed two things:

  • The neon parameter that is POSTed is passed into the template (possible injection)
  • There is a regex validation for neon that only allows alpha-numeric chars and spaces.

This will require a two pronged approach. An Server Side Template Injection (SSTI) and bypassing validation. This is the code that needs to be bypassed.

post '/' do
  if params[:neon] =~ /^[0-9a-z ]+$/i
    @neon = ERB.new(params[:neon]).result(binding)
    @neon = "Malicious Input Detected"
  erb :'index'


There are a lot of examples online on SSTI and for ruby in general. But before that can be passed through to read the flag.txt file, it needs to bypass the regex validation. After a bit of research, apparently using ^ and $ within regex’s in Ruby is a bad idea (?). Looked fine to me, but what do I know? Knowing that the regex can be bypassed with newlines, we can start looking for an exploit to read flag.txt.

<%= File.open('flag.txt').read %> # Read file

Easy enough, but it needs to be URL encoded. With that in mind, the full payload will look like:


NOTICE: There is an actual newline character in the payload. A \n written out is not enough to be parsed properly.

What’s I really like about this challenge, is that it is one of those exploits that can be run directly in your browser. No need for any special tools! Although, I still prefer to use curl ;)

Let’s start with that!

via curl

# deliberate newline in console as `\n` fails to parse
curl -d 'neon=a
curl \
  -s -X POST -d 'neon=a
%3C%25%3D%20File.open%28%27flag.txt%27%29.read%20%25%3E' | grep -Eo 'HTB{.*}'

katherine ryan cats does countdown

Yeah, it was that easy! Cheers 🍻

via Firefox (or Chrome (or other Browser))

There’s too many screenshots to take so I’ll keep it brief and in a list:

  • Open the browser’s dev tools and view the network stack
  • Submit a valid entry (I used a)
  • Find the document with the POST request.
  • Edit and resend
  • Change the request body to the payload above
  • Resend
  • Open in a new tab (if applicable).