Thanks ArgumentParser.
This commit is contained in:
parent
1433048512
commit
c33a124834
10 changed files with 141 additions and 61 deletions
|
@ -8,28 +8,31 @@ The directory *must* contain a `problem.json`; this information will be loaded i
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
{
|
{
|
||||||
"pid": "survey", // required
|
"pid": "survey", // required
|
||||||
"title": "Survey", // required
|
"title": "Survey", // required
|
||||||
"description": "Take our survey.", // required - can use HTML
|
"hint": "No hint!", // optional - defaults to ""
|
||||||
"hint": "No hint!", // optional - defaults to ""
|
"category": "Miscellaneous", // required
|
||||||
"category": "Miscellaneous", // required
|
"autogen": false, // optional - defaults to false
|
||||||
"autogen": false, // optional - defaults to false
|
"programming": false, // optional - defaults to false
|
||||||
"programming": false, // optional - defaults to false
|
"value": 20, // required - integer out of 800
|
||||||
"value": 20, // required - integer out of 800
|
"bonus": 0, // optional - defaults to 0; see below for details
|
||||||
"bonus": 0, // optional - defaults to 0; see below for details
|
"threshold": 0, // recommended - defaults to 0; see below for details
|
||||||
"threshold": 0, // recommended - defaults to 0; see below for details
|
"weightmap": { } // recommended - defaults to {}
|
||||||
"weightmap": { } // recommended - defaults to {}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `description.md`
|
||||||
|
|
||||||
|
The directory *must* contain a `description.md`. Just write your description here in Markdown. If you're using `autogen: true`, you can include `${}` variables to produce dynamic content.
|
||||||
|
|
||||||
## `grader.py`
|
## `grader.py`
|
||||||
|
|
||||||
The directory must *also* contain a `grader.py`; this script must contain a `grade` function that takes in two parameters: `tid`, the team ID and `answer`, their attempted answer. The function must return a dict containing the following keys:
|
The directory must *also* contain a `grader.py`; this script must contain a `grade` function that takes in two parameters: `tid`, the team ID and `answer`, their attempted answer. The function must return a dict containing the following keys:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
"correct": True # or False, indicating whether the answer is correct.
|
"correct": True # or False, indicating whether the answer is correct.
|
||||||
"message": "Congratulations!" # a custom message for feedback on the website.
|
"message": "Congratulations!" # a custom message for feedback on the website.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -37,9 +40,9 @@ For the most part, checking in flags will simply be a matter of comparing string
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def grade(tid, answer):
|
def grade(tid, answer):
|
||||||
if answer.find("csrf_protection_would_probably_have_been_a_good_idea_:/") != -1:
|
if answer.find("csrf_protection_would_probably_have_been_a_good_idea_:/") != -1:
|
||||||
return { "correct": True, "message": "Indeed." }
|
return { "correct": True, "message": "Indeed." }
|
||||||
return { "correct": False, "message": "Nope, that's not quite right." }
|
return { "correct": False, "message": "Nope, that's not quite right." }
|
||||||
```
|
```
|
||||||
|
|
||||||
You can copy-paste the above example into your grader and make any necessary adjustments. If you were wondering why we don't just compare strings, some problems may warrant a more complicated grader, like the problem H4SH3D from last year:
|
You can copy-paste the above example into your grader and make any necessary adjustments. If you were wondering why we don't just compare strings, some problems may warrant a more complicated grader, like the problem H4SH3D from last year:
|
||||||
|
@ -48,31 +51,31 @@ You can copy-paste the above example into your grader and make any necessary adj
|
||||||
import binascii
|
import binascii
|
||||||
|
|
||||||
def compute(uinput):
|
def compute(uinput):
|
||||||
if len(uinput) > 32: return ""
|
if len(uinput) > 32: return ""
|
||||||
blen = 32
|
blen = 32
|
||||||
n = blen - len(uinput) % blen
|
n = blen - len(uinput) % blen
|
||||||
if n == 0:
|
if n == 0:
|
||||||
n = blen
|
n = blen
|
||||||
pad = chr(n)
|
pad = chr(n)
|
||||||
ninput = uinput + pad * n
|
ninput = uinput + pad * n
|
||||||
r = ""
|
r = ""
|
||||||
for i in range(0, blen, 4):
|
for i in range(0, blen, 4):
|
||||||
s = ninput[i:i+4]
|
s = ninput[i:i+4]
|
||||||
h = 0
|
h = 0
|
||||||
for j in range(len(s)):
|
for j in range(len(s)):
|
||||||
h = (h << 4) + ord(s[j])
|
h = (h << 4) + ord(s[j])
|
||||||
g = h & 4026531840
|
g = h & 4026531840
|
||||||
if not(g == 0):
|
if not(g == 0):
|
||||||
h ^= g >> 24
|
h ^= g >> 24
|
||||||
h &= ~g
|
h &= ~g
|
||||||
r += chr(h % 256)
|
r += chr(h % 256)
|
||||||
h = binascii.hexlify(bytes(r, 'Latin-1'))
|
h = binascii.hexlify(bytes(r, 'Latin-1'))
|
||||||
return h
|
return h
|
||||||
|
|
||||||
def grade(tid, answer):
|
def grade(tid, answer):
|
||||||
if compute(answer) == compute("they_see_me_hashin_they_hatin"):
|
if compute(answer) == compute("they_see_me_hashin_they_hatin"):
|
||||||
return { "correct": True, "message": "They see me hashin' they hatin'" }
|
return { "correct": True, "message": "They see me hashin' they hatin'" }
|
||||||
return { "correct": False, "message": "Nope." }
|
return { "correct": False, "message": "Nope." }
|
||||||
```
|
```
|
||||||
|
|
||||||
## `static`
|
## `static`
|
||||||
|
@ -98,7 +101,7 @@ Bonus points encourage teams to finish solving a problem first. Rather than an a
|
||||||
| 4 | 6% | 8% | 10% |
|
| 4 | 6% | 8% | 10% |
|
||||||
| 5 | 8% | 12% | 20% |
|
| 5 | 8% | 12% | 20% |
|
||||||
|
|
||||||
The table indicates how many percent bonus a team should receive if they solve a problem first, second, or third. Low problems such as the survey should not yield bonus points; only high-valued points should have bonus points in order to encourage teams t o solve them first.
|
The table indicates how many percent bonus a team should receive if they solve a problem first, second, or third. Low problems such as the survey should not yield bonus points; only high-valued points should have bonus points in order to encourage teams to solve them first.
|
||||||
|
|
||||||
## Problem Unlocking
|
## Problem Unlocking
|
||||||
|
|
||||||
|
@ -106,20 +109,20 @@ Problem unlocking is managed through a mechanism that involves a threshold and w
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
{
|
{
|
||||||
"pid": "launch-code",
|
"pid": "launch-code",
|
||||||
"threshold": 5,
|
"threshold": 5,
|
||||||
"weightmap": {
|
"weightmap": {
|
||||||
"php3": 1,
|
"php3": 1,
|
||||||
"faster-math": 1,
|
"faster-math": 1,
|
||||||
"biggerisbetter": 1,
|
"biggerisbetter": 1,
|
||||||
"cave-johnson": 1,
|
"cave-johnson": 1,
|
||||||
"blackmesa": 1,
|
"blackmesa": 1,
|
||||||
"rsa3": 1,
|
"rsa3": 1,
|
||||||
"yandere": 1,
|
"yandere": 1,
|
||||||
"rsi": 1,
|
"rsi": 1,
|
||||||
"adoughbee": 1,
|
"adoughbee": 1,
|
||||||
"infinity_star": 1
|
"infinity_star": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
1
problems/misc/survey/description.md
Normal file
1
problems/misc/survey/description.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Take our survey.
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
|
"pid": "survey",
|
||||||
"title": "Survey",
|
"title": "Survey",
|
||||||
"description": "Take our survey.",
|
|
||||||
"hint": "No hint!",
|
"hint": "No hint!",
|
||||||
"category": "Miscellaneous",
|
"category": "Miscellaneous",
|
||||||
"autogen": false,
|
"autogen": false,
|
||||||
|
|
1
problems/programming/cancer/description.md
Normal file
1
problems/programming/cancer/description.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Help! I started growing this array culture, but it outgrew its petri dish! Using standard array syntax (either JavaScript or Python), indicate which element of the array contains the value ${value}.
|
12
problems/programming/cancer/problem.json
Normal file
12
problems/programming/cancer/problem.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"pid": "cancer",
|
||||||
|
"title": "Cancer",
|
||||||
|
"hint": "No hint!",
|
||||||
|
"category": "Miscellaneous",
|
||||||
|
"autogen": false,
|
||||||
|
"programming": false,
|
||||||
|
"value": 20,
|
||||||
|
"bonus": 0,
|
||||||
|
"threshold": 0,
|
||||||
|
"weightmap": { }
|
||||||
|
}
|
|
@ -5,4 +5,5 @@ import problem
|
||||||
import user
|
import user
|
||||||
import stats
|
import stats
|
||||||
import team
|
import team
|
||||||
|
import tools
|
||||||
import utils
|
import utils
|
||||||
|
|
|
@ -130,3 +130,7 @@ def problem_data():
|
||||||
jason.append({"pid": problem[1], "name": problem[2] ,"category": problem[3], "description": problem[4], "hint": problem[5], "value": problem[6], "solves": problem[7], "files": problem_files})
|
jason.append({"pid": problem[1], "name": problem[2] ,"category": problem[3], "description": problem[4], "hint": problem[5], "value": problem[6], "solves": problem[7], "files": problem_files})
|
||||||
|
|
||||||
return jsonify(data=jason)
|
return jsonify(data=jason)
|
||||||
|
|
||||||
|
def insert_problem(data):
|
||||||
|
print data
|
||||||
|
pass
|
0
server/api/tools.py
Normal file
0
server/api/tools.py
Normal file
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
|
||||||
|
@ -6,6 +8,7 @@ app = Flask(__name__)
|
||||||
import api
|
import api
|
||||||
import config
|
import config
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from api.decorators import api_wrapper
|
from api.decorators import api_wrapper
|
||||||
|
@ -34,12 +37,65 @@ api.logger.initialize_logs()
|
||||||
def api_main():
|
def api_main():
|
||||||
return { "success": 1, "message": "The API is online." }
|
return { "success": 1, "message": "The API is online." }
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def run(args):
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
parser = ArgumentParser(description="EasyCTF Server Configuration")
|
|
||||||
parser.add_argument("-d", "--debug", action="store_true", help="Run the server in debug mode.", default=False)
|
|
||||||
args = parser.parse_args()
|
|
||||||
keyword_args, _ = dict(args._get_kwargs()), args._get_args()
|
|
||||||
|
|
||||||
app.debug = keyword_args["debug"]
|
app.debug = keyword_args["debug"]
|
||||||
app.run(host="0.0.0.0", port=8000)
|
app.run(host="0.0.0.0", port=8000)
|
||||||
|
|
||||||
|
def load_problems(args):
|
||||||
|
if not os.path.exists(config.PROBLEM_DIR):
|
||||||
|
logging.critical("Problems directory doesn't exist.")
|
||||||
|
return
|
||||||
|
|
||||||
|
for (dirpath, dirnames, filenames) in os.walk(config.PROBLEM_DIR):
|
||||||
|
if "problem.json" in filenames:
|
||||||
|
json_file = os.path.join(dirpath, "problem.json")
|
||||||
|
contents = open(json_file).read()
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.loads(contents)
|
||||||
|
except ValueError as e:
|
||||||
|
logging.warning("Invalid JSON format in file {filename} ({exception})".format(filename=json_file, exception=e))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
logging.warning("{filename} is not a dict.".format(filename=json_file))
|
||||||
|
continue
|
||||||
|
|
||||||
|
missing_keys = []
|
||||||
|
for key in ["pid", "title", "category", "value"]:
|
||||||
|
if key not in data:
|
||||||
|
missing_keys.append(key)
|
||||||
|
if len(missing_keys) > 0:
|
||||||
|
logging.warning("{filename} is missing the following keys: {keys}".format(filename=json_file, keys=", ".join(missing_keys)))
|
||||||
|
continue
|
||||||
|
|
||||||
|
relative_path = os.path.relpath(dirpath, config.PROBLEM_DIR)
|
||||||
|
logging.info("Found problem '{}'".format(data["title"]))
|
||||||
|
|
||||||
|
try:
|
||||||
|
api.problem.insert_problem(data)
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning("Problem '{}' was not added to the database. Error: {}".format(data["title"], e))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = ArgumentParser(description="EasyCTF Server Management")
|
||||||
|
|
||||||
|
subparser = parser.add_subparsers(help="Select one of the following actions.")
|
||||||
|
parser_problems = subparser.add_parser("problems", help="Manage problems.")
|
||||||
|
subparser_problems = parser_problems.add_subparsers(help="Select one of the following actions.")
|
||||||
|
parser_problems_load = subparser_problems.add_parser("load", help="Load all problems into database.")
|
||||||
|
parser_problems_load.set_defaults(func=load_problems)
|
||||||
|
|
||||||
|
parser_run = subparser.add_parser("run", help="Run the server.")
|
||||||
|
parser_run.add_argument("-d", "--debug", action="store_true", help="Run the server in debug mode.", default=False)
|
||||||
|
parser_run.set_defaults(func=run)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
keyword_args, _ = dict(args._get_kwargs()), args._get_args()
|
||||||
|
logging.getLogger().setLevel(logging.INFO)
|
||||||
|
|
||||||
|
if "func" in args:
|
||||||
|
args.func(args)
|
||||||
|
else:
|
||||||
|
parser.print_help()
|
|
@ -23,3 +23,5 @@ CTF_END = 0 # To be used later
|
||||||
MG_HOST = ""
|
MG_HOST = ""
|
||||||
MG_API_KEY = ""
|
MG_API_KEY = ""
|
||||||
ADMIN_EMAIL = ""
|
ADMIN_EMAIL = ""
|
||||||
|
|
||||||
|
PROBLEM_DIR = "../problems"
|
Loading…
Reference in a new issue