├── flag_format.py ├── spl_example_runme.py ├── spl_example_template.py ├── team_list.py ├── README.md ├── start_posting.py └── start_sploit.py /flag_format.py: -------------------------------------------------------------------------------- 1 | # put the regexp for flag here 2 | FLAG_FORMAT = b"[0-9A-Fa-f]{32}" 3 | -------------------------------------------------------------------------------- /spl_example_runme.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import random 4 | import sys 5 | 6 | print("Hello! I am a little sploit. I could be written on any language, but " 7 | "my author loves Python. Look at my source - it is really simple. " 8 | "I should steal flags and print them on stdout or stderr. ") 9 | 10 | print("My args are: %s" % sys.argv) 11 | 12 | print("Here are some random flags for you:") 13 | 14 | print("First flag is %032d" % random.randrange(0, 10000)) 15 | print("Second flag is %032d" % random.randrange(0, 10000)) 16 | -------------------------------------------------------------------------------- /spl_example_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import socket 5 | import sys 6 | 7 | HOST = sys.argv[1] 8 | PORT = 12347 9 | TIMEOUT = 5 10 | 11 | # force line buffering for stdout. Useful for long-running sploits 12 | sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1) 13 | 14 | 15 | def readline(s): 16 | "Just an example how to read a line from a socket" 17 | chars = [] 18 | while True: 19 | a = s.recv(1) 20 | chars.append(a) 21 | if a == "\n" or a == "": 22 | return "".join(chars) 23 | 24 | 25 | s = socket.create_connection((HOST, PORT), TIMEOUT) 26 | 27 | s.sendall(b"gimmeflags\n") 28 | s.recv(1337) 29 | 30 | PAYLOAD = b"INSERT SHELLCODE HERE\n" 31 | s.sendall(PAYLOAD) 32 | 33 | print(s.recv(1337)) 34 | -------------------------------------------------------------------------------- /team_list.py: -------------------------------------------------------------------------------- 1 | # put the teamnames and ip addresses here 2 | TEAMS = { 3 | "team1" : "127.0.0.1", 4 | "team2" : "127.0.0.2", 5 | # "team3" : "127.0.0.3", 6 | # "team4" : "127.0.0.4", 7 | # "team5" : "127.0.0.5", 8 | # "team6" : "127.0.0.6", 9 | # "team7" : "127.0.0.7", 10 | # "team8" : "127.0.0.8", 11 | # "team9" : "127.0.0.9", 12 | # "team10" : "127.0.0.10", 13 | # "team11" : "127.0.0.11", 14 | # "team12" : "127.0.0.12", 15 | # "team13" : "127.0.0.13", 16 | # "team14" : "127.0.0.14", 17 | # "team15" : "127.0.0.15", 18 | # "team16" : "127.0.0.16", 19 | # "team17" : "127.0.0.17", 20 | # "team18" : "127.0.0.18", 21 | # "team19" : "127.0.0.19", 22 | # "team20" : "127.0.0.20", 23 | # "team21" : "127.0.0.21", 24 | # "team22" : "127.0.0.22", 25 | # "team23" : "127.0.0.23", 26 | # "team24" : "127.0.0.24", 27 | # "team25" : "127.0.0.25" 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exploit Farm # 2 | 3 | The utility for CTF hacker competition for launching sploits for all teams 4 | and submitting flags. 5 | 6 | ## Prepare ## 7 | 1. Set FLAG_FORMAT regexp in flag_format.py 8 | 2. Set TEAMS in team_list.py 9 | 3. Edit submit_flags in start_posting.py for the checking system 10 | 11 | ## Usage ## 12 | ### Starting a sploit ### 13 | ./start_sploit.py 14 | 15 | Launches sploit for all teams. 16 | Sploit should take team ip as first arg. Its output should contain flags. 17 | Flags will be saved in **flags/\\_\.txt** 18 | 19 | ### Starting the poster ### 20 | ./start_posting.py 21 | 22 | Posts the flags in flags/\*.txt to the checking system. 23 | Saves posted flags in **posted_good_flags.txt** and **posted_bad_flags.txt**. 24 | 25 | ## Optimizing a sploit ### 26 | 27 | If there are >100 teams, don't write a sploit like this: 28 | 29 | hack(ip) 30 | 31 | Write like this: 32 | 33 | while True: 34 | hack(ip) 35 | sys.stdout.flush() 36 | sleep(30) 37 | 38 | It is much faster. 39 | -------------------------------------------------------------------------------- /start_posting.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # author: Alexander Bersenev (Bay) from Hackerdom team 3 | # posts flags from flags/*.txt to submit server 4 | 5 | import re 6 | import socket 7 | import random 8 | 9 | from glob import glob 10 | from time import time, sleep, strftime 11 | 12 | from flag_format import FLAG_FORMAT 13 | 14 | FLAGS_IN_SUMBIT_ITERATION = 100 15 | RESTART_DELAY = 2 # in sec 16 | FLAGS_GLOB = "flags/*.txt" 17 | HOST = "127.0.0.1" # checksystem host 18 | PORT = 31337 # checksystem port 19 | TIMEOUT = 5 # checksystem timeout 20 | 21 | 22 | def log(text): 23 | print(strftime("%H:%M:%S") + " " + text) 24 | 25 | 26 | class PostedFlags: 27 | GOOD_FLAGS_FILE = "posted_good_flags.txt" 28 | BAD_FLAGS_FILE = "posted_bad_flags.txt" 29 | 30 | def __init__(self): 31 | # create flags files if not exists 32 | open(PostedFlags.GOOD_FLAGS_FILE, 'ab').close() 33 | open(PostedFlags.BAD_FLAGS_FILE, 'ab').close() 34 | 35 | good_flags_content = open(PostedFlags.GOOD_FLAGS_FILE, "rb", 1).read() 36 | good_flags = re.findall(FLAG_FORMAT, good_flags_content) 37 | self.good_flags = set(good_flags) 38 | 39 | bad_flags_content = open(PostedFlags.BAD_FLAGS_FILE, "rb", 1).read() 40 | bad_flags = re.findall(FLAG_FORMAT, bad_flags_content) 41 | self.bad_flags = set(bad_flags) 42 | 43 | def add_good_flag(self, flag): 44 | if flag not in self.good_flags: 45 | with open(PostedFlags.GOOD_FLAGS_FILE, "ab") as f: 46 | f.write(flag + b"\n") 47 | self.good_flags.add(flag) 48 | 49 | def add_bad_flag(self, flag): 50 | if flag not in self.bad_flags: 51 | with open(PostedFlags.BAD_FLAGS_FILE, "ab") as f: 52 | f.write(flag + b"\n") 53 | self.bad_flags.add(flag) 54 | 55 | def get(self): 56 | return self.good_flags | self.bad_flags 57 | 58 | 59 | ################################################### 60 | def submit_flags(flags, posted_flags): 61 | "YOU LIKELY HAVE TO EDIT THIS FUNCTION" 62 | 63 | # STAGE 0: connecting 64 | s = socket.create_connection((HOST, PORT), TIMEOUT) 65 | 66 | # just an example how to use sockets over SSL 67 | # import ssl 68 | # s = ssl.wrap_socket(s, 69 | # keyfile="team_private_key.pem", 70 | # certfile="team_cert.pem", 71 | # server_side=False, cert_reqs=False, 72 | # ssl_version=ssl.PROTOCOL_SSLv3, 73 | # do_handshake_on_connect=1 74 | # ) 75 | 76 | # STAGE 1: check if system greets us 77 | greeting = s.recv(4096) 78 | if b'Hello' not in greeting: 79 | print("Not greeted: " + greeting) 80 | return 81 | 82 | # STAGE 2: send the team name 83 | s.sendall(b"hackerdom\n") 84 | 85 | # STAGE 3: check if system asks for a password 86 | pass_greeting = s.recv(4096) 87 | if b'pass' not in pass_greeting: 88 | print("Not pass-greeted: %s" % pass_greeting) 89 | return 90 | 91 | # STAGE 4: send the password 92 | s.sendall(b"pass\n") 93 | 94 | # STAGE 5: check if system asks for flags 95 | keys_prompt = s.recv(4096) 96 | if b'keys' not in keys_prompt: 97 | print("Not keys prompted %s" % keys_prompt) 98 | return 99 | 100 | for flag in flags: 101 | # STAGE 6: send a flag 102 | s.sendall(flag + b"\n") 103 | 104 | # STAGE 7: check result 105 | result = s.recv(4096).strip().lower() 106 | 107 | # GOOD ANSWERS 108 | if (b'accepted' in result or 109 | b'congratulations' in result): 110 | posted_flags.add_good_flag(flag) 111 | # BAD ANSWERS 112 | elif (b'bad' in result or 113 | b'wrong' in result or 114 | b'expired' in result or 115 | b'unknown' in result or 116 | b'your own' in result or 117 | b'not in database' in result or 118 | b'already submitted' in result): 119 | posted_flags.add_bad_flag(flag) 120 | # NEUTRAL ANSWERS 121 | elif (b'timeout' in result or 122 | b'game not started' in result or 123 | b'game over' in result): 124 | pass 125 | else: 126 | print("unknown answer: %s" % result) 127 | ################################################## 128 | 129 | 130 | def get_all_flags(): 131 | flags = list() 132 | 133 | flag_files = glob(FLAGS_GLOB) # all files with flags 134 | for flag_file in flag_files: 135 | file_contents = open(flag_file, "rb" , 1).read() 136 | flags += re.findall(FLAG_FORMAT, file_contents) 137 | return flags 138 | 139 | # main posting cycle 140 | while True: 141 | begin_load_time = time() 142 | posted_flags = PostedFlags() 143 | flags_set = set(get_all_flags()) - posted_flags.get() 144 | flags = list(flags_set) 145 | 146 | if len(flags) > FLAGS_IN_SUMBIT_ITERATION: 147 | flags = random.sample(flags, FLAGS_IN_SUMBIT_ITERATION) 148 | 149 | end_load_time = time() 150 | 151 | log("Loaded %s new flags in %.2f sec, sending %s random" % 152 | (len(flags_set), end_load_time - begin_load_time, len(flags))) 153 | 154 | good_before = len(posted_flags.good_flags) 155 | bad_before = len(posted_flags.bad_flags) 156 | 157 | if flags: 158 | try: 159 | submit_flags(flags, posted_flags) 160 | except Exception as E: 161 | log("Exception while submitting: %s" % E + "\a") 162 | 163 | good_after = len(posted_flags.good_flags) 164 | bad_after = len(posted_flags.bad_flags) 165 | 166 | log("Iteration finished: good %s, bad %s" % 167 | (good_after - good_before, bad_after - bad_before)) 168 | sleep(RESTART_DELAY) 169 | -------------------------------------------------------------------------------- /start_sploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # author: Alexander Bersenev (Bay) from Hackerdom team 3 | # starts one sploit for all teams in a loop 4 | 5 | import re 6 | import sys 7 | import os 8 | 9 | from os.path import basename, splitext, abspath, exists 10 | from subprocess import Popen, PIPE, STDOUT 11 | from threading import Thread, Lock 12 | from time import time, sleep, strftime 13 | try: 14 | from Queue import Queue, Empty 15 | except ImportError: 16 | from queue import Queue, Empty # python 3.x 17 | 18 | from flag_format import FLAG_FORMAT 19 | from team_list import TEAMS 20 | 21 | MAX_RUNTIME = 60 # in secs, sploit will be restarted if it running too long 22 | PAUSE = 10 # in secs, the time between sploit relaunches 23 | 24 | OWNER_LOCK = Lock() # global lock that protects TeamOwner members 25 | 26 | def log(text): 27 | print(strftime("%H:%M:%S") + " " + text) 28 | 29 | 30 | class TeamOwner(Thread): 31 | def __init__(self, sploit_name, team_name, team_ip): 32 | Thread.__init__(self, name=team_name) 33 | 34 | self.sploit_name = sploit_name 35 | self.team_name = team_name 36 | self.team_ip = team_ip 37 | 38 | spl_short_name = splitext(basename(sploit_name))[0] 39 | self.flag_filename = "flags/%s_%s.txt" % (spl_short_name, team_name) 40 | 41 | open(self.flag_filename, 'ab').close() # create file if not exists 42 | 43 | savedflags = re.findall(FLAG_FORMAT, 44 | open(self.flag_filename, "rb").read()) 45 | self.flags = set(savedflags) 46 | 47 | self.cycle_num = 0 48 | 49 | log("Starting hacking team %s, launching in a loop: %s %s" % 50 | (self.team_name, basename(self.sploit_name), self.team_ip)) 51 | 52 | def run(self): 53 | while True: 54 | try: 55 | self.cycle_num += 1 56 | self.last_launch_time = time() 57 | self.launch_spl() 58 | except Exception as E: 59 | log("Exception, team %s: %s" % (self.team_name, E) + "\a") 60 | finally: 61 | if self.cycle_num == 1: 62 | log("Sploit for team %s: hiding output" % self.team_name) 63 | sleep(PAUSE) 64 | 65 | def launch_spl(self): 66 | need_launch_in_shell = (os.name == "nt") 67 | # launch sploit proccess with team_ip as arg 68 | spl = Popen([self.sploit_name, self.team_ip], 69 | stdout=PIPE, stderr=STDOUT, bufsize=1, 70 | shell=need_launch_in_shell) 71 | q = Queue() 72 | 73 | # we are processing output in other thread to prevent blocking 74 | def enqueue_output(queue, out): 75 | while True: 76 | line = out.readline() 77 | queue.put(line) 78 | if not line: 79 | break 80 | 81 | t = Thread(target=enqueue_output, args=(q, spl.stdout)) 82 | t.daemon = True 83 | t.start() 84 | 85 | # get output by lines until EOF 86 | while True: 87 | try: 88 | remaining_time = MAX_RUNTIME - (time() - self.last_launch_time) 89 | line = q.get(timeout=remaining_time) 90 | except (Empty, ValueError): 91 | log("Killing %s sploit(tried to run for more than %d secs)" % ( 92 | self.team_name, MAX_RUNTIME)) 93 | break 94 | 95 | if not line: 96 | break 97 | 98 | line = line.strip() 99 | if not line: 100 | continue 101 | 102 | if self.cycle_num == 1: 103 | print("%s: %s" % (self.team_name, line)) 104 | 105 | flags = re.findall(FLAG_FORMAT, line) 106 | 107 | OWNER_LOCK.acquire() 108 | for flag in flags: 109 | if flag not in self.flags: 110 | if self.cycle_num == 1: 111 | log("Flag from %s: %s" % (self.team_name, flag)) 112 | with open(self.flag_filename, "ab", 0) as f: 113 | f.write(flag + b"\n") 114 | 115 | self.flags.add(flag) 116 | else: 117 | if self.cycle_num == 1: 118 | log("Flag from %s: %s (dup)" % (self.team_name, flag)) 119 | OWNER_LOCK.release() 120 | 121 | if os.name != "nt": 122 | spl.kill() 123 | else: 124 | spl.communicate() 125 | 126 | # LETS ROCK !!! 127 | if len(sys.argv) < 2: 128 | print("Usage: start_sploit.py ") 129 | sys.exit(1) 130 | 131 | sploit_name = abspath(sys.argv[1]) 132 | if not exists(sploit_name): 133 | print("Sploit doesn't exist: " + sploit_name) 134 | sys.exit(1) 135 | 136 | # ensure that flag dir is exists 137 | if not exists("flags"): 138 | os.makedirs("flags") 139 | 140 | if os.name != "nt": 141 | os.setpgrp() 142 | 143 | if os.name == "nt": 144 | # Because Windows sucks at the proccess management 145 | log("Windows detected. Timeouts are not supported on this os!") 146 | log("Please write only short-time-running exploits") 147 | MAX_RUNTIME = 0xFFFFFFFF 148 | 149 | owners = [] 150 | for team_name, team_ip in TEAMS.items(): 151 | owner = TeamOwner(sploit_name, team_name, team_ip) 152 | owner.daemon = True 153 | owners.append(owner) 154 | 155 | try: 156 | for owner in owners: 157 | owner.start() # start pwning thread for each team 158 | 159 | while True: 160 | # generate stats in a loop 161 | OWNER_LOCK.acquire() 162 | last_flags = [set(o.flags) for o in owners] 163 | last_stats = [(len(o.flags), o.cycle_num) for o in owners] 164 | OWNER_LOCK.release() 165 | 166 | sleep(60 - int(time()) % 60) # show stat on zero second 167 | 168 | curr_flags = [set(o.flags) for o in owners] 169 | curr_stats = [(len(o.flags), o.cycle_num) for o in owners] 170 | stats = [(owners[i].team_name, 171 | curr_stats[i][0], curr_stats[i][0] - last_stats[i][0], 172 | curr_stats[i][1], curr_stats[i][1] - last_stats[i][1], 173 | list(curr_flags[i] - last_flags[i])) 174 | for i in range(len(owners))] 175 | last_flags = ["%s: %s" % (s[0], s[5]) for s in stats] 176 | log("LAST MINUTE NEW FLAGS: " + "\n".join(last_flags)) 177 | flag_stats = ["%s: %d(%d)" % (s[0], s[2], s[1]) for s in stats] 178 | log("LAST MINUTE FLAG STATS: " + ", ".join(flag_stats)) 179 | iter_stats = ["%s: %d(%d)" % (s[0], s[4], s[3]) for s in stats] 180 | log("LAST MINUTE LAUNCHES STATS: " + ", ".join(iter_stats)) 181 | 182 | except KeyboardInterrupt: 183 | print("Ctrl-c received!") 184 | 185 | # kill all sploits with the hardest fire I can cast 186 | log("Done") 187 | if os.name != "nt": 188 | os.killpg(0, 9) # UNIX only =( 189 | --------------------------------------------------------------------------------