In this short write-up, we will go over two challenges in the Misc category of Inferno CTF, which was hosted by Dc1ph3R
. The challenges that we will discuss are “Color Blind” (which can be solved with a simple one-liner) and “Registering X” (which was a bit more complex). I completed these challenges with our CTF team, U+1F966
.
Color Blind
In this challenge, we are provided with a PNG
image with a resolution of $64 \times 1$. Running strings
, binwalk
, and foremost
on the given file does not give us any useful information.
Looking closer at this image, we can see that each pixel has a specific colour. A teammate mentioned that the actual colours of the pixels could be the key to our flag. Each pixel has a colour code that can be expressed in RGB
or in HEX
. Stringing together all the HEX
values is the key to obtaining our flag.
Opening an image, retrieving all the HEX
values of the pixels, and stringing them together is a trivial task that can be done with a simple one-liner in the Julia programming language:
using FileIO, Colorsjoin(map(hex, load("colorblind.png")))
This works because images in Julia are simple arrays containing the RGB
value of each pixel. We map the hex
function (provided to us by Colors
) over the color values, which returns an array with HEX
values. We then join all the values together, which gives us the following string:
626C6168626C6168626C61685F68656C6C6F5F686F775F6172655F796F755F746F6461795F695F686F70655F796F755F6172655F6E6F745F646F696E675F746869735F6D616E75616C6C795F696E6665726E6F4354467B6833795F3130306B5F7930755F3472335F6E30375F6833785F626C316E445F3A4F7D5F646F696E675F746869735F6D616E75616C6C795F776F756C645F62655F615F6261645F696465615F796F755F73686F756C646E745F646F5F69745F6D616E75616C6C795F6F6B
This string can probably be translated to readable text using Julia, but it was quicker to copy this output and paste it in an online hex to string converter:
blahblahblah_hello_how_are_you_today_i_hope_you_are_not_doing_this_manually_infernoCTF{h3y_100k_y0u_4r3_n07_h3x_bl1nD_:O}_doing_this_manually_would_be_a_bad_idea_you_shouldnt_do_it_manually_ok
The output above contains the flag:
infernoCTF{h3y_100k_y0u_4r3_n07_h3x_bl1nd_:O}
Registering X
The goal of this challenge was to construct a string that matches the regex
given to us in the challenge attachment:
infernoCTF{.(?<=H){21}.[a-z](?<=\+a){1024}[a-z][a-j](?<!([a-u]|[w-z])(j|[a-h])).{2,64}(?<!\S){255}n.{2}(?<=\s)g_fUn\W(?<=[A-z])}(?<=\..{14})(?<=^.{33})
The given regex
is complex at first glance, but the challenge itself is very straightforward. We did not find a way to automate the solution of this challenge, so we solved it by hand. First, we simplified the regex
by removing the quantifiers after the lookbehinds, because they do not serve a purpose. Removing these quantifiers will give us an equivalent, but shorter, regex
:
infernoCTF{.(?<=H).[a-z](?<=\+a)[a-z][a-j](?<!([a-u]|[w-z])(j|[a-h])).{2,64}(?<!\S)n.{2}(?<=\s)g_fUn\W(?<=[A-z])}(?<=\..{14})(?<=^.{33})
We start by learning about lookbehinds. Positive lookbehinds (e.g, "(?<=a)b"
) match the character after the parenthesis, but only if the character is followed by the character in the parenthesis, after the =
. Negative lookbehinds (e.g, "(?<!a)b"
) match the character after the parenthesis, but only if they are not followed by the character in the parenthesis, after the !
. Knowing this, we can start constructing our flag.
Our flag starts with infernoCTF{
, because the given regex
states that we have to match these characters literally. .
in our regex matches any character in our flag, so we will put an X
as a placeholder. We now have to match the positive lookbehind (?<=H).
, which means that our string will only match the regex when we have a character following the character H
. We could try adding H+
to our flag (making our flag infernoCTF{XH+)
, but this will not match the given regex. The cause of this mismatch is the fact that lookbehinds only match the character after the parenthesis. It will not match the character in the parenthesis.
The solution for this problem is replacing our previous X
with an H
. We now have infernoCTF{H+
, which matches the first part of our regex
. The rest of the regex
follows the same principle: we have a bunch of lookbehinds that force us to precede a character with another character. Around those lookbehinds we have more common regex
symbols that make it possible to match the character in the parenthesis. Now that we got the gist of the challenge, we will quickly go over the solution:
infernoCTF{
forces us to start our flag withinfernoCTF{
.(?<=H).[a-z](?<=\+a)
forces us to addH+a
[a-z][a-j](?<!([a-u]|[w-z)(j|[a-h])).{2,64}
makes us addvi
to our flag, followed by an unspecified amount (between two and sixty-four, which will be constrained later in theregex
) of placeholder characters(?<!\S)n
forces us to add ann
after a whitespace character.{2}(?<=\s)g
forces us to add ag
after a whitespace character_fUn
forces us to add_fUn
to our flag\W(?<=[A-z])}
forces us to add a}
after a non-word character betweenA
andz
in the ASCII table (e.g.,]
)
(?<=\..{14})
forces us to count fourteen characters back and add a .
. We can safely put our .
in the collection of unspecified characters mentioned in the third step above.
The last part of our regex
forces us to make the flag exactly thirty-three characters long. This can be achieved by adding or removing a bunch of unspecified characters (which were mentioned in the same third step). One of the possible solutions is infernoCTF{H+avi . ng no g_fUn]}
.
Because of a lack of knowledge about tools that generate strings based on given regex
(that also support lookbehinds!), we decided to solve this challenge by hand. Although it is a bit of tedious work, it certainly is possible to construct the flag manually.