UIUCTF 2024: tooooo fancy 😏 [rev] writeup
Writeup author: fsharp
Introduction
For this year's UIUCTF, I played with the ADMinions
team and mainly worked on reverse engineering and cryptography challenges. A challenge I solved during the CTF is tooooo fancy 😏
, which I got second blood on and found to be very fancy indeed.
Problem description
Author: spicypete
I really hope this challenge TiCkLes your fancy! It is my most cursed challenge yet.
(Files provided: main.tbc
and Dockerfile
)
Image Not Showing
Possible Reasons
- The image was uploaded to a note which you don't have access to
- The note which the image was originally uploaded to has been deleted
Learn More →
Initial analysis: What are we dealing with?
The Dockerfile
sets up a Linux environment where you can run the main.tbc
file. What kind of file is that? Let's dig around for answers.
The first several lines of main.tbc
provide some information:
There's an if
statement and a mention of ByteCode
, so it seems to be some sort of program.
From the first line, the tbcload
package is loaded. Looking it up on Google leads to a webpage from the Tcler's Wiki. The wiki is about a programming language called Tcl, and the webpage contains the following sentences:
tbcload, loads code obfuscated by tclcompiler, tclchecker, and procomp.
tbcload is for source protection of deployed programs. It appeared originally in TclPro, and is part of ActiveState's ActiveTcl and Tcl Dev Kit distributions.
At this point, we can guess that main.tbc
is a Tcl program that has been obfuscated. A commented section of the Dockerfile
confirms this:
The section contains instructions on creating the main.tbc
file from the main.tcl
source code, which includes compiling main.tcl
with tclcompiler
.
So, the goal of this challenge is to reverse-engineer the obfuscated main.tbc
Tcl program and recover the flag from it!
Decoding the main.tbc
bytecodes
Part of the bytecode in main.tbc
looks like this:
They're a mess! Luckily, someone made a program called 'tbcload', which decodes these bytecodes and turns them into a format we can understand more easily. On a Windows computer, we can run tbcload-windows-amd64.exe decompile --detail main.tbc
to generate the disassembly of main.tbc
that we can further analyze.
The disassembly looks like this:
Understanding the disassembly
The bottom section of the disassembly contains many lit
s, which we can assume are string lit
erals used by the program. Above this section are the disassembly of 52 commands, with bytecodes like push1 0
and invokeStk1 3
. The number in the brackets preceding each bytecode indicates their location.
Searching for the bytecodes lands us on a webpage that describes what they do. We see that many of them are stack-based, where the execution of a bytecode leads to a stack being manipulated.
There are 19 kinds of bytecodes used in the challenge program. Here's my understanding of what they do.
General operations:
push1 <i>
: Push an object at the i
-th index of the 'object array' onto the stack. In the challenge, i
is always between 0 and 44, so we can guess that the 'object array' is the 45 literals / variables at the end of the disassembly, and i
refers to one of the lit
erals.
pop
: Remove the object at the top of the stack.
done
: Stop executing the bytecodes.
startCommand <num1> <num2>
: Mark the start of a command. This can be safely ignored in this challenge.
Comparison operations:
lt
: Push the result of (2nd-topmost stack object < topmost stack object)
onto the stack. The result will be logical, i.e. true
or false
.
eq
: Push the result of (2nd-topmost stack object == topmost stack object)
onto the stack.
ge
: Push the result of (2nd-topmost stack object >= topmost stack object)
onto the stack.
Jump operations:
jump1 <num>
: Jump to the bytecode located at pc + num
. pc
stands for 'program counter' and is used by the Tcl shell to keep track of which bytecode is being executed. So, if there's a (123)jump1 456
bytecode and we reach it, the shell will jump to the bytecode at 123 + 456 = 579
.
jump4 <num>
: Behaves the same as jump1
. The 1
and 4
suffixes refer to the number of bytes used to store num
.
jumpTrue1 <num>
: If the topmost stack object is true
, jump to pc + num
.
jumpTrue4 <num>
: Behaves the same as jumpTrue1
.
jumpFalse1 <num>
: If the topmost stack object is false
, jump to pc + num
.
Reading and writing variables:
loadStk
: Load the variable referred by the topmost stack object into the stack. What this means is that we can consider push1 <i>
to put the name of the i
-th variable from the object array onto the stack, and loadStk
retrieves the actual value of that variable using its name.
storeStk
: Set the value of the variable referred by the 2nd-topmost stack object to the topmost stack object.
incrStkImm <num>
: Increment the value of the variable referred by the topmost stack object by num
.
listIndex
: Push a list element onto the stack, with the 2nd-topmost stack object referring to the list, and the topmost stack object being the list index.
Invoke / Eval operations:
exprStk
: Evaluate the topmost stack object as an expression. For example, if the expression is the string 1 + 2 * 3
, then this bytecode turns the topmost stack object into the number 7
.
invokeStk1 <num>
: Pop num
objects from the stack and execute a Tcl command, with the first object as the command name and the remaining objects as arguments to the command. For instance, if the top 2 objects are puts
and the string wow
respectively, then invokeStk1 2
executes puts "wow"
.
String operations:
strcat <num>
: Push the concatenation of the top num
stack objects onto the stack.
With all of this in mind, we can reverse the disassembly.
A peculiarity with sets
By carefully keeping track of the state of the stack for each bytecode, we'll notice things we usually see in a program, such as if statements and for loops.
Something in particular stood out to me when I first reversed the disassembly:
The set
Tcl command sets the value of a variable. In the literal above, there are two statements: The first sets set
to the hexadecimal MD5 digest of itself, while the second statement sets $set
to the variable wat
. However, $set
is NOT the same as set
, with the former being the value of the variable called set
and the latter being the name of that variable.
Doesn't the second statement look a bit strange? How is it possible to set the value of the value of a variable to something else?
Let's try running some Tcl code to see what's going on.
This prints 123
, 456
, and a
. Now let's try doing what the second statement did:
This time, the first line printed is wow
, not 123
. So, the second statement creates a new variable whose name is equal to the value of the set
variable, and sets its value to the value of the wat
variable.
Solving…
The bytecode disassembly is equivalent to this Python script:
from hashlib import md5
input = input("Enter flag:\n").encode()
wtf = [492636, 406872, 481036, 456848, 489619, 458431, 466416, 482512, 413259, 424205, 476839, 441981, 489662, 490341, 510145, 428550, 480326, 499060, 417618, 486312, 452152, 470192, 476645, 496113, 433471, 403520, 503087, 467533, 508246, 477960, 367896, 408320, 423790, 439274, 525261, 462079]
definitely_not_flag_length = 36
set = "set"
wats = [254, 109, 126, 66, 220, 98, 230, 17, 83, 106, 123, 57, 214, 225, 96, 113, 126, 47, 73, 32, 174, 224, 111, 153, 83, 78, 253, 164, 96, 208, 68, 49, 55, 195, 2, 84, 39, 66, 84, 47, 248, 189, 176, 135, 105, 99, 124, 92, 180, 102, 97, 213, 118, 94, 155, 34, 225, 76, 168, 131, 106, 69, 64, 75, 162, 58, 138, 22, 205, 146, 228, 15, 235, 155, 253, 236, 158, 180, 70, 109, 115, 154, 208, 134, 14, 255, 244, 103, 203, 182, 199, 129, 43, 186, 101, 183, 25, 178, 212, 56, 49, 12, 18, 209, 1, 51, 172, 117, 233, 48, 56, 219, 177, 86, 3, 67, 139, 149, 217, 103, 233, 98, 3, 139, 3, 243, 248, 222, 6, 9, 87, 14, 230, 211, 121, 198, 140, 182, 27, 38, 145, 189, 110, 74, 99, 116, 99, 193, 47, 237, 16, 133, 104, 109, 86, 46, 15, 59, 40, 153, 45, 177, 0, 163, 157, 129, 211, 68, 158, 135, 93, 230, 60, 65, 116, 254, 204, 67, 44, 51, 135, 230, 88, 198, 141, 156, 108, 63, 84, 164, 119, 240, 224, 121, 168, 216, 116, 250, 191, 58, 237, 77, 8, 206, 134, 65, 222, 198, 40, 119, 202, 76, 204, 153, 234, 123, 241, 135, 13, 172, 251, 1, 247, 169, 206, 184, 215, 63, 225, 3, 17, 88, 238, 215, 197, 97, 50, 101, 130, 146, 46, 45, 185, 163, 18, 219, 174, 182, 247, 73, 209, 62, 195, 139, 154, 92, 248, 203, 138, 131, 45, 122, 186, 187, 151, 25, 27, 185, 110, 251, 3, 99, 206, 85, 187, 246, 176, 56, 215, 87, 100, 132, 69, 112, 56, 60, 137, 194, 229, 161, 69, 124, 137, 207, 251, 28, 66, 10, 228, 3, 176, 3, 23, 219, 183, 34, 37, 49, 87, 85, 153, 145, 186, 194, 45, 10, 136, 245, 224, 25, 99, 67, 115, 65, 7, 198, 99, 225, 167, 210, 220, 166, 133, 137, 168, 233, 68, 250, 215, 232, 149, 89, 50, 164, 44, 92, 227, 83, 81, 231, 127, 108, 61, 104, 245, 247, 247, 14, 206, 23, 62, 156, 117, 80, 240, 238, 152, 126, 143, 177, 208, 127, 238, 238, 84, 235, 239, 218, 117, 4, 149, 32, 167, 129, 98, 106, 210, 103, 90, 103, 163, 58, 166, 1, 232, 180, 145, 173, 22, 254, 53, 217, 74, 239, 128, 35, 113, 83, 94, 71, 39, 87, 245, 83, 118, 78, 29, 19, 37, 1, 133, 105, 81, 163, 77, 25, 97, 117, 221, 198, 186, 157, 81, 149, 144, 30, 183, 208, 158, 12, 90, 211, 144, 92, 38, 98, 7, 106, 165, 14, 113, 128, 224, 130, 230, 37, 175, 62, 189, 14, 61, 124, 105, 81, 37, 222, 147, 186, 6, 100, 218, 71, 77, 241, 78, 198, 224, 51, 97, 3, 128, 104, 126, 126, 77, 94, 165, 113, 52, 83, 2, 226, 237, 224, 14, 2, 91, 218, 110, 238, 160, 210, 183, 121, 32, 158, 3, 223, 233, 219, 247, 79, 112, 106, 219, 78, 111, 21, 158, 187, 84, 31, 70, 224, 135, 77, 189, 248, 216, 223, 27, 65, 16, 218, 129, 135, 37, 147, 255, 112, 191, 228, 120, 148, 89, 30, 211, 69, 113, 142, 184, 0, 89, 28, 65, 21, 188, 251, 28, 8, 40, 20, 182, 248, 95, 223, 110, 235, 39, 25, 175, 125, 204, 130, 223, 32, 96, 41, 233, 76, 42, 197, 3, 101, 191, 197, 178, 194, 218, 231, 211, 145, 147, 157, 201, 179, 22, 46, 130, 243, 49, 121, 253, 8, 240, 146, 163, 167, 154, 233, 91, 234, 114, 71, 105, 239, 71, 81, 122, 148, 237, 140, 26, 238, 123, 61, 39, 52, 207, 39, 126, 8, 154, 254, 208, 2, 55, 180, 3, 39, 179, 55, 64, 205, 199, 70, 29, 24, 38, 207, 74, 152, 32, 190, 252, 53, 115, 178, 111, 212, 179, 179, 201, 183, 113, 72, 0, 225, 30, 20, 23, 18, 60, 111, 187, 123, 212, 152, 19, 116, 153, 126, 6, 194, 231, 55, 239, 71, 244, 93, 10, 199, 248, 228, 144, 71, 174, 171, 191, 157, 42, 234, 15, 118, 3, 84, 49, 33, 253, 231, 34, 63, 12, 106, 26, 199, 168, 41, 156, 253, 143, 234, 180, 45, 196, 36, 103, 231, 233, 187, 201, 142, 148, 33, 213, 213, 8, 153, 76, 245, 145, 0, 22, 32, 174, 94, 121, 20, 214, 207, 60, 26, 228, 190, 101, 179, 191, 141, 100, 174, 6, 75, 155, 130, 149, 242, 84, 190, 28, 147, 66, 218, 212, 104, 160, 93, 47, 102, 40, 122, 136, 175, 229, 214, 181, 22, 192, 148, 159, 108, 153, 32, 187, 136, 80, 69, 173, 6, 127, 6, 120, 248, 228, 196, 160, 113, 137, 208, 103, 27, 174, 45, 21, 20, 253, 110, 183, 31, 159, 114, 163, 158, 69, 53, 20, 34, 205, 22, 253, 169, 253, 23, 93, 73, 68, 126, 124, 250, 169, 157, 198, 133, 199, 51, 217, 250, 81, 227, 153, 99, 40, 195, 181, 59, 76, 182, 151, 118, 94, 126, 213, 167, 209, 191, 161, 214, 38, 238, 228, 121, 238, 0, 122, 233, 19, 110, 152, 133, 115, 93, 211, 200, 119, 239, 112, 209, 231, 2, 163, 69, 235, 165, 64, 82, 37, 27, 174, 200, 46, 254, 9, 87, 217, 199, 132, 140, 85, 33, 152, 150, 232, 111, 84, 2, 7, 173, 241, 189, 166, 50, 230, 21, 44, 202, 150, 176, 79, 243, 123, 178, 27, 98, 105, 28, 186, 247, 101, 197, 240, 77, 220, 240, 253, 155, 201, 109, 174, 3, 215, 129, 109, 63, 250, 28, 88, 94, 174, 242, 237, 14, 165, 96, 241, 176, 199, 39, 143, 101, 107, 232, 33, 126, 133, 102, 231, 240, 86, 180, 113, 148, 98, 238, 156, 226, 1, 96, 25, 43, 223, 17, 97, 29, 107, 22, 47, 182, 211, 237, 126, 249, 22, 39, 63, 71, 141, 123, 214, 109, 77, 1, 121, 3, 215, 242, 99, 8, 15, 95, 159, 26, 14, 240, 67, 227, 249, 24, 14, 251, 11, 70, 227, 151, 69, 111, 168, 59, 236, 30, 148, 59, 74, 207, 205, 216, 13, 101, 81, 13, 151, 242, 199, 236, 24, 225, 108, 107, 155, 87, 17, 56, 97, 169, 66, 139, 163, 68, 82, 75, 90, 16, 170, 91, 39, 21, 191, 137, 104, 133, 16, 75, 190, 197, 234, 213, 177, 79, 195, 152, 32, 248, 0, 78, 127, 144, 170, 185, 146, 53, 113, 82, 83, 0, 214, 143, 151, 211, 206, 191, 185, 151, 185, 100, 191, 168, 160, 223, 185, 124, 183, 94, 14, 64, 112, 139, 162, 36, 239, 29, 217, 10, 187, 31, 1, 137, 123, 173, 21, 72, 117, 54, 248, 186, 29, 180, 88, 20, 81, 19, 242, 231, 207, 112, 166, 34, 28, 109, 55, 221, 115, 182, 180, 175, 66, 182, 31, 57, 77, 217, 68, 27, 71, 108, 239, 224, 227, 59, 12, 211, 17, 178, 49, 211, 42, 224, 84, 45, 23, 219, 4, 157, 42, 52, 151, 102, 141, 227, 163, 153, 246, 12, 231, 20, 89, 97, 39, 205, 212, 42, 58, 48, 174, 241, 146, 139, 35, 121, 93, 107, 32, 107, 126, 86, 61, 168, 204, 219, 219, 18, 100, 30, 49, 169, 102, 16, 219, 195, 5, 59, 18, 185, 214, 39, 123, 44, 41, 69, 151, 149, 52, 212, 77, 151, 152, 144, 122, 114, 254, 109, 162, 208, 55, 27, 193, 33, 64, 110, 127, 130, 186, 55, 21, 169, 131, 206, 99, 230, 117, 11, 36, 239, 40, 57, 62, 70, 222, 132, 90, 88, 16, 155, 118, 154, 180, 25, 176, 92]
md5_vars = {}
for wat in wats:
set = md5(set.encode()).hexdigest()
md5_vars[set] = wat
unfanciness = 0
for n in range(definitely_not_flag_length):
set = md5(b"set").hexdigest()
epic = 0
for k in range(definitely_not_flag_length):
if k == 0:
for i in range(((n + 3) * n + 1) // 2):
set = md5(set.encode()).hexdigest()
epic += md5_vars[set] * input[definitely_not_flag_length - 1 - k]
offset = n + k + 1
if k >= (definitely_not_flag_length - n - 1):
offset = definitely_not_flag_length * 2 - offset - 1
for i in range(offset):
set = md5(set.encode()).hexdigest()
unfanciness |= (epic ^ wtf[n])
if unfanciness == 0:
print("Wow, you must be quite fancy. Exquisite job!")
else:
print("That's not very fancy. Throw it away!")
The goal is to enter a flag such that unfanciness
is 0
. This is only possible if epic ^ wtf[n] == 0
.
For a particular n
, epic
can be expressed as a0 * flag[0] + a1 * flag[1] + ... + a35 * flag[35]
, and we know that it must be equal to wtf[n]
. So, we have a 36 x 36 matrix A
with values coming from wats
, a vector b
equal to wtf
, and the equation Ax = b
, where x
is a vector representing the flag.
We can solve this equation using SageMath. Let's modify this Python script to print a Sage script that can recover the flag:
from hashlib import md5
wtf = [492636, 406872, 481036, 456848, 489619, 458431, 466416, 482512, 413259, 424205, 476839, 441981, 489662, 490341, 510145, 428550, 480326, 499060, 417618, 486312, 452152, 470192, 476645, 496113, 433471, 403520, 503087, 467533, 508246, 477960, 367896, 408320, 423790, 439274, 525261, 462079]
definitely_not_flag_length = 36
set = "set"
wats = [254, 109, 126, 66, 220, 98, 230, 17, 83, 106, 123, 57, 214, 225, 96, 113, 126, 47, 73, 32, 174, 224, 111, 153, 83, 78, 253, 164, 96, 208, 68, 49, 55, 195, 2, 84, 39, 66, 84, 47, 248, 189, 176, 135, 105, 99, 124, 92, 180, 102, 97, 213, 118, 94, 155, 34, 225, 76, 168, 131, 106, 69, 64, 75, 162, 58, 138, 22, 205, 146, 228, 15, 235, 155, 253, 236, 158, 180, 70, 109, 115, 154, 208, 134, 14, 255, 244, 103, 203, 182, 199, 129, 43, 186, 101, 183, 25, 178, 212, 56, 49, 12, 18, 209, 1, 51, 172, 117, 233, 48, 56, 219, 177, 86, 3, 67, 139, 149, 217, 103, 233, 98, 3, 139, 3, 243, 248, 222, 6, 9, 87, 14, 230, 211, 121, 198, 140, 182, 27, 38, 145, 189, 110, 74, 99, 116, 99, 193, 47, 237, 16, 133, 104, 109, 86, 46, 15, 59, 40, 153, 45, 177, 0, 163, 157, 129, 211, 68, 158, 135, 93, 230, 60, 65, 116, 254, 204, 67, 44, 51, 135, 230, 88, 198, 141, 156, 108, 63, 84, 164, 119, 240, 224, 121, 168, 216, 116, 250, 191, 58, 237, 77, 8, 206, 134, 65, 222, 198, 40, 119, 202, 76, 204, 153, 234, 123, 241, 135, 13, 172, 251, 1, 247, 169, 206, 184, 215, 63, 225, 3, 17, 88, 238, 215, 197, 97, 50, 101, 130, 146, 46, 45, 185, 163, 18, 219, 174, 182, 247, 73, 209, 62, 195, 139, 154, 92, 248, 203, 138, 131, 45, 122, 186, 187, 151, 25, 27, 185, 110, 251, 3, 99, 206, 85, 187, 246, 176, 56, 215, 87, 100, 132, 69, 112, 56, 60, 137, 194, 229, 161, 69, 124, 137, 207, 251, 28, 66, 10, 228, 3, 176, 3, 23, 219, 183, 34, 37, 49, 87, 85, 153, 145, 186, 194, 45, 10, 136, 245, 224, 25, 99, 67, 115, 65, 7, 198, 99, 225, 167, 210, 220, 166, 133, 137, 168, 233, 68, 250, 215, 232, 149, 89, 50, 164, 44, 92, 227, 83, 81, 231, 127, 108, 61, 104, 245, 247, 247, 14, 206, 23, 62, 156, 117, 80, 240, 238, 152, 126, 143, 177, 208, 127, 238, 238, 84, 235, 239, 218, 117, 4, 149, 32, 167, 129, 98, 106, 210, 103, 90, 103, 163, 58, 166, 1, 232, 180, 145, 173, 22, 254, 53, 217, 74, 239, 128, 35, 113, 83, 94, 71, 39, 87, 245, 83, 118, 78, 29, 19, 37, 1, 133, 105, 81, 163, 77, 25, 97, 117, 221, 198, 186, 157, 81, 149, 144, 30, 183, 208, 158, 12, 90, 211, 144, 92, 38, 98, 7, 106, 165, 14, 113, 128, 224, 130, 230, 37, 175, 62, 189, 14, 61, 124, 105, 81, 37, 222, 147, 186, 6, 100, 218, 71, 77, 241, 78, 198, 224, 51, 97, 3, 128, 104, 126, 126, 77, 94, 165, 113, 52, 83, 2, 226, 237, 224, 14, 2, 91, 218, 110, 238, 160, 210, 183, 121, 32, 158, 3, 223, 233, 219, 247, 79, 112, 106, 219, 78, 111, 21, 158, 187, 84, 31, 70, 224, 135, 77, 189, 248, 216, 223, 27, 65, 16, 218, 129, 135, 37, 147, 255, 112, 191, 228, 120, 148, 89, 30, 211, 69, 113, 142, 184, 0, 89, 28, 65, 21, 188, 251, 28, 8, 40, 20, 182, 248, 95, 223, 110, 235, 39, 25, 175, 125, 204, 130, 223, 32, 96, 41, 233, 76, 42, 197, 3, 101, 191, 197, 178, 194, 218, 231, 211, 145, 147, 157, 201, 179, 22, 46, 130, 243, 49, 121, 253, 8, 240, 146, 163, 167, 154, 233, 91, 234, 114, 71, 105, 239, 71, 81, 122, 148, 237, 140, 26, 238, 123, 61, 39, 52, 207, 39, 126, 8, 154, 254, 208, 2, 55, 180, 3, 39, 179, 55, 64, 205, 199, 70, 29, 24, 38, 207, 74, 152, 32, 190, 252, 53, 115, 178, 111, 212, 179, 179, 201, 183, 113, 72, 0, 225, 30, 20, 23, 18, 60, 111, 187, 123, 212, 152, 19, 116, 153, 126, 6, 194, 231, 55, 239, 71, 244, 93, 10, 199, 248, 228, 144, 71, 174, 171, 191, 157, 42, 234, 15, 118, 3, 84, 49, 33, 253, 231, 34, 63, 12, 106, 26, 199, 168, 41, 156, 253, 143, 234, 180, 45, 196, 36, 103, 231, 233, 187, 201, 142, 148, 33, 213, 213, 8, 153, 76, 245, 145, 0, 22, 32, 174, 94, 121, 20, 214, 207, 60, 26, 228, 190, 101, 179, 191, 141, 100, 174, 6, 75, 155, 130, 149, 242, 84, 190, 28, 147, 66, 218, 212, 104, 160, 93, 47, 102, 40, 122, 136, 175, 229, 214, 181, 22, 192, 148, 159, 108, 153, 32, 187, 136, 80, 69, 173, 6, 127, 6, 120, 248, 228, 196, 160, 113, 137, 208, 103, 27, 174, 45, 21, 20, 253, 110, 183, 31, 159, 114, 163, 158, 69, 53, 20, 34, 205, 22, 253, 169, 253, 23, 93, 73, 68, 126, 124, 250, 169, 157, 198, 133, 199, 51, 217, 250, 81, 227, 153, 99, 40, 195, 181, 59, 76, 182, 151, 118, 94, 126, 213, 167, 209, 191, 161, 214, 38, 238, 228, 121, 238, 0, 122, 233, 19, 110, 152, 133, 115, 93, 211, 200, 119, 239, 112, 209, 231, 2, 163, 69, 235, 165, 64, 82, 37, 27, 174, 200, 46, 254, 9, 87, 217, 199, 132, 140, 85, 33, 152, 150, 232, 111, 84, 2, 7, 173, 241, 189, 166, 50, 230, 21, 44, 202, 150, 176, 79, 243, 123, 178, 27, 98, 105, 28, 186, 247, 101, 197, 240, 77, 220, 240, 253, 155, 201, 109, 174, 3, 215, 129, 109, 63, 250, 28, 88, 94, 174, 242, 237, 14, 165, 96, 241, 176, 199, 39, 143, 101, 107, 232, 33, 126, 133, 102, 231, 240, 86, 180, 113, 148, 98, 238, 156, 226, 1, 96, 25, 43, 223, 17, 97, 29, 107, 22, 47, 182, 211, 237, 126, 249, 22, 39, 63, 71, 141, 123, 214, 109, 77, 1, 121, 3, 215, 242, 99, 8, 15, 95, 159, 26, 14, 240, 67, 227, 249, 24, 14, 251, 11, 70, 227, 151, 69, 111, 168, 59, 236, 30, 148, 59, 74, 207, 205, 216, 13, 101, 81, 13, 151, 242, 199, 236, 24, 225, 108, 107, 155, 87, 17, 56, 97, 169, 66, 139, 163, 68, 82, 75, 90, 16, 170, 91, 39, 21, 191, 137, 104, 133, 16, 75, 190, 197, 234, 213, 177, 79, 195, 152, 32, 248, 0, 78, 127, 144, 170, 185, 146, 53, 113, 82, 83, 0, 214, 143, 151, 211, 206, 191, 185, 151, 185, 100, 191, 168, 160, 223, 185, 124, 183, 94, 14, 64, 112, 139, 162, 36, 239, 29, 217, 10, 187, 31, 1, 137, 123, 173, 21, 72, 117, 54, 248, 186, 29, 180, 88, 20, 81, 19, 242, 231, 207, 112, 166, 34, 28, 109, 55, 221, 115, 182, 180, 175, 66, 182, 31, 57, 77, 217, 68, 27, 71, 108, 239, 224, 227, 59, 12, 211, 17, 178, 49, 211, 42, 224, 84, 45, 23, 219, 4, 157, 42, 52, 151, 102, 141, 227, 163, 153, 246, 12, 231, 20, 89, 97, 39, 205, 212, 42, 58, 48, 174, 241, 146, 139, 35, 121, 93, 107, 32, 107, 126, 86, 61, 168, 204, 219, 219, 18, 100, 30, 49, 169, 102, 16, 219, 195, 5, 59, 18, 185, 214, 39, 123, 44, 41, 69, 151, 149, 52, 212, 77, 151, 152, 144, 122, 114, 254, 109, 162, 208, 55, 27, 193, 33, 64, 110, 127, 130, 186, 55, 21, 169, 131, 206, 99, 230, 117, 11, 36, 239, 40, 57, 62, 70, 222, 132, 90, 88, 16, 155, 118, 154, 180, 25, 176, 92]
md5_vars = {}
for wat in wats:
set = md5(set.encode()).hexdigest()
md5_vars[set] = wat
matrix = [[0 for i in range(definitely_not_flag_length)] for j in range(definitely_not_flag_length)]
for n in range(definitely_not_flag_length):
set = md5(b"set").hexdigest()
for k in range(definitely_not_flag_length):
if k == 0:
for i in range(((n + 3) * n + 1) // 2):
set = md5(set.encode()).hexdigest()
matrix[n][definitely_not_flag_length - 1 - k] = md5_vars[set]
offset = n + k + 1
if k >= (definitely_not_flag_length - n - 1):
offset = definitely_not_flag_length * 2 - offset - 1
for i in range(offset):
set = md5(set.encode()).hexdigest()
print(f"A = matrix({matrix})\n")
print(f"b = vector({wtf})\n")
print("flag = bytes(A.solve_right(b)).decode()")
print("print(flag)")
This gives us the following Sage script:
A = matrix([[126, 179, 20, 216, 91, 222, 30, 113, 117, 108, 198, 176, 176, 139, 88, 202, 119, 230, 109, 140, 233, 51, 129, 70, 138, 34, 99, 39, 96, 224, 113, 123, 230, 66, 109, 254], [0, 8, 22, 182, 223, 218, 147, 183, 83, 4, 61, 99, 3, 56, 154, 238, 76, 240, 60, 86, 182, 98, 172, 43, 109, 22, 225, 124, 66, 208, 111, 126, 57, 17, 220, 126], [234, 225, 154, 46, 248, 27, 110, 186, 208, 94, 149, 104, 225, 23, 215, 92, 215, 204, 224, 65, 46, 27, 3, 117, 186, 115, 205, 76, 92, 84, 68, 153, 47, 214, 83, 98], [213, 15, 30, 254, 130, 95, 65, 238, 6, 158, 71, 32, 245, 167, 219, 87, 248, 197, 153, 121, 116, 15, 38, 139, 233, 101, 154, 146, 168, 180, 47, 49, 83, 73, 225, 106], [28, 8, 118, 20, 208, 243, 223, 16, 160, 100, 12, 39, 167, 247, 210, 183, 100, 203, 97, 234, 168, 254, 59, 145, 3, 48, 183, 208, 228, 131, 102, 248, 55, 78, 32, 96], [120, 147, 153, 3, 23, 2, 49, 110, 218, 210, 218, 90, 87, 129, 247, 220, 34, 132, 138, 50, 123, 216, 204, 40, 189, 243, 56, 25, 134, 15, 106, 97, 189, 195, 253, 174], [23, 248, 66, 76, 84, 18, 55, 121, 235, 129, 183, 71, 211, 245, 98, 14, 166, 37, 69, 131, 101, 241, 116, 67, 153, 110, 248, 219, 178, 14, 235, 69, 213, 176, 2, 164], [167, 93, 228, 218, 245, 49, 60, 180, 253, 39, 135, 121, 77, 144, 83, 106, 206, 133, 49, 112, 45, 130, 135, 250, 44, 45, 74, 222, 177, 212, 255, 155, 64, 118, 135, 84], [235, 209, 73, 196, 212, 145, 33, 111, 3, 8, 25, 37, 32, 241, 92, 118, 210, 23, 137, 87, 56, 122, 146, 13, 191, 51, 177, 99, 6, 86, 56, 244, 253, 75, 94, 105], [166, 165, 191, 68, 160, 104, 0, 253, 187, 39, 240, 175, 147, 158, 78, 38, 78, 103, 62, 168, 85, 60, 186, 46, 172, 58, 135, 0, 116, 9, 3, 49, 103, 236, 162, 155], [109, 50, 64, 161, 126, 113, 160, 22, 231, 123, 179, 146, 125, 255, 3, 198, 98, 29, 90, 156, 233, 153, 137, 187, 45, 251, 237, 230, 163, 99, 87, 67, 12, 203, 158, 58], [126, 174, 230, 82, 214, 124, 137, 93, 32, 34, 212, 55, 163, 204, 112, 223, 224, 7, 19, 103, 117, 68, 145, 194, 151, 185, 1, 77, 88, 157, 193, 14, 139, 18, 182, 180], [211, 133, 3, 21, 37, 38, 250, 208, 47, 174, 63, 152, 64, 167, 130, 191, 233, 51, 106, 37, 163, 80, 250, 186, 229, 25, 163, 247, 8, 198, 129, 47, 230, 149, 209, 199], [14, 237, 102, 215, 44, 27, 238, 169, 103, 102, 94, 12, 19, 205, 154, 223, 228, 219, 97, 165, 1, 58, 240, 215, 194, 161, 27, 18, 169, 206, 141, 211, 237, 211, 217, 1], [216, 240, 126, 231, 129, 202, 174, 228, 157, 27, 40, 121, 106, 116, 199, 233, 32, 120, 247, 3, 14, 133, 166, 238, 232, 45, 69, 185, 219, 206, 134, 156, 68, 16, 121, 103], [68, 13, 67, 249, 240, 109, 150, 200, 121, 198, 174, 122, 20, 26, 153, 70, 91, 96, 148, 79, 128, 113, 105, 1, 152, 149, 10, 124, 110, 174, 184, 65, 108, 158, 133, 198], [195, 82, 101, 227, 22, 86, 63, 176, 46, 238, 133, 45, 136, 214, 199, 126, 29, 234, 41, 89, 112, 104, 128, 81, 232, 126, 89, 136, 137, 251, 182, 215, 222, 63, 135, 104], [206, 152, 75, 81, 249, 39, 180, 250, 79, 254, 0, 199, 21, 175, 207, 168, 6, 24, 114, 233, 30, 106, 126, 224, 163, 180, 143, 50, 245, 207, 3, 247, 63, 198, 84, 93], [36, 191, 32, 90, 13, 24, 63, 113, 28, 243, 9, 122, 51, 20, 229, 60, 41, 194, 38, 71, 76, 211, 219, 126, 130, 77, 145, 177, 164, 224, 251, 99, 73, 225, 40, 164], [180, 239, 185, 248, 16, 151, 14, 71, 148, 88, 123, 87, 233, 217, 253, 214, 26, 156, 231, 207, 105, 42, 69, 78, 77, 230, 25, 173, 208, 44, 25, 28, 206, 209, 3, 119], [180, 88, 29, 151, 0, 170, 242, 251, 141, 98, 94, 178, 217, 19, 250, 110, 181, 228, 253, 55, 74, 239, 197, 113, 111, 94, 37, 97, 22, 127, 92, 99, 66, 85, 62, 17], [12, 175, 20, 217, 185, 78, 91, 199, 11, 123, 238, 174, 27, 199, 110, 81, 183, 22, 190, 143, 239, 152, 71, 3, 142, 21, 165, 175, 117, 254, 238, 227, 67, 10, 187, 195], [52, 211, 66, 81, 10, 100, 127, 39, 236, 70, 214, 156, 242, 98, 132, 152, 227, 31, 192, 101, 234, 71, 32, 81, 101, 184, 158, 113, 62, 221, 53, 238, 83, 115, 228, 246], [205, 151, 17, 182, 19, 187, 191, 144, 21, 24, 227, 109, 226, 237, 105, 140, 133, 153, 159, 148, 179, 180, 244, 190, 122, 191, 0, 187, 52, 189, 198, 217, 84, 81, 65, 3], [32, 212, 102, 178, 31, 242, 31, 168, 170, 191, 225, 151, 77, 1, 14, 28, 85, 115, 99, 114, 159, 191, 45, 93, 252, 148, 197, 89, 84, 83, 14, 186, 74, 235, 231, 7], [49, 107, 42, 141, 49, 57, 231, 1, 160, 185, 137, 108, 69, 1, 96, 165, 186, 33, 93, 40, 163, 108, 141, 196, 10, 53, 237, 178, 28, 31, 2, 61, 157, 239, 239, 127], [39, 169, 126, 58, 227, 211, 77, 207, 137, 223, 146, 104, 107, 111, 121, 25, 96, 247, 152, 211, 195, 158, 153, 100, 36, 199, 115, 140, 194, 65, 70, 226, 124, 81, 128, 218], [151, 123, 102, 86, 48, 163, 42, 217, 112, 123, 185, 53, 133, 155, 168, 3, 43, 241, 101, 150, 200, 181, 69, 32, 174, 103, 248, 178, 26, 218, 21, 224, 237, 105, 149, 35], [55, 152, 44, 16, 61, 174, 153, 224, 68, 166, 173, 124, 113, 16, 87, 59, 215, 223, 176, 197, 232, 119, 59, 53, 187, 6, 231, 228, 111, 238, 231, 188, 135, 224, 81, 144], [186, 27, 144, 41, 219, 168, 241, 246, 84, 27, 34, 21, 183, 82, 75, 17, 236, 242, 17, 199, 240, 111, 239, 76, 20, 136, 75, 233, 144, 212, 123, 211, 251, 77, 14, 37], [230, 55, 193, 122, 69, 195, 204, 146, 12, 45, 71, 28, 72, 94, 83, 190, 56, 30, 99, 97, 39, 77, 84, 112, 182, 34, 80, 155, 187, 71, 179, 61, 145, 28, 189, 2], [57, 117, 21, 33, 114, 151, 5, 219, 139, 231, 23, 108, 109, 117, 14, 0, 197, 97, 148, 8, 29, 143, 220, 2, 209, 151, 205, 69, 130, 201, 174, 179, 39, 147, 8, 248], [90, 62, 11, 169, 64, 254, 149, 59, 219, 35, 20, 219, 239, 55, 54, 64, 214, 234, 169, 59, 15, 107, 101, 240, 7, 231, 118, 22, 173, 149, 142, 171, 201, 52, 157, 40], [118, 88, 70, 36, 131, 110, 109, 52, 18, 18, 121, 89, 4, 224, 221, 248, 112, 143, 213, 66, 74, 95, 22, 107, 253, 173, 2, 94, 253, 6, 242, 148, 191, 183, 207, 201], [25, 154, 16, 222, 239, 206, 127, 162, 212, 185, 100, 93, 97, 157, 227, 115, 186, 139, 151, 177, 139, 207, 159, 47, 232, 155, 241, 163, 126, 169, 127, 84, 33, 157, 113, 39], [92, 176, 180, 155, 132, 40, 99, 130, 208, 77, 214, 30, 107, 39, 42, 59, 182, 29, 162, 211, 79, 163, 205, 26, 182, 33, 201, 189, 69, 213, 253, 6, 190, 213, 42, 72]])
b = vector([492636, 406872, 481036, 456848, 489619, 458431, 466416, 482512, 413259, 424205, 476839, 441981, 489662, 490341, 510145, 428550, 480326, 499060, 417618, 486312, 452152, 470192, 476645, 496113, 433471, 403520, 503087, 467533, 508246, 477960, 367896, 408320, 423790, 439274, 525261, 462079])
flag = bytes(A.solve_right(b)).decode()
print(flag)
Running it, we get the flag: uiuctf{1_hope_that_tcls_y0ur_f4ncy!}