2

This is my first post here so hopefully someone will be able to give me some help!

I'm currently working on a Capture The Flag (CTF) for a university project, as a part of it I want to involve a Python quiz; to validate whether or not the users answers are correct.

Although I've made a rather simple script that gets the job done, one potential problem that comes to mind is that the user can simply open the Python script in gedit or nano and easily find the answer. I've tried a few solutions, but I can't think of any way that would be reliable.

As I said, the code itself is rather simple, but I thought I'd include it just so you can get an idea of what I've got so far:

import sys

answer = ("Blessed be our saviour, a warrior...")
userInput = input("What does the decrypted text read? ")

if userInput == answer:
    print("Correct, the way is open")
else:
    print("Incorrect! The way is barred until you find the solution!")
    sys.exit(0)

I don't really know if this is possible, but I thought that I'd may as well ask.

Thanks for any advice you can give!

gehbiszumeis
  • 3,525
  • 4
  • 24
  • 41
  • You could encrypt the string the user provides, and then compare that to the encrypted answer string. – CDJB Dec 12 '19 at 16:10
  • You could either encode the answers with some simple encryption, like f.i. the Vigenere cipher string encoding, or store it on disk in some data format. – JE_Muc Dec 12 '19 at 16:10
  • 2
    Use a cryptographic hash algorithm if you want the plaintext answer not to be derivable from the data in the file. https://docs.python.org/3/library/hashlib.html However, if your users can open the file in an editor, then what's stopping them from just changing the condition which checks whether their answer is correct? – kaya3 Dec 12 '19 at 16:14
  • 1
    I don't think encryption is enough, because they can see the encryption method and then reverse it. You could store the answer as a hash instead (see e.g. [this site](https://emn178.github.io/online-tools/sha256.html) ) and simply store the answer hash, and compare the user input to the hashed value. Anyone who gets beyond that kind of thing deserves full marks and more. As for editing the script to make answers correct, you could always just distribute the `.pyc` files. While it's not exactly hard to reverse this, I image it's far more effort than people care for in this case – roganjosh Dec 12 '19 at 16:14
  • The only sure way to prevent reverse engineering is to NOT distribute the code (either in plain text or compiled form). IOW, make it a web app ;-) – bruno desthuilliers Dec 12 '19 at 17:15

3 Answers3

1

As mentioned above, if a user can open and see your script what's to stop them from changing your check to be if True or ... to bypass it?

That said:

Use an sha256 hash of the answer.

Then, hash what the user answers and see if it matches.

sha256 is a one-way hash and you cannot read the script and tell what was used to generate it.

Simple enccoder:

import hashlib

def encode(str):
  return hashlib.sha256(str).hexdigest()

print("Encoded string: %s" %(encode("Test")))

Output is: 532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25

So, your script:

import sys

answer = '532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25' # encode("Test") -> Change "Test" to whatever your answer is
userInput = input("What does the decrypted text read? ")
hashedUserInput = encode(userInput)

if hashedUserInput == answer:
    print("Correct, the way is open")
else:
    print("Incorrect! The way is barred until you find the solution!")
    sys.exit(0)

Use that value in your script.

mikeb
  • 10,578
  • 7
  • 62
  • 120
1

Either store it on disk in some binary data type containing all answers, or use some simple encryption technique, like f.i. Vigenere cipher.

Here is a short example of a function for encrypting with a Vigenere cipher:

def encode_string(key, string):
    """Encode a string using the Vigenere cipher."""
    if isinstance(key, (int, float)):
        key = str(key)
    if isinstance(string, (int, float)):
        string = str(string)
    encoded_chars = []
    for i in range(len(string)):
        key_c = key[i % len(key)]
        encoded_c = chr(ord(string[i]) + ord(key_c) % 256)
        encoded_chars.append(encoded_c)
    encoded_string = "".join(encoded_chars)
    return encoded_string

With this function you could encode your answer, let's say with:

encoded_answer = encode_string('this is some key', 'This is the answer')

With the encoded answer being encoded_answer='ÈÐÒæ@Òæ@ç×Ò\x85\x81ÙØðÙÚ'. You can hard code the encoded answer into your quiz.

To check for a correct answer, just encode the input with the same key and compare it to the hardcoded answer:

if encode_string('this is some key', userInput) == encoded_answer:
    print("Correct, the way is open")
else:
    print("Incorrect! The way is barred until you find the solution!")
    sys.exit(0)
JE_Muc
  • 5,403
  • 2
  • 26
  • 41
  • Why use a weak cryptographic scheme when you could just `import hashlib`? Note that your method requires including the secret key in the file provided to users, so all they need to do to decrypt the answer is change a `+` to a `-`. – kaya3 Dec 12 '19 at 16:18
  • Because it is really interesting to do some basic stuff by hand, especially if it is for learning purposes at university. Furthermore for things like this, weak encoding should be enough. :) And yeah, to decode swapping the `+` to a `-` is enough. But to know this, you already need to know some basics about cryptography. So for a "weak security case" like this, this should be ok and it furthermore is a funny/interesting way to learn basics about cryptography. There is imho no need to use the "big guns" for cases like this, especially if you have the possibility to learn something. – JE_Muc Dec 12 '19 at 16:20
  • I agree that it's a trivial case, but to balance things out. They lose not a single learning opportunity by swapping to an industry standard hash function (why do you think changing some letters around in the code detracts from anything?). What they do get by being introduced to MD5 is a missed opportunity to learn what the currently-correct hash method is and potentially a lasting impression of the wrong method if they come to such issues in many years' time – roganjosh Dec 12 '19 at 16:34
  • In my time at university we always started with a trivial case and then increased the difficulty in steps. We didn't go directly from zero to rocket-science. Thus the Vigenere cipher is imho a good introduction to cryptography, especially when looking at the history of cryptography. And for me knowing the basics never resulted in retaining a lasting impression of the wrong method. In my experience it instead always helped when going for the more in-depth topics. – JE_Muc Dec 12 '19 at 17:05
1

You can store the answer in a separate .yml file that the user does not have access to, for eg: data.yml

Your .yml file will look like:

answer: Blessed be our saviour, a warrior...

And your code will look like:

import sys
import yaml
data = yaml.load(open('data.yml'))

answer = data['answer']
userInput = input("What does the decrypted text read? ")

if userInput == answer:
    print("Correct, the way is open")
else:
    print("Incorrect! The way is barred until you find the solution!")
    sys.exit(0)

Then set your file to read-only mode, that way the user will not be able to edit and print the answer. You can do this by:

chmod 0444 filename.py

PS: Keep the .yml file in a path not accessible to the end user. If not, you can use hashlib to hide the path of the file by encoding it. If you're on unix, you can lock the data.yml file by doing chmod -rwx data.yml and the file cannot be opened by the end user.

Hope this helps!

Shayan
  • 1,931
  • 1
  • 9
  • 14
  • 1
    How exactly would that work? If the user is able to open the file then they presumably have access to any other file that the program uses. – roganjosh Dec 12 '19 at 16:37
  • This assumes a Unix system. The program would have to be run as a different user with privileges to read the YAML file, and the real user would need permission to run that program as the different user (possible with `sudo`). – kaya3 Dec 12 '19 at 16:41
  • You can either store the file on a drive, where it is authenticated. But that's the slightly more complex solution. It's easiest to do a ```chmod -rwx``` on the ```data.yml``` to prevent opening of the file. – Shayan Dec 12 '19 at 16:44
  • But the script has to be able to open the file, so you'd just modify the script to dump the contents into another file somewhere else or directly into the terminal. Of course, this is going above and beyond a simple introductory quiz, but it's interesting to follow the rabbit hole :) – roganjosh Dec 12 '19 at 16:46