Intigriti CTF 12-23
After a long time, this month I had the opportunity to attend in the monthly CTF provided by INTIGRITI.
As part of the learning process I really recommend everybody to attend on this, it is a mind-blowing experience, you can find more information about the CTF and many other things in the Intigriti's discord community.
The CTF is available through the link https://challenge-1223.intigriti.io/challenge.php
Here we go!!
Summary
At first glance, the target is a simple HTML page containing a text box where the payloads should be sent.
Also, it shows the regex that is being applied to check the text.
If we pass some malicious payload it is going to catch it.
Recon
Also in the page, the challenge is providing to us the source code running on the server side, let's go through it.
More below, we can see two IF clauses;
The first will redirect you case the text posted is not being submitted by the variable data.
The second is checking the data, the check is quite weird, it is going to evaluate the return from the preg_match function using the regex and the data being passed, and in case of any matching with regex, it simply shows the red message and exit...
In the last piece of the code, we can see a temporary smarty template file being created and the data submitted is being written into the template file without being sanitized.
With all of this understanding, it is easy to conclude that the way to capture the flag would be through a Server Side Template Injection flaw, but how?
Understanding the problem
The SSTI vulnerability allows an attacker to execute malicious code in the server and get the result of it rendered in the page.
The template syntax for Smarty template uses curly brackets, in this way if we pass the value {3*3} it should display the number 9 in the page.
Inside this {}, we should pass the PHP code that will capture the flag, but due to the regex, it is not as simple as that;
At this point, I got stuck for a long time, I had this big regex that won't let me perform the template injection and I was convinced that the only way to capture the flag would be passing the curly brackets payload {code}
Looking back to the code, I had to find a way to not return TRUE in the check_data function.
As mentioned before, this function is using php's preg_match function to evaluate the data against the regex, the only way to not return true would be finding a bug in this php function.
To look into that I went to the php documentation for preg_match function where I found some interesting information.
The first attempt I did to bypass was including carriage returns in the payload (/n/r), this could be an option if our regex didn't have /s in the end, as the preg_match would consider only the first line.
I also tried weird things, like passing 2 bytes characters, different types of character encoding, so on and so forth, without success, until I found this interesting comment more below in the php documentation.
Reading the PCRE configuration page, took me to the backtracking exploit researches on google where I found more information about REDOS in this link as well as other related CTF in this link both helped me to find the right approach.
Exploit
Having the backtracking in mind, I was aware that I had to pass 1 million characters + {malicious code} in a way that it could fall into the backtracking, to do that, I had to revisit the regex;
/(\b)(on\S+)(\s*)=|javascript|<(|\/|[^\/>][^>]+|\/[^>][^>]+)>|({+.*}+)/s
As I'm not good with regex, I checked the first block in chatGPT which gave me the following information:
In this way the regex will backtrack the data in case we have something started on, looking for something that matches on and =
Finally, I created a payload with 1 milion letters, this payload had a repeated sequence of the characters 'onl' (e.g onl * X)
printf "%0.s$(echo -n 'onl')" {1..333333} > output.txt
With this payload, I was able to include the {3*3} in the end of the string, which proved that the preg_match had failed and the template injection succeed.
The Payload
in the end, the final payload was:
'onl'*333 + {include file="/flag.txt"}
INTIGRITI{7h3_fl46_l457_71m3_w45_50_1r0n1c!}