mirror of
https://github.com/cliffe/SecGen.git
synced 2026-02-20 13:50:45 +00:00
Merge pull request #134 from Th3Prim3/master
CTFd Export/Import Fix (fixes #129)
This commit is contained in:
10
README.md
10
README.md
@@ -40,8 +40,7 @@ Install all the required packages:
|
||||
wget https://releases.hashicorp.com/vagrant/1.9.8/vagrant_1.9.8_x86_64.deb
|
||||
sudo apt install ./vagrant_1.9.8_x86_64.deb
|
||||
# install other required packages via repos
|
||||
sudo apt-get install ruby-dev zlib1g-dev liblzma-dev build-essential patch virtualbox ruby-bundler imagemagick libmagickwand-dev exiftool libpq-dev libcurl4-openssl-dev libxml2-dev graphviz graphviz-dev
|
||||
```
|
||||
sudo apt-get install ruby-dev zlib1g-dev liblzma-dev build-essential patch virtualbox ruby-bundler imagemagick libmagickwand-dev exiftool libpq-dev libcurl4-openssl-dev libxml2-dev graphviz graphviz-dev python3.6 python3-passlib
|
||||
|
||||
Copy SecGen to a directory of your choosing, such as */home/user/bin/SecGen*
|
||||
|
||||
@@ -117,7 +116,7 @@ Scenarios can be found in the scenarios/ directory. For example, to spin up a VM
|
||||
```bash
|
||||
ruby secgen.rb --scenario scenarios/examples/remotely_exploitable_user_vulnerability.xml run
|
||||
```
|
||||

|
||||

|
||||
|
||||
#### VMs for a security audit of an organisation
|
||||
To generate a set of VMs for a randomly generated fictional organisation, with a desktop system, webserver, and intranet server:
|
||||
@@ -132,6 +131,11 @@ To generate a set of VMs for a CTF competition:
|
||||
ruby secgen.rb --scenario scenarios/ctf/flawed_fortress_1.xml run
|
||||
```
|
||||
Note that a 'CTFd_importable.zip' file is also generated, containing all the flags and hints, which you can import into the [CTFd scoreboard frontend](https://github.com/CTFd/CTFd).
|
||||
This is compatible with CTFd v2.0.2 and newer.
|
||||
|
||||
**Default admin account:**
|
||||
Username: adminusername
|
||||
Password: adminpassword
|
||||
|
||||
### Defining new scenarios
|
||||
Writing your own scenarios enables you to define a VM or set of VMs with a configuration as specific or general as desired.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'bcrypt'
|
||||
|
||||
# Convert systems objects into a format that can be imported into CTFd
|
||||
class CTFdGenerator
|
||||
|
||||
@@ -30,7 +28,7 @@ class CTFdGenerator
|
||||
|
||||
challenges = []
|
||||
hints = []
|
||||
keys = []
|
||||
flags = []
|
||||
|
||||
challenges << {
|
||||
"id"=> 1,
|
||||
@@ -40,12 +38,13 @@ class CTFdGenerator
|
||||
"value"=>FREE_POINTS,
|
||||
"category"=>"Freebie",
|
||||
"type"=>"standard",
|
||||
"hidden"=>0}
|
||||
keys << {
|
||||
"state"=>"visible",
|
||||
"requirements"=>"null"}
|
||||
flags << {
|
||||
"id"=>1,
|
||||
"chal"=>1,
|
||||
"challenge_id"=>1,
|
||||
"type"=>"static",
|
||||
"flag"=>"flag{FREEPOINTS}",
|
||||
"content"=>"flag{FREEPOINTS}",
|
||||
"data"=>nil}
|
||||
|
||||
@systems.each { |system|
|
||||
@@ -56,19 +55,20 @@ class CTFdGenerator
|
||||
challenge_id = challenges.length + 1
|
||||
challenges << {
|
||||
"id"=> challenge_id,
|
||||
"name"=>"",
|
||||
"name"=>"Challenge ##{challenge_id}",
|
||||
"description"=>"Remember, search for text in the format of flag{SOMETHING}, and submit it for points. If you are stuck a hint may help!",
|
||||
"max_attempts"=>0,
|
||||
"value"=>POINTS_PER_FLAG,
|
||||
"category"=>"#{system.name} VM (#{system.module_selections.first.attributes['platform'].first})",
|
||||
"type"=>"standard",
|
||||
"hidden"=>0}
|
||||
key_id = keys.length + 1
|
||||
keys << {
|
||||
"id"=>key_id,
|
||||
"chal"=>challenge_id,
|
||||
"state"=>"visible",
|
||||
"requirements"=>"null"}
|
||||
flag_id = flags.length + 1
|
||||
flags << {
|
||||
"id"=>flag_id,
|
||||
"challenge_id"=>challenge_id,
|
||||
"type"=>"static",
|
||||
"flag"=>output_value,
|
||||
"content"=>output_value,
|
||||
"data"=>nil}
|
||||
|
||||
collected_hints = []
|
||||
@@ -92,10 +92,11 @@ class CTFdGenerator
|
||||
end
|
||||
hints << {
|
||||
"id"=> hint_id,
|
||||
"type"=>0,
|
||||
"chal"=>challenge_id,
|
||||
"hint"=>collected_hint["hint_text"],
|
||||
"cost"=>cost
|
||||
"type"=>"standard",
|
||||
"challenge_id"=>challenge_id,
|
||||
"content"=>collected_hint["hint_text"],
|
||||
"cost"=>cost,
|
||||
"requirements"=>nil
|
||||
}
|
||||
}
|
||||
end
|
||||
@@ -104,26 +105,43 @@ class CTFdGenerator
|
||||
}
|
||||
|
||||
output_hash = {
|
||||
"alembic_version.json" => "",
|
||||
"alembic_version.json" => alembic_version_json(),
|
||||
"awards.json" => "",
|
||||
"challenges.json" => challenges_json(challenges),
|
||||
"config.json" => config_json(),
|
||||
"dynamic_challenge.json" => "",
|
||||
"files.json" => files_json(),
|
||||
"flags.json" => flags_json(flags),
|
||||
"hints.json" => hints_json(hints),
|
||||
"keys.json" => keys_json(keys),
|
||||
"notifications.json" => "",
|
||||
"pages.json" => pages_json(),
|
||||
"solves.json" => "",
|
||||
"submissions.json" => "",
|
||||
"tags.json" => "",
|
||||
"teams.json" => teams_json(),
|
||||
"teams.json" => "",
|
||||
"tracking.json" => "",
|
||||
"unlocks.json" => "",
|
||||
"wrong_keys.json" => "",
|
||||
"users.json" => users_json(),
|
||||
}
|
||||
|
||||
output_hash
|
||||
|
||||
end
|
||||
|
||||
|
||||
def alembic_version_json
|
||||
alembic_version_json_hash = {
|
||||
"count" => 1,
|
||||
"results" => [
|
||||
{
|
||||
"version_num"=>"8369118943a1"
|
||||
}
|
||||
],
|
||||
"meta"=>{}
|
||||
}
|
||||
|
||||
alembic_version_json_hash.to_json
|
||||
end
|
||||
|
||||
def files_json
|
||||
return ''
|
||||
end
|
||||
@@ -137,47 +155,47 @@ class CTFdGenerator
|
||||
|
||||
def config_json
|
||||
config_json_hash = {
|
||||
"count" => 31,
|
||||
"count" => 23,
|
||||
"results" => [
|
||||
{
|
||||
"id"=>1,
|
||||
"key"=>"next_update_check",
|
||||
"value"=>"1529096764"
|
||||
"key"=>"ctf_version",
|
||||
"value"=>"2.0.2"
|
||||
},
|
||||
{
|
||||
"id"=>2,
|
||||
"key"=>"ctf_version",
|
||||
"value"=>"1.2.0"
|
||||
},
|
||||
{
|
||||
"id"=>3,
|
||||
"key"=>"ctf_theme",
|
||||
"value"=>"core"
|
||||
},
|
||||
{
|
||||
"id"=>4,
|
||||
"id"=>3,
|
||||
"key"=>"ctf_name",
|
||||
"value"=>"SecGenCTF"
|
||||
},
|
||||
{
|
||||
"id"=>4,
|
||||
"key"=>"user_mode",
|
||||
"value"=>"users"
|
||||
},
|
||||
{
|
||||
"id"=>5,
|
||||
"key"=>"ctf_logo",
|
||||
"value"=>nil #"fca9b07e1f3699e07870b86061815b1c/logo.svg"
|
||||
"key"=>"challenge_visibility",
|
||||
"value"=>"private"
|
||||
},
|
||||
{
|
||||
"id"=>6,
|
||||
"key"=>"workshop_mode",
|
||||
"value"=>"0"
|
||||
"key"=>"score_visibility",
|
||||
"value"=>"public"
|
||||
},
|
||||
{
|
||||
"id"=>7,
|
||||
"key"=>"hide_scores",
|
||||
"value"=>"0"
|
||||
"key"=>"account_visibility",
|
||||
"value"=>"public"
|
||||
},
|
||||
{
|
||||
"id"=>8,
|
||||
"key"=>"prevent_registration",
|
||||
"value"=>"0"
|
||||
"key"=>"registration_visibility",
|
||||
"value"=>"public"
|
||||
},
|
||||
{
|
||||
"id"=>9,
|
||||
@@ -186,113 +204,73 @@ class CTFdGenerator
|
||||
},
|
||||
{
|
||||
"id"=>10,
|
||||
"key"=>"max_tries",
|
||||
"value"=>"0"
|
||||
},
|
||||
{
|
||||
"id"=>11,
|
||||
"key"=>"end",
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>12,
|
||||
"id"=>11,
|
||||
"key"=>"freeze",
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>13,
|
||||
"key"=>"view_challenges_unregistered",
|
||||
"value"=>"0"
|
||||
},
|
||||
{
|
||||
"id"=>14,
|
||||
"id"=>12,
|
||||
"key"=>"verify_emails",
|
||||
"value"=>"0"
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>15,
|
||||
"id"=>13,
|
||||
"key"=>"mail_server",
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>16,
|
||||
"id"=>14,
|
||||
"key"=>"mail_port",
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>17,
|
||||
"id"=>15,
|
||||
"key"=>"mail_tls",
|
||||
"value"=>"0"
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>18,
|
||||
"id"=>16,
|
||||
"key"=>"mail_ssl",
|
||||
"value"=>"0"
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>19,
|
||||
"id"=>17,
|
||||
"key"=>"mail_username",
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>20,
|
||||
"id"=>18,
|
||||
"key"=>"mail_password",
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>21,
|
||||
"id"=>19,
|
||||
"key"=>"mail_useauth",
|
||||
"value"=>"0"
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>20,
|
||||
"key"=>"setup",
|
||||
"value"=>"true"
|
||||
},
|
||||
{
|
||||
"id"=>21,
|
||||
"key"=>"paused",
|
||||
"value"=>"false"
|
||||
},
|
||||
{
|
||||
"id"=>22,
|
||||
"key"=>"setup",
|
||||
"value"=>"1"
|
||||
},
|
||||
{
|
||||
"id"=>23,
|
||||
"key"=>"css",
|
||||
"value"=>File.read(ROOT_DIR + '/lib/templates/CTFd/css.css')
|
||||
},
|
||||
{
|
||||
"id"=>24,
|
||||
"key"=>"view_scoreboard_if_authed",
|
||||
"value"=>"0"
|
||||
},
|
||||
{
|
||||
"id"=>25,
|
||||
"key"=>"prevent_name_change",
|
||||
"value"=>"1"
|
||||
},
|
||||
{
|
||||
"id"=>26,
|
||||
"key"=>"version_latest",
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>27,
|
||||
"key"=>"mailfrom_addr",
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>28,
|
||||
"key"=>"mg_api_key",
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>29,
|
||||
"key"=>"mg_base_url",
|
||||
"value"=>nil
|
||||
},
|
||||
{
|
||||
"id"=>30,
|
||||
"key"=>"view_after_ctf",
|
||||
"value"=>"1"
|
||||
},
|
||||
{
|
||||
"id"=>31,
|
||||
"key"=>"paused",
|
||||
"value"=>"0"
|
||||
"id"=>23,
|
||||
"key"=>"ctf_logo",
|
||||
"value"=>nil #"fca9b07e1f3699e07870b86061815b1c/logo.svg"
|
||||
}
|
||||
],
|
||||
"meta"=>{}
|
||||
@@ -308,9 +286,9 @@ class CTFdGenerator
|
||||
}.to_json
|
||||
end
|
||||
|
||||
def keys_json(keys)
|
||||
{"count"=>keys.length,
|
||||
"results"=>keys,
|
||||
def flags_json(flags)
|
||||
{"count"=>flags.length,
|
||||
"results"=>flags,
|
||||
"meta"=>{}
|
||||
}.to_json
|
||||
end
|
||||
@@ -321,19 +299,21 @@ class CTFdGenerator
|
||||
"results" => [
|
||||
{
|
||||
"id"=>1,
|
||||
"title"=>"Welcome",
|
||||
"route"=>"index",
|
||||
"html"=>File.read(ROOT_DIR + '/lib/templates/CTFd/index.html'),
|
||||
"auth_required"=>0,
|
||||
"draft"=>0,
|
||||
"title"=>"Welcome"
|
||||
"content"=>File.read(ROOT_DIR + '/lib/templates/CTFd/index.html'),
|
||||
"draft"=>false,
|
||||
"hidden"=>false,
|
||||
"auth_required"=>false
|
||||
},
|
||||
{
|
||||
"id"=>2,
|
||||
"title"=>"Flag Submission",
|
||||
"route"=>"submit",
|
||||
"html"=>File.read(ROOT_DIR + '/lib/templates/CTFd/submit.html'),
|
||||
"auth_required"=>0,
|
||||
"draft"=>0,
|
||||
"title"=>"Flag submission"
|
||||
"content"=>File.read(ROOT_DIR + '/lib/templates/CTFd/submit.html'),
|
||||
"draft"=>false,
|
||||
"hidden"=>false,
|
||||
"auth_required"=>true
|
||||
}
|
||||
],
|
||||
"meta"=>{}
|
||||
@@ -342,56 +322,41 @@ class CTFdGenerator
|
||||
pages_json_hash.to_json
|
||||
end
|
||||
|
||||
def teams_json
|
||||
|
||||
teams_json_hash = {
|
||||
"count" => 2,
|
||||
def users_json
|
||||
# Default admin username: adminusername
|
||||
# Default admin password: adminpassword
|
||||
# To use an alternate password, utilize the lib/output/sha256_password.py script.
|
||||
# This ensures compatibility with CTFd v2.0.2+
|
||||
|
||||
users_json_hash = {
|
||||
"count" => 1,
|
||||
"results" => [
|
||||
{
|
||||
"id"=>1,
|
||||
"oauth_id"=>nil,
|
||||
"name"=>"adminusername",
|
||||
"password"=>"$bcrypt-sha256$2b,12$Fh9KaueZuSEK5YzSdTbcI.$cbJCW5wGDNBX0/C/xDvMhnv8X3vqI92",
|
||||
"email"=>"admin@email.com",
|
||||
"password"=>password_hash_string("adminpassword"),
|
||||
"type"=>"admin",
|
||||
"secret"=>nil,
|
||||
"website"=>nil,
|
||||
"affiliation"=>nil,
|
||||
"country"=>nil,
|
||||
"bracket"=>nil,
|
||||
"banned"=>0,
|
||||
"verified"=>1,
|
||||
"admin"=>1,
|
||||
"joined"=>"2018-06-22T10:46:26"
|
||||
"hidden"=>true,
|
||||
"banned"=>false,
|
||||
"verified"=>true,
|
||||
"team_id"=>nil,
|
||||
"created"=>"2019-02-01T20:13:03.80374"
|
||||
},
|
||||
{
|
||||
"id"=>2,
|
||||
"name"=>"Me",
|
||||
"email"=>"email@email.com",
|
||||
"password"=>password_hash_string("mypassword"),
|
||||
"website"=>nil,
|
||||
"affiliation"=>nil,
|
||||
"country"=>nil,
|
||||
"bracket"=>nil,
|
||||
"banned"=>0,
|
||||
"verified"=>1,
|
||||
"admin"=>1,
|
||||
"joined"=>"2018-06-22T10:46:26"
|
||||
}
|
||||
],
|
||||
"meta"=>{}
|
||||
}
|
||||
|
||||
teams_json_hash.to_json
|
||||
users_json_hash.to_json
|
||||
|
||||
end
|
||||
|
||||
# fix difference between ruby and python bcrypt formats used by libraries
|
||||
# $bcrypt-sha256$variant,rounds$salt$checksum
|
||||
# python lib used by CTFd expects , between variant and rounds, the ruby lib puts a $ there...
|
||||
def password_hash_string(pass)
|
||||
hash_string = "$bcrypt-sha256" + BCrypt::Password.create(pass)
|
||||
hash_string[17]= ","
|
||||
hash_string
|
||||
end
|
||||
|
||||
def get_module_hints(search_module_for_hints, collected_hints, all_module_selections)
|
||||
|
||||
if search_module_for_hints.write_to_module_with_id != ""
|
||||
|
||||
20
lib/output/sha256_password.py
Executable file
20
lib/output/sha256_password.py
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Short script to encode password for use with CTFd v2.0.2+.
|
||||
Requires Python3 and the following libraries:
|
||||
|
||||
pip install passlib passlib[bcrypt]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from passlib.hash import bcrypt_sha256
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("password", help="Password to encode to bcrypt sha256 2b variant")
|
||||
|
||||
args = parser.parse_args()
|
||||
print("Generating password for use with CTFd v2.0.2+...\n")
|
||||
hashed = bcrypt_sha256.encrypt(args.password)
|
||||
print(hashed)
|
||||
@@ -3,8 +3,12 @@
|
||||
<br>
|
||||
<h1 class="text-center">Welcome to SecGenCTF</h1>
|
||||
<br>
|
||||
<br>
|
||||
<h4 class="text-center">
|
||||
Here you can <a href="submit">submit flags you discover</a>, and <a href="challenges">review the challenges, and (if available) purchase hints</a>.
|
||||
You may review the <a href=\"challenges">challenges</a> and (if available) purchase hints.
|
||||
</h4>
|
||||
<h4 class="text-center">
|
||||
Please use the <a href=\"submit">Flag Submission</a> page to submit your flags.
|
||||
</h4>
|
||||
<h4 class="text-center">
|
||||
Good luck!
|
||||
|
||||
@@ -12,24 +12,27 @@
|
||||
<div role="tabpanel" class="tab-pane fade show active" id="challenge">
|
||||
<div class="chal-tags text-center"></div>
|
||||
<p>This will submit your flag against a set of challenges (such as a VM). Navigate to <a href="challenges">Challenges</a> for hints for specific flags.</p>
|
||||
</br>
|
||||
<strong><p class="text-primary">Flags will be in the form of: flag{something}</p></strong>
|
||||
</br>
|
||||
<p class="text-danger">Note: The system has a limit of 10 flag submissions per minute. If you submit more, this screen will pause for 60 seconds and then continue. Please do not try submitting again during this time.</p>
|
||||
</div>
|
||||
<div class="row submit-row">
|
||||
<div class="col-md-12 form-group">
|
||||
<input class="form-control" type="text" name="answer" id="answer-input" placeholder="Flag" />
|
||||
</div>
|
||||
<div class="row submit-row">
|
||||
<div class="col-md-12 form-group">
|
||||
<input class="form-control" type="text" name="answer" id="answer-input" placeholder="Flag" />
|
||||
</div>
|
||||
<div class="col-md-12 form-group">
|
||||
<select id="challenge_set" class="form-control" style="height:60px;">
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-12 form-group key-submit">
|
||||
<button type="submit" onclick="submitflags()" id="submit-key" tabindex="5" class="btn btn-md btn-outline-secondary float-right">Submit</button>
|
||||
</div>
|
||||
<div class="col-md-12 form-group">
|
||||
<select id="challenge_set" class="form-control" style="height:60px;">
|
||||
</select>
|
||||
</div>
|
||||
<div class="row notification-row">
|
||||
<div class="col-md-12">
|
||||
<div id="result-notification" class="alert alert-dismissable text-center w-100" role="alert">
|
||||
<strong id="result-message"></strong>
|
||||
</div>
|
||||
<div class="col-md-12 form-group key-submit">
|
||||
<button type="submit" onclick="submitflags()" id="submit-key" tabindex="5" class="btn btn-md btn-outline-secondary float-right">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row notification-row">
|
||||
<div class="col-md-12">
|
||||
<div id="result-notification" class="alert alert-dismissable text-center w-100" role="alert">
|
||||
<strong id="result-message"></strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,89 +43,94 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// pause submitting, due to rate limits?
|
||||
var cont = true;
|
||||
// stop submitting
|
||||
var abort = false;
|
||||
|
||||
var challenge_list;
|
||||
|
||||
// retrieve the list of challenges and add VMs options to selection box on load
|
||||
function loadoptions() {
|
||||
var chal_list = $.get(window.location.origin + "/chals", {}, function (json) {
|
||||
// get the challenge list from the server, and store it for later
|
||||
challenge_list = json;
|
||||
// for each challenge, add the category to the selection box, if it's not been added already
|
||||
$.each(json['game'], function (i, item) {
|
||||
if (!$("#challenge_set option[value='" + item['category'] + "']").length) {
|
||||
$('#challenge_set').append($('<option>', {
|
||||
value: item['category'],
|
||||
text : item['category']
|
||||
}));
|
||||
}
|
||||
});
|
||||
$('#challenge_set').append($('<option>', {
|
||||
value: 'All',
|
||||
text : 'All'
|
||||
}));
|
||||
}, 'json');
|
||||
|
||||
}
|
||||
window.onload = loadoptions;
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// Spams the challenge submission with flags.
|
||||
// This is necessary since with SecGen flags it's not always known which # challenge they are completing.
|
||||
// Since this is all done client-side (all easily importable into CTFd), we have to pause when we hit rate limits.
|
||||
// This involves some timing between asynchronous threads.
|
||||
async function submitflags() {
|
||||
var answer = $('#answer-input').val();
|
||||
var nonce = csrf_nonce;
|
||||
var url = window.location.origin + "/chal/";
|
||||
|
||||
$( "#result-message" ).empty();
|
||||
|
||||
cont = true;
|
||||
abort = false;
|
||||
var process_count = 0;
|
||||
$.each(challenge_list['game'], async function (i, item) {
|
||||
var selected = $( "#challenge_set option:selected" ).text();
|
||||
if (selected == "All" || selected == item["category"]) {
|
||||
process_count++;
|
||||
await sleep(process_count * 500); // asynchronous spacing of queries
|
||||
var chal_id = item["id"];
|
||||
if (abort == true) {
|
||||
return;
|
||||
} else if (cont == false) {
|
||||
await sleep(60000);
|
||||
cont = true; // on waking this currently clobbers any subsequent pause
|
||||
}
|
||||
$( "#result-message" ).append('#' + chal_id + ': ');
|
||||
|
||||
var retval = $.post(url + chal_id, {
|
||||
key: answer,
|
||||
nonce: nonce
|
||||
}, function (json) {
|
||||
// success post
|
||||
$( "#result-message" ).append(json['message'] + '<br/>');
|
||||
if(json['status'] == 3) {
|
||||
$( "#result-message" ).append( 'Waiting 60 seconds...<br/>' );
|
||||
cont = false;
|
||||
// currently doesn't retry the challenges that trigger a wait
|
||||
} else if(json['status'] == 1) {
|
||||
$( "#result-message" ).append( '<p class="text-success">Success! (Stopping)</p>' );
|
||||
abort = true;
|
||||
// pause submitting, due to rate limits?
|
||||
var cont = true;
|
||||
// stop submitting
|
||||
var abort = false;
|
||||
var challenge_list;
|
||||
// retrieve the list of challenges and add VMs options to selection box on load
|
||||
function loadoptions() {
|
||||
var chal_list = $.get(window.location.origin + "/api/v1/challenges", {}, function (json) {
|
||||
// get the challenge list from the server, and store it for later
|
||||
challenge_list = json;
|
||||
// for each challenge, add the category to the selection box, if it's not been added already
|
||||
$.each(json.data, function (i, item) {
|
||||
if (!$("#challenge_set option[value='" + item.category + "']").length) {
|
||||
$('#challenge_set').append($('<option>', {
|
||||
value: item['category'],
|
||||
text : item['category']
|
||||
}));
|
||||
}
|
||||
}, 'json');
|
||||
|
||||
retval.fail(function() {
|
||||
alert( "error" );
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
$('#challenge_set').append($('<option>', {
|
||||
value: 'All',
|
||||
text : 'All'
|
||||
}));
|
||||
}, 'json');
|
||||
}
|
||||
window.onload = loadoptions;
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
// Spams the challenge submission with flags.
|
||||
// This is necessary since with SecGen flags it's not always known which # challenge they are completing.
|
||||
// Since this is all done client-side (all easily importable into CTFd), we have to pause when we hit rate limits.
|
||||
// This involves some timing between asynchronous threads.
|
||||
async function submitflags() {
|
||||
var answer = $('#answer-input').val();
|
||||
var nonce = csrf_nonce;
|
||||
var url = window.location.origin + "/api/v1/challenges/attempt";
|
||||
$( "#result-message" ).empty();
|
||||
cont = true;
|
||||
abort = false;
|
||||
var process_count = 0;
|
||||
var delayed = [];
|
||||
$.each(challenge_list.data, async function (i, item) {
|
||||
var selected = $( "#challenge_set option:selected" ).text();
|
||||
if (selected == "All" || selected == item["category"]) {
|
||||
process_count++;
|
||||
await sleep(process_count * 1000); // asynchronous spacing of queries
|
||||
var chal_id = item["id"];
|
||||
if (abort == true) {
|
||||
return;
|
||||
} else if (cont == false) {
|
||||
await sleep(60000);
|
||||
cont = true; // on waking this currently clobbers any subsequent pause
|
||||
}
|
||||
var result_message = $( '#result-message' );
|
||||
result_message.append( 'Challenge #' + chal_id + ' : ' );
|
||||
var retval = $.post(url, {
|
||||
challenge_id: chal_id,
|
||||
submission: answer,
|
||||
nonce: nonce
|
||||
}, function (json) {
|
||||
var result = json.data;
|
||||
if (result.status === "incorrect") { //Incorrect Key
|
||||
result_message.append( '<p class="text-danger">' + result.message + '</p></br>' );
|
||||
} else if (result.status === "correct") { //Challenge Solved
|
||||
result_message.append( '<p class="text-success">' + result.message + '</p></br>' );
|
||||
abort = true;
|
||||
} else if (result.status === "already_solved") { //Challenge Already Solved
|
||||
result_message.append( '<p class="text-warning">' + result.message + '</p></br>' );
|
||||
}
|
||||
}, 'json');
|
||||
retval.fail(function(xhr, status, text) {
|
||||
var result = xhr.responseJSON.data;
|
||||
if (xhr.status === 429) { //Keys per minute too high
|
||||
result_message.append( '<p class="text-danger">' + result.message + ' Pausing for 60 seconds. Do not hit Submit again.</p></br>' );
|
||||
delayed.push(chal_id);
|
||||
cont = false;
|
||||
} else if (xhr.status === 403) {
|
||||
if (result.status === "paused") { //CTF is paused
|
||||
result_message.append( '<p class="text-danger">' + result.message + ' (Aborting...)</p></br>' );
|
||||
abort = true;
|
||||
} else if (result.status === "authentication_required") { //User is logged out
|
||||
window.location = script_root + "/login?next=" + script_root + window.location.pathname + window.location.hash;
|
||||
return
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user