This was my first time doing a CTF (and I was alone), and therefore it was a bit above my skill level, but with some help from the admins and authors of the challenges, it was an overall fun experience.
Thank you to (in no particular order): The Hexion Team Authors (with mentions of the challenges which I completed):
- Idan (PIL, Nameless, Mirage, T&J)
- Yarin (Well Known, Nameless, X0R)
- moka (Hmmm)
- MrPeck (sorry I didn't complete your challenges 😅)
Please read the rules :)
On the rules/about page there is a flag: hexCTF{mu5t_b3_7he_eas1est_fl4g_y0u_g0t}
🤔
Note: anime girl isn't the flag
(linked binary: "hmmm")
The binary contains a flag at the beginning: hexCTF{1m_s0rry_1f_y0u_r3v3r5ed_7h1s}
It prints out an image with Unicode Braille:
⢸⣿⣿⣿⣿⠃⠄⢀⣴⡾⠃⠄⠄⠄⠄⠄⠈⠺⠟⠛⠛⠛⠛⠻⢿⣿⣿⣿⣿⣶⣤⡀⠄
⢸⣿⣿⣿⡟⢀⣴⣿⡿⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⣸⣿⣿⣿⣿⣿⣿⣿⣷
⢸⣿⣿⠟⣴⣿⡿⡟⡼⢹⣷⢲⡶⣖⣾⣶⢄⠄⠄⠄⠄⠄⢀⣼⣿⢿⣿⣿⣿⣿⣿⣿⣿
⢸⣿⢫⣾⣿⡟⣾⡸⢠⡿⢳⡿⠍⣼⣿⢏⣿⣷⢄⡀⠄⢠⣾⢻⣿⣸⣿⣿⣿⣿⣿⣿⣿
⡿⣡⣿⣿⡟⡼⡁⠁⣰⠂⡾⠉⢨⣿⠃⣿⡿⠍⣾⣟⢤⣿⢇⣿⢇⣿⣿⢿⣿⣿⣿⣿⣿
⣱⣿⣿⡟⡐⣰⣧⡷⣿⣴⣧⣤⣼⣯⢸⡿⠁⣰⠟⢀⣼⠏⣲⠏⢸⣿⡟⣿⣿⣿⣿⣿⣿
⣿⣿⡟⠁⠄⠟⣁⠄⢡⣿⣿⣿⣿⣿⣿⣦⣼⢟⢀⡼⠃⡹⠃⡀⢸⡿⢸⣿⣿⣿⣿⣿⡟
⣿⣿⠃⠄⢀⣾⠋⠓⢰⣿⣿⣿⣿⣿⣿⠿⣿⣿⣾⣅⢔⣕⡇⡇⡼⢁⣿⣿⣿⣿⣿⣿⢣
⣿⡟⠄⠄⣾⣇⠷⣢⣿⣿⣿⣿⣿⣿⣿⣭⣀⡈⠙⢿⣿⣿⡇⡧⢁⣾⣿⣿⣿⣿⣿⢏⣾
⣿⡇⠄⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⢻⠇⠄⠄⢿⣿⡇⢡⣾⣿⣿⣿⣿⣿⣏⣼⣿
⣿⣷⢰⣿⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⢰⣧⣀⡄⢀⠘⡿⣰⣿⣿⣿⣿⣿⣿⠟⣼⣿⣿
⢹⣿⢸⣿⣿⠟⠻⢿⣿⣿⣿⣿⣿⣿⣿⣶⣭⣉⣤⣿⢈⣼⣿⣿⣿⣿⣿⣿⠏⣾⣹⣿⣿
⢸⠇⡜⣿⡟⠄⠄⠄⠈⠙⣿⣿⣿⣿⣿⣿⣿⣿⠟⣱⣻⣿⣿⣿⣿⣿⠟⠁⢳⠃⣿⣿⣿
⠄⣰⡗⠹⣿⣄⠄⠄⠄⢀⣿⣿⣿⣿⣿⣿⠟⣅⣥⣿⣿⣿⣿⠿⠋⠄⠄⣾⡌⢠⣿⡿⠃
⠜⠋⢠⣷⢻⣿⣿⣶⣾⣿⣿⣿⣿⠿⣛⣥⣾⣿⠿⠟⠛⠉⠄⠄
I first thought I had to reverse it, because it wouldn't show up in strings
for some reason.
"Your eyes can deceive you; don't trust them." -- Obi-Wan Kenobi
The flag is encoded in a font (hexFont) that has some glyphs rearranged. Doing some simple deduction and replacement, you get: hexCTF{Don7_judge_a_B0Ok_by_1ts_c0v3r}
strng = "j4teqybvAskIE2S}4IdIc_M5IB8IHmlIF_0Ypn"
alphabet = {
"d": "a",
"B": "b",
"F": "c",
"S": "d",
"4": "e",
"f": "f",
"}": "g",
"j": "h",
"Z": "i",
"E": "j",
"5": "k",
"g": "l",
"R": "m",
"s": "n",
"A": "o",
"K": "p",
"O": "q",
"p": "r",
"l": "s",
"m": "t",
"2": "u",
"0": "v",
"x": "w",
"t": "x",
"8": "y",
"h": "z",
"w": "A",
"c": "B",
"e": "C",
"v": "D",
"o": "E",
"y": "F",
"G": "G",
"z": "H",
"1": "I",
"T": "J",
"J": "K",
"{": "L",
"V": "M",
"D": "N",
"M": "O",
"Q": "P",
"3": "Q",
"9": "R",
"i": "S",
"q": "T",
"u": "U",
"C": "V",
"7": "W",
"W": "X",
"X": "Y",
"N": "Z",
"_": "0",
"H": "1",
"L": "2",
"Y": "3",
"U": "4",
"a": "5",
"P": "6",
"k": "7",
"r": "8",
"6": "9",
"b": "{",
"n": "}",
"I": "_"
}
newStr = ""
for c in strng:
newStr = newStr + alphabet[c]
print(newStr)
Our team detected a suspicious image, and managed to get a code of some sort, and we think they are related.
Can you investigate this subject and see if you can give us more data?
PIL = PI + IL
Some ILCode is provided as well as a bitmap.
source
(provided):
.class private auto ansi '<Module>'
{
} // end of class <Module>
.class private auto ansi beforefieldinit csharp.Program
extends [mscorlib]System.Object
{
// Fields
.field private static class [mscorlib]System.IO.FileStream piFile
// Methods
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 38 (0x26)
.maxstack 8
IL_0000: ldstr "one-million-digits.txt"
IL_0005: ldc.i4.3
IL_0006: ldc.i4.1
IL_0007: newobj instance void [mscorlib]System.IO.FileStream::.ctor(string, valuetype [mscorlib]System.IO.FileMode, valuetype [mscorlib]System.IO.FileAccess)
IL_000c: stsfld class [mscorlib]System.IO.FileStream csharp.Program::piFile
IL_0011: ldstr "original.bmp"
IL_0016: ldstr "result.bmp"
IL_001b: ldstr "<CENSORED>"
IL_0020: call void csharp.Program::Hide(string, string, string)
IL_0025: ret
} // end of method Program::Main
.method private hidebysig static
void Hide (
string srcPath,
string dstPath,
string secret
) cil managed
{
// Method begins at RVA 0x2078
// Code size 106 (0x6a)
.maxstack 5
.locals init (
[0] class [mscorlib]System.Collections.BitArray,
[1] uint8[],
[2] int32,
[3] int32,
[4] uint8,
[5] int32
)
IL_0000: call class [mscorlib]System.Text.Encoding [mscorlib]System.Text.Encoding::get_UTF8()
IL_0005: ldarg.2
IL_0006: callvirt instance uint8[] [mscorlib]System.Text.Encoding::GetBytes(string)
IL_000b: newobj instance void [mscorlib]System.Collections.BitArray::.ctor(uint8[])
IL_0010: stloc.0
IL_0011: ldarg.0
IL_0012: call uint8[] [mscorlib]System.IO.File::ReadAllBytes(string)
IL_0017: stloc.1
IL_0018: ldloc.1
IL_0019: ldc.i4.s 14
IL_001b: ldelem.u1
IL_001c: ldc.i4.s 14
IL_001e: add
IL_001f: stloc.2
IL_0020: ldc.i4.0
IL_0021: stloc.s 5
// sequence point: hidden
IL_0023: br.s IL_0058
// loop start (head: IL_0058)
IL_0025: ldloc.2
IL_0026: call int32 csharp.Program::GetNextPiDigit()
IL_002b: add
IL_002c: stloc.3
IL_002d: ldc.i4 254
IL_0032: ldloc.1
IL_0033: ldloc.3
IL_0034: ldelem.u1
IL_0035: and
IL_0036: conv.u1
IL_0037: stloc.s 4
IL_0039: ldloc.1
IL_003a: ldloc.3
IL_003b: ldloc.s 4
IL_003d: ldloc.0
IL_003e: ldloc.s 5
IL_0040: callvirt instance bool [mscorlib]System.Collections.BitArray::get_Item(int32)
IL_0045: call uint8 [mscorlib]System.Convert::ToByte(bool)
IL_004a: add
IL_004b: conv.u1
IL_004c: stelem.i1
IL_004d: ldloc.2
IL_004e: ldc.i4.s 10
IL_0050: add
IL_0051: stloc.2
IL_0052: ldloc.s 5
IL_0054: ldc.i4.1
IL_0055: add
IL_0056: stloc.s 5
IL_0058: ldloc.s 5
IL_005a: ldloc.0
IL_005b: callvirt instance int32 [mscorlib]System.Collections.BitArray::get_Length()
IL_0060: blt.s IL_0025
// end loop
IL_0062: ldarg.1
IL_0063: ldloc.1
IL_0064: call void [mscorlib]System.IO.File::WriteAllBytes(string, uint8[])
IL_0069: ret
} // end of method Program::Hide
.method private hidebysig static
int32 GetNextPiDigit () cil managed
{
// Method begins at RVA 0x20f0
// Code size 32 (0x20)
.maxstack 2
.locals init (
[0] int32
)
IL_0000: ldsfld class [mscorlib]System.IO.FileStream csharp.Program::piFile
IL_0005: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte()
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: ldc.i4.s 10
IL_000e: bne.un.s IL_001b
IL_0010: ldsfld class [mscorlib]System.IO.FileStream csharp.Program::piFile
IL_0015: callvirt instance int32 [mscorlib]System.IO.Stream::ReadByte()
IL_001a: stloc.0
IL_001b: ldloc.0
IL_001c: ldc.i4.s 48
IL_001e: sub
IL_001f: ret
} // end of method Program::GetNextPiDigit
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x211c
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class csharp.Program
Now, you see, I'm terrible at ILCode, so I compiled it to a DLL:
ilasm /output:test.dll /dll /debug source
Then I used ILSpy (the cross-platform command-line version since I'm using Linux):
ilspycmd test.dll > source.cs
The source I got:
using System;
using System.Collections;
using System.IO;
using System.Text;
namespace csharp
{
internal class Program
{
private static FileStream piFile;
private static void Main(string[] args)
{
piFile = new FileStream("one-million-digits.txt", FileMode.Open, FileAccess.Read);
Hide("original.bmp", "result.bmp", "<CENSORED>");
}
private static void Hide(string srcPath, string dstPath, string secret)
{
BitArray bitArray = new BitArray(Encoding.UTF8.GetBytes(secret));
byte[] array = File.ReadAllBytes(srcPath);
int num = array[14] + 14;
for (int i = 0; i < bitArray.Length; i++)
{
int num2 = num + GetNextPiDigit();
byte b = (byte)(0xFE & array[num2]);
array[num2] = (byte)(b + Convert.ToByte(bitArray[i]));
num += 10;
}
File.WriteAllBytes(dstPath, array);
}
private static int GetNextPiDigit()
{
int num = piFile.ReadByte();
if (num == 10)
{
num = piFile.ReadByte();
}
return num - 48;
}
}
}
The million digits of Pi start after the decimal point (e.g. 14159265...
). This was found out by asking the admins. Thanks admins, you're doing great!
The algorithm works like this (pseudocode):
Initial conditions:
* bitmap = OpenTheFile("original.bmp").ExtractItsBytesIntoAnArray(); // we aren't provided the original, but it's required to encode
* flag = ExtractBitsFrom("<INSERT FLAG HERE>");
* num = bitmap[14] + 14;
Algorithm:
* for every bitIndex in the flag,
* num2 = num + GetNextPiDigit();
* b = ClearLastBit(bitmap[num2]);
* bitmap[num2] = b + GetBitAsZeroOrOne(flag[i]);
* num = num + 10;
End of loop:
* OpenTheFile("result.bmp").WriteBytes(bitmap);
To decode this, you simply have to go use the same loop but replace these two instructions:
b = ClearLastBit(bitmap[num2]);
bitmap[num2] = b + GetBitAsZeroOrOne(flag[i]);
with some type of logic to set a bit in a character to 0 or 1.
Here's the script that I used for this:
(I had to use Python 2 cause it was complaining about non-UTF8 characters in result.bmp
)
with open("result.bmp", "r") as file:
bmp = file.read()
with open("pi.txt", "r") as file:
pi = file.read()
def getPi():
global cpi
cpi += 1
return int(pi[cpi-1])
cpi = 0
num = ord(bmp[14]) + 14
flagLst = ("a "*64).split(" ")
charNum = 0
for i in range(0, 64): # Going to 64 characters cause I don't know the length of the flag
for i2 in range(0, 8):
pin = getPi()
num2 = num + pin
charNum |= (0x01 & ord(bmp[num2])) << i2
# print("num : %d / pi : %d / num2 : %d / charNum : %s" % (num, pin, num2, bin(charNum))) # debugging
num += 10
# print(chr(charNum)) # debugging
flagLst[i] = chr(charNum)
charNum = 0
flag = "".join(flagLst)
print(flag)
Running this script got me:
hexCTF{l00k_wh0_l3arned_t0_sp34k_byt3c0de}F��c��u�Ѡ�Y��P,8/
That extra gibberish at the end is non-encoded characters, so you can just ignore that.
Extracting that first bit gets you the flag.
pi.txt
is simply a truncated (remove the first two characters, 3.
) version of pi1000000.txt from angio.net.