Hackvent 2019: Day 8

08 Dec 2019
CTF: Hackvent 2019
Link to challenge:
Date Completed: 8 December 2019



HV19.08 SmileNcryptor 4.0




We download the zip file and extract the dump.sql file within.
It contains the database schema for some credit cards as well as our flag.

First we look at the flag insert statement:

The prefix and postfix of the flag is stored explicitly in different fields. We then see our first smile ciphertext which looks like this:  :)SlQRUPXWVo\Vuv_n_\ajjce
We don’t know the plaintext that generated this ciphertext so there isn’t much information we can extract from this. We note that it appears as if all smiley ciphertexts begin with an ASCII smiley face :)

Next we look at the credit card insert statements:

Again we see some smile ciphertext which is in the cc_number field which indicates that the plaintext for these ciphertexts should be valid credit card numbers (why would invalid numbers be stored on a shopping website).

At this point we do some research on credit card number formats and standards. See Payment card number on Wikipedia.
In particular, we wanted to know how long various credit card numbers can be, if they have any hard coded values and how to check if a credit card number is valid.

We find out the following:

When inspecting the lengths of our credit card numbers with the smiley face :)  removed, we observe the following lengths:

Naturally, as these lengths match the required lengths of credit card numbers produced by various vendors, we can assume that a 1 to 1 mapping exists between the ciphertext and plaintext. This is a good indicator that an ASCII rotation/shift cipher should be used. We match each credit card vendor to the ciphertext based on its length. Ciphertext A is matched to American Express and B to Diners Club. For the 16 digit ciphertexts, we don’t know exactly which vendor to use. However, as ciphertext E begins with Q (just like and B) which means that logically Q maps to the digit  3. We take a guess here and assume that the other two 16 digit ciphertexts belong to Mastercard and Visa respectively. As R comes after Q in the alphabet we guess that R maps to the digit  4 and maps to the digit  5.

From this we guess we need to do an ASCII rotation which shifts Q (ASCII 81) to equal 3 (ASCII 51) which is a shift of -30 dec.
We write a script to perform this ASCII rotation decipher on our ciphertexts, throwing the final result in a credit card checksum validator function but the results are not valid credit card numbers.

Finally, we notice that our ciphertexts creep up the ASCII range as we move through the string character by character even though the number space for the credit cards is always 0-9 (digits). This implies the ASCII rotation is offset after each character is encoded or decoded. Ciphertext E (JCB) helped us the most here as we know it has to start with the digits  35. Based on our initial mapping, this suggested the offset simply grew by each character.
After adding this offset to our script, our computed plain texts validate as valid credit card numbers!

Python script:


Flag:  HV19{5M113-420H4-KK3A1-19801}


No Comments

Posted in Hackvent 2019


Leave a Reply