diff --git a/lib/objects/local_script_challenge_generator.rb b/lib/objects/local_script_challenge_generator.rb
new file mode 100644
index 000000000..692116b7e
--- /dev/null
+++ b/lib/objects/local_script_challenge_generator.rb
@@ -0,0 +1,130 @@
+require 'getoptlong'
+require_relative '../helpers/constants'
+require 'base64'
+
+# Inherited by local script challenge generators
+# stdout used to return value
+# use Print.local to print status messages (formatted to stdout)
+
+# A nice side-effect is that each of these modules is also an executable script
+
+class ScriptChallengeGenerator
+ require_relative '../helpers/print.rb'
+
+ attr_accessor :module_name
+ attr_accessor :has_base64_inputs
+ attr_accessor :outputs
+
+ # override this
+ def initialize
+ # default values
+ self.module_name = 'Null generator'
+ self.has_base64_inputs = false
+ self.outputs = []
+ end
+
+ # override this
+ def generate
+ self.outputs << shebang_line + script_content
+ end
+
+ def read_arguments
+ # Get command line arguments
+ opts = get_options
+
+ # process option arguments
+ opts.each do |opt, arg|
+ # Check if base64 decoding is required and set instance variable
+ if opt == '--b64'
+ self.has_base64_inputs = true
+ end
+ # Decode if required
+ argument = self.has_base64_inputs ? Base64.strict_decode64(arg) : arg
+ process_options(opt, argument)
+ end
+ end
+
+ # Override this when using read_fact's in your module
+ def get_options
+ GetoptLong.new(*get_options_array)
+ end
+
+ def get_options_array
+ [['--help', '-h', GetoptLong::NO_ARGUMENT],
+ ['--b64', GetoptLong::OPTIONAL_ARGUMENT]]
+ end
+
+ # Override this when using read_fact's in your module. Always call super first
+ def process_options(opt, arg)
+ unless option_is_valid(opt)
+ Print.err "Argument not valid: #{arg}"
+ usage
+ exit
+ end
+
+ case opt
+ when '--help'
+ usage
+ when '--b64'
+ # do nothing
+ end
+ end
+
+ def usage
+ Print.err "Usage:
+ #{$0} [--options]
+"
+ exit
+ end
+
+ def run
+ Print.local module_name
+
+ read_arguments
+
+ Print.local_verbose "Generating..."
+ generate
+
+ # print the first 1000 chars to screen
+ output = self.outputs.to_s
+ length = output.length
+ if length < 1000
+ Print.local_verbose "Generated: #{output}..."
+ else
+ Print.local_verbose "Generated: #{output.to_s[0..1000]}..."
+ Print.local_verbose "(Displaying 1000/#{length} length output)"
+ end
+
+ puts has_base64_inputs ? base64_encode_outputs : self.outputs
+ end
+
+ def base64_encode_outputs
+ self.outputs.map { |o| Base64.strict_encode64 o }
+ end
+
+ def option_is_valid(opt_to_check)
+ arg_validity = false
+ valid_arguments = get_options_array
+ valid_arguments.each{ |valid_arg_array|
+ valid_arg_array.each_with_index { |valid_arg|
+ if valid_arg == opt_to_check
+ arg_validity = true
+ end
+ }
+ }
+ arg_validity
+ end
+
+ def script_content
+ # override me with script content
+ end
+
+ def interpreter_path
+ # override me with a string containing the interpreter path e.g. "/bin/bash"
+ end
+
+ def shebang_line
+ "#!/usr/local/bin/suid #{interpreter_path} --\n"
+ end
+end
+
diff --git a/modules/generators/challenges/misc/programming/python_example/manifests/.no_puppet b/modules/generators/challenges/misc/programming/python_example/manifests/.no_puppet
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/generators/challenges/misc/programming/python_example/python_example.pp b/modules/generators/challenges/misc/programming/python_example/python_example.pp
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/generators/challenges/misc/programming/python_example/secgen_local/local.rb b/modules/generators/challenges/misc/programming/python_example/secgen_local/local.rb
new file mode 100644
index 000000000..c63a7ae0e
--- /dev/null
+++ b/modules/generators/challenges/misc/programming/python_example/secgen_local/local.rb
@@ -0,0 +1,24 @@
+#!/usr/bin/ruby
+
+require_relative '../../../../../../../lib/objects/local_script_challenge_generator.rb'
+class RubyExampleScriptGenerator < ScriptChallengeGenerator
+
+ def initialize
+ super
+ self.module_name = 'Python Example Script Generator'
+ end
+
+
+ def interpreter_path
+ '/usr/bin/python'
+ end
+
+ def script_content
+"from sys import argv
+with open('flag') as f:
+ print f.read()"
+ end
+
+end
+
+RubyExampleScriptGenerator.new.run
\ No newline at end of file
diff --git a/modules/generators/challenges/misc/programming/python_example/secgen_metadata.xml b/modules/generators/challenges/misc/programming/python_example/secgen_metadata.xml
new file mode 100644
index 000000000..7b143d8fd
--- /dev/null
+++ b/modules/generators/challenges/misc/programming/python_example/secgen_metadata.xml
@@ -0,0 +1,18 @@
+
+
+
+ Ruby Example Challenge Generator
+ Thomas Shaw
+ MIT
+ TODO
+
+ python2_script_challenge
+
+ linux
+ windows
+
+ script
+
+
\ No newline at end of file
diff --git a/modules/generators/challenges/misc/programming/ruby_example/secgen_local/local.rb b/modules/generators/challenges/misc/programming/ruby_example/secgen_local/local.rb
index 2d7435e89..5dba077ea 100644
--- a/modules/generators/challenges/misc/programming/ruby_example/secgen_local/local.rb
+++ b/modules/generators/challenges/misc/programming/ruby_example/secgen_local/local.rb
@@ -1,7 +1,7 @@
#!/usr/bin/ruby
-require_relative '../../../../../../../lib/objects/local_string_generator.rb'
-class RubyExampleScriptGenerator < StringGenerator
+require_relative '../../../../../../../lib/objects/local_script_challenge_generator.rb'
+class RubyExampleScriptGenerator < ScriptChallengeGenerator
def initialize
super
@@ -9,9 +9,12 @@ class RubyExampleScriptGenerator < StringGenerator
end
- def generate
- self.outputs << "#!/usr/local/bin/suid /usr/bin/ruby --
- puts File.read('flag')"
+ def interpreter_path
+ '/usr/bin/ruby'
+ end
+
+ def script_content
+ "puts File.read('flag')"
end
end
diff --git a/modules/vulnerabilities/unix/ctf/python2_challenge_example/manifests/install.pp b/modules/vulnerabilities/unix/ctf/python2_challenge_example/manifests/install.pp
new file mode 100644
index 000000000..747a9f5a8
--- /dev/null
+++ b/modules/vulnerabilities/unix/ctf/python2_challenge_example/manifests/install.pp
@@ -0,0 +1,45 @@
+class python2_challenge_example::install {
+ $secgen_params = secgen_functions::get_parameters($::base64_inputs_file)
+ $group = $secgen_params['group']
+ $script_data = $secgen_params['script_data']
+
+ if $secgen_params['account'][0] and $secgen_params['account'][0] != '' {
+ $account = parsejson($secgen_params['account'][0])
+ } else {
+ $account = undef
+ }
+
+ if $secgen_params['storage_directory'] and $secgen_params['storage_directory'][0] {
+ $storage_dir = $secgen_params['storage_directory'][0]
+ } else {
+ $storage_dir = undef
+ }
+
+ if $group {
+ ::secgen_functions::install_setgid_script { 'python2_challenge_example':
+ source_module_name => $module_name,
+ challenge_name => $secgen_params['challenge_name'][0],
+ script_name => 'test.py',
+ script_data => $script_data[0],
+ group => $group[0],
+ account => $account,
+ flag => $secgen_params['flag'][0],
+ flag_name => 'flag',
+ storage_dir => $storage_dir,
+ strings_to_leak => $secgen_params['strings_to_leak'],
+ }
+ } else {
+ ::secgen_functions::install_setuid_root_script { 'python2_challenge_example':
+ source_module_name => $module_name,
+ challenge_name => $secgen_params['challenge_name'][0],
+ script_name => 'test.py',
+ script_data => $script_data[0],
+ account => $account,
+ flag => $secgen_params['flag'][0],
+ flag_name => 'flag',
+ storage_dir => $storage_dir,
+ strings_to_leak => $secgen_params['strings_to_leak'],
+ }
+ }
+
+}
diff --git a/modules/vulnerabilities/unix/ctf/python2_challenge_example/python2_challenge_example.pp b/modules/vulnerabilities/unix/ctf/python2_challenge_example/python2_challenge_example.pp
new file mode 100644
index 000000000..99d16c5df
--- /dev/null
+++ b/modules/vulnerabilities/unix/ctf/python2_challenge_example/python2_challenge_example.pp
@@ -0,0 +1 @@
+include python2_challenge_example::install
diff --git a/modules/vulnerabilities/unix/ctf/python2_challenge_example/secgen_metadata.xml b/modules/vulnerabilities/unix/ctf/python2_challenge_example/secgen_metadata.xml
new file mode 100644
index 000000000..3fc6c29f8
--- /dev/null
+++ b/modules/vulnerabilities/unix/ctf/python2_challenge_example/secgen_metadata.xml
@@ -0,0 +1,61 @@
+
+
+
+ python2 Challenge Example
+ Thomas Shaw
+ MIT
+ python2 challenge example
+
+ ctf_challenge
+ script_challenge
+ none
+ local
+ linux
+
+
+ challenge_name
+ script_data
+ account
+ flag
+
+ storage_directory
+
+ group
+
+ python2_script_example
+
+
+
+
+
+
+
+ challenges
+
+
+ password
+
+
+
+
+
+
+
+ python2_script_example
+
+
+
+ utilities/unix/system/accounts
+
+
+
+ utilities/unix/system/binary_script_container
+
+
+
+ utilities/unix/languages/python
+
+
+