Let's take a look at the source!
use std::fs::File;
use std::io::{self, BufRead, Write};
use std::path::Path;
fn supasecurefibberdachicheckerthing(n: usize) -> Vec<u64> {
let mut fib: Vec<u64> = vec![0, 1];
for i in 2..n {
let next = fib[i - 1].checked_add(fib[i - 2]).unwrap_or(0);
fib.push(next);
}
fib
}
fn validate_license_key(key: &str) -> bool {
if !key.starts_with("XMAS") {
return false;
}
if key.len() != 12 {
return false;
}
let ascii_sum: u32 = key.chars().skip(4).take(5).map(|c| c as u32).sum();
if ascii_sum != 610 {
return false;
}
let fib_482 = supasecurefibberdachicheckerthing(483)[482];
let fib_last_3 = fib_482 % 1000;
let key_last_3: u16 = match key[9..12].parse() {
Ok(num) => num,
Err(_) => return false,
};
if key_last_3 != fib_last_3 as u16 {
return false;
}
true
}
fn win() {
let flag_path = Path::new("flag.txt");
if let Ok(file) = File::open(flag_path) {
let mut buf_reader = io::BufReader::new(file);
let mut flag = String::new();
if buf_reader.read_line(&mut flag).is_ok() {
println!("🎄 Ho Ho Ho!, go watch some ELFTV!: {}", flag.trim());
} else {
println!("smth went wrong contact vip3r with error (flag-file-1)");
}
} else {
println!("smth went wrong contact vip3r with error (flag-file-2)");
}
}
fn main() {
println!("🎄 Welcome to the ElfTV XMAS-license key checker!");
println!("Please enter your license key:");
let stdin = io::stdin();
let mut input = String::new();
let mut stdout = io::stdout();
if stdin.read_line(&mut input).is_ok() {
let key = input.trim();
if validate_license_key(key) {
win();
} else {
writeln!(stdout, "Ho ho ho! Try again.").unwrap();
}
} else {
writeln!(stdout, "Failed to read the input!").unwrap();
}
}
So the goal of this challenge is to piece together a license key to get the flag. Let's start by analyzing each function used to check for the license key.
fn validate_license_key(key: &str) -> bool {
if !key.starts_with("XMAS") {
return false;
}
if key.len() != 12 {
return false;
}
let ascii_sum: u32 = key.chars().skip(4).take(5).map(|c| c as u32).sum();
if ascii_sum != 610 {
return false;
}
let fib_482 = supasecurefibberdachicheckerthing(483)[482];
let fib_last_3 = fib_482 % 1000;
let key_last_3: u16 = match key[9..12].parse() {
Ok(num) => num,
Err(_) => return false,
};
if key_last_3 != fib_last_3 as u16 {
return false;
}
true
}
This function in the source is what checks if the license key is correct or not.
if !key.starts_with("XMAS") {
return false;
}
This first check is pretty straight forward, it checks if the string starts with XMAS
if key.len() != 12 {
return false;
}
The next check above is looking at if the key is 12 characters, so now we have general idea on how the license key should look like. XMAS********
let ascii_sum: u32 = key.chars().skip(4).take(5).map(|c| c as u32).sum();
if ascii_sum != 610 {
return
Now it is starting to get tricky to understand, lets go over it!
key.chars()
: Turns the string into a iterator of is characters.skip(4)
: Skips the first 4 chars.take(5)
: Takes the next 5 characters after skipping the first 4..map(|c| c as u32)
: Maps each character in this slice to its ASCII value (as a u32 integer)..sum()
: Takes the sum of these ASCII values.let ascii_sum: u32 = ...;
: Saves the sum in a variable ascii_sum.if ascii_sum != 610 {
: Checks if the sum of these ASCII values is not equal to 610.
So at this point we need to find 5 characters that add up to 610 which is nice since it is even. So I wrote a Python one liner print(chr(int(610 / 5)) * 5)
which outputs zzzzz
. So our license key should look like XMASzzzzz***
so far.
let fib_482 = supasecurefibberdachicheckerthing(483)[482];
let fib_last_3 = fib_482 % 1000;
let key_last_3: u16 = match key[9..12].parse() {
Ok(num) => num,
Err(_) => return false,
};
if key_last_3 != fib_last_3 as u16 {
return false;
}
Now the final check had me confused, I was looking very surface level and thought the key was just the last 3 digits of the Fibonacci sequence for the 482 number (which is 041) but it didn't work. As I look in deeper, it seems as the codes implementation of the Fibonacci check is faulty because of this
- The Function is computing the digits using
u64
integers u64
can hold up to 2^64−1≈1.84×10^19- And by the 94th number is overflows that limit changing f'ing up the rest of the numbers.
So to get the correct number, I modified the code to just print out the last 3 digits.
fn main() {
fn supasecurefibberdachicheckerthing(n: usize) -> Vec<u64> {
let mut fib: Vec<u64> = vec![0, 1];
for i in 2..n {
let next = fib[i - 1].checked_add(fib[i - 2]).unwrap_or(0);
fib.push(next);
}
fib
}
let fib_482 = supasecurefibberdachicheckerthing(483)[482];
let fib_last_3 = fib_482 % 1000;
println!("last 3 digits: {}", fib_last_3);
}
And running it...
$ rustc test.rs
$ ./test
last 3 digits: 738
So now we have our full license key: XMASzzzzz738
. Lets try it!
$ nc ctf.csd.lol 1001
🎄 Welcome to the ElfTV XMAS-license key checker!
Please enter your license key:
XMASzzzzz738
🎄 Ho Ho Ho!, go watch some ELFTV!: csd{Ru57y_L1c3N53_k3Y_CH3Ck3r}
Flag: csd{Ru57y_L1c3N53_k3Y_CH3Ck3r}