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 u64integers
- u64can 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}