No description
Find a file
2017-03-14 20:31:35 -05:00
20xx renamed file 2017-03-13 17:50:31 -05:00
a-maze-ing amazing 2017-03-14 12:44:26 -07:00
bizarro changed category of Bizarro to forensics 2017-03-09 23:24:48 -06:00
blogbox add blogbox 2017-03-14 19:44:28 +00:00
commentary renaming commentary folder 2017-03-13 18:44:08 -05:00
cookieblog Update grader.py 2017-03-13 21:26:12 -05:00
decode-me Update 2017-03-07 16:04:58 -06:00
decomphose add luckyguess 2017-02-15 04:26:11 +00:00
diffie-cult revert messages.txt 2017-03-13 14:33:35 -05:00
doubly-dangerous Update description.md 2017-03-13 17:52:00 -05:00
down-a-notch updated instructions 2017-02-11 17:25:24 -06:00
edge1 Name can't begin with a number. 2017-03-13 03:39:25 -05:00
edge2 Name can't begin with a number. 2017-03-13 03:39:25 -05:00
finn Add autogen parameter. 2016-10-31 16:45:42 -05:00
fizz-buzz-1 Implemented fizzbuzz graders, updated values 2017-03-13 18:10:04 -05:00
fizz-buzz-2 Implemented fizzbuzz graders, updated values 2017-03-13 18:10:04 -05:00
flag-collection Update description.md 2017-01-24 22:31:16 -06:00
flag-peg tried to make the point values for my problems more reasonable, probably could still be tuned but w/e 2017-03-10 00:17:08 -06:00
flip-my-letters spelling is hard 2017-03-11 01:59:12 -06:00
genius add genius 2017-02-12 11:05:47 +00:00
gibberish Add autogen parameter. 2016-10-31 16:45:42 -05:00
hash-on-hash typing everything is hard 2017-03-13 17:36:36 -05:00
hello-world Update description.md 2017-03-13 22:24:22 -05:00
hexable-autogen Added hexable hint. 2017-03-12 04:33:10 -05:00
injection1 make injection1 http 2017-03-13 17:53:09 -05:00
irc fuck ryan 2017-03-14 01:29:40 +00:00
let-me-be-frank added a vignere cipher 2017-03-13 17:06:26 -05:00
library library tl 2017-03-14 05:22:28 +00:00
library2 Create grader.py 2017-03-12 00:29:28 -06:00
listen-closely fixed a ? 2017-03-12 04:38:19 -05:00
lost-seeds Added lost-seeds. 2017-02-15 03:49:56 -06:00
luckyguess Update 2017-03-07 16:04:58 -06:00
mane-event add decomphose 2017-02-15 03:02:31 +00:00
match-me modifying problem.yml to try to force an update 2017-03-14 16:11:34 -05:00
my-usb usb.img 2016-10-28 19:54:46 -05:00
ogrewatch oops switched the graders 2016-11-22 19:04:36 -05:00
paillier-service typo 2017-03-14 05:17:07 +00:00
petty-difference changed pt value 2017-03-12 19:04:10 +00:00
phunkypython1 Update 2017-03-07 16:04:58 -06:00
phunkypython2 Update 2017-03-07 16:04:58 -06:00
qr-1 Added qr-1 2017-03-08 10:32:56 -06:00
qr-2 Renamed qr.bmp to qr2.bmp 2017-03-08 10:33:10 -06:00
qrt Update qrt. 2017-03-07 18:33:17 -06:00
r3ndom-67k Hint and description changes 2017-03-12 04:34:35 -05:00
risky-business Retrieve binaries from the shell server. 2017-03-13 00:26:28 -05:00
rsa1 renamed file to make sure regen happens 2017-03-14 01:20:41 -05:00
rsa2 renamed file to make sure regen happens 2017-03-14 01:20:41 -05:00
rsa3 fix rsa3 and kill rsa4 2017-03-11 06:47:41 +00:00
rsa4 Update description.md 2017-03-14 20:31:35 -05:00
scisnerof oops switched the graders 2016-11-22 19:04:36 -05:00
security-through-obscurity Update description.md 2017-03-14 09:07:16 -04:00
self-modifier Fixed an issue with the Morphin problem 2017-03-14 02:33:34 -05:00
serial Link to serial. 2017-03-12 04:50:34 -05:00
simple-rop Increased simple rop point value 2017-03-13 00:52:20 -05:00
things-add-up fix things add up 2017-03-14 05:15:04 +00:00
things-dont-add-up fix rsa3 and kill rsa4 2017-03-11 06:47:41 +00:00
tiny-eval Update tinyeval grader. 2017-03-14 17:23:55 -05:00
wayward-space-junk added ip for space junk 2017-03-13 18:44:49 -05:00
web-tunnel changed flag for web-tunnel 2017-03-14 20:53:19 +00:00
ziptunnel newline 2017-02-15 06:13:17 +00:00
zooooooom add zooooooom 2017-03-01 06:10:45 +00:00
.gitignore Ignore *.pyc 2017-03-05 23:58:29 -08:00
count.py Count total points. 2017-03-13 00:50:22 -05:00
README.md Add problem format. 2016-10-28 14:06:27 -05:00

OpenCTF Problem Import via Github

Each problem belongs in its own folder under the root folder of the repository. The name of the folder will become a readable identifier for the problem, so it should only contain letters, numbers, and -.

Inside this folder, there are 3 required files:

  • problem.yml: This is probably the most important file. It contains all the metadata for the problem, including:
    • title: The title of the problem.
    • category: The category of the problem.
    • value: An integer value for the problem.
    • hint: A hint for the problem. (no markdown supported)
    • autogen: (true/false) whether or not the problem is autogenerated. If you put true, read on to see what needs to be added to the grader.
  • description.md: A problem description. As the extension suggests, you may use markdown in this file.
  • grader.py: A python file containing several functions that relate to grading the problem (and generating, if autogen is enabled).

The Grader

At the most basic level, the grader should contain a grade function that takes in two parameters (you can call them whatever you like): (1) an instance of the random library used for autogenerating problems, and (2) the flag that the user actually typed into the input box. It should return a tuple (correct, message), where correct is a boolean value signifying whether the user got the problem right, and message, a custom message to be displayed.

An example of a basic grader looks like this:

def grade(random, key):
	if key.find("this_is_the_flag") != -1:
		return True, "Correct!"
	return False, "Nope."

If autogen is not enabled, please do not use random while judging the key. It's meant for checking problems that were autogenerated with the same seed. Also, note that I'm using key.find rather than comparing them with equals. This is useful if you want to match both flag{this_is_the_flag} and this_is_the_flag, or if you are trying to perform a case-insensitive match.

If autogen were enabled, you would need to add an additional function to the grader, called generate. Essentially, this function is required for generating the problem. It takes in a single parameter, the same instance of random as the grade function gets, and must return a dictionary of values (obviously, this dictionary can be blank, but ideally it shouldn't be).

Possible keys that you can include in your dictionary are:

  • variables: This should be another dictionary of variables that are inserted into the description during runtime. More details on how these variables are inserted comes in a later section. You would probably want to use the random variable that is passed to the function to generate a value that is different per team.
  • files: This is similar to variables in that it is a dictionary of keys that are inserted into the description. However, instead of returning variables directly, you should return a function that takes in the random variable and returns a File object (could be StringIO as well). Files generated this way are stored into the static container during runtime.

An example using both variables and files is:

from cStringIO import StringIO
def gen_b(random):
	b = random.choice(range(50, 100))
	return b
def generate(random):
	a = random.choice(range(50, 100))
	return dict(variables={
		"a": a
	}, files={
		"b.txt": gen_b
	})

Note that we are returning gen_b without (), since we want to return the function definition, rather than a value returned by the function. This way, the platform can call this function at runtime, generating a different problem for every user.

This part is very important. Filenames usually have extensions, like ciphertext.txt. In this case, we are passing the filename into the description as a string. But Python template strings don't allow symbols such as periods. Therefore, ALL FUNCTION NAMES ARE SANITIZED by replacing anything matching this regex: [^a-zA-Z]+ with _ (underscore). This way, ciphertext.txt becomes ciphertext_txt in the description. Here is a description that uses the above generator.

Help me decipher [this](${b_txt}).

Here is a full example using autogen:

problem.yml:

title: Caesar Cipher
value: 20
author: mzhang
autogen: true

description.md:

Help me decipher [this ciphertext](${ciphertext_txt}).

grader.py:

from cStringIO import StringIO
from string import maketrans

flag = "caesar_cipher_is_fun!"
alphabet = "abcdefghijklmnopqrstuvwxyz"

def get_problem(random):
	n = random.randint(1, 25)
	salt = "".join([random.choice("0123456789abcdef") for i in range(6)])
	return (n, salt)

def generate_ciphertext(random):
	n, salt = get_problem(random)
	trans = maketrans(alphabet, alphabet[n:] + alphabet[:n])
	ciphertext = ("easyctf{%s_%s}" % (flag, salt)).translate(trans)
	return StringIO(ciphertext)

def generate(random):
	return dict(files={
		"ciphertext.txt": generate_ciphertext
	})

def grade(random, key):
	n, salt = get_problem(random)
	if key.find("%s_%s" % (flag, salt)) >= 0:
		return True, "Correct!"
	return False, "Nope."

In this way, not only is the shift randomly generated, so is the actual flag itself. Note that I use a helper function, get_random to actually generate the numbers to ensure that the numbers generated are the same for both the generation and the grading.