diff --git a/cve/django/2022/CVE-2022-22818/README.md b/cve/django/2022/CVE-2022-22818/README.md new file mode 100644 index 0000000000000000000000000000000000000000..cd0364664e5a07e2a96e214120942c0df851c1f2 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/README.md @@ -0,0 +1,38 @@ +# Run tests on django + +```bash +python runfuzz.py +``` + +### To run django server: + +```bash +python manage.py runserver +``` + +### Check the python modules are installed: + +```bash +python3 -m django --version # pip install django==4.0.1 +grammarinator-generate --version # pip install grammarinator +pip install selenium # should be installed +pip install webdriver-manager # also +pip install progress +``` + +### Prepare fuzzer and check that all is ok + +```bash +touch grammars/fuzzer/HTMLGenerator.py # Otherwise it will not find the file +grammarinator-process grammars/HTMLLexer.g4 grammars/HTMLParser.g4 -o grammars/fuzzer # Fill the file +``` + +Try fuzzer: + +```bash +grammarinator-generate grammars.fuzzer.HTMLCustomGenerator.HTMLCustomGenerator -r htmlDocument -d 20 -o grammars/examples/test_%d.html -n 10 --sys-path ./ +cd grammars/examples/ +ls # Here is your files. +python -m http.server # start the server +``` + diff --git a/cve/django/2022/CVE-2022-22818/djangocontext.py b/cve/django/2022/CVE-2022-22818/djangocontext.py new file mode 100644 index 0000000000000000000000000000000000000000..14a5495a08b73f9905989da5d4e1f3b77ce82f07 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/djangocontext.py @@ -0,0 +1,100 @@ +import json +from logging import Logger +import logging +import os +from typing import List +from mylogger import setup_logger + +class CommentSection: + inner: str + type: str + name: str + + def __init__(self, section_data: str, info_json_payload: str): + self.inner = section_data + info = json.loads(info_json_payload) + self.type = info['type'] + self.name = info['name'] + + def isBlock(self) -> bool: + return self.type == "block" + + def isInclude(self) -> bool: + return self.type == "include" + +class ContextLoader: + logger: Logger = logging.getLogger("ContextLoader") + ctx_json_data: str + text: str + template_relative_path: str + base_file_relative_path: str + created_included_files: List[str] + + COMMENT = "{% comment %}" + ENDCOMMENT = "{% endcomment %}" + END_INFO = "#}" + + def __init__(self, template_filename: str): + self.base_file_relative_path = None + self.created_included_files = [] + if template_filename is not None: + self.template_relative_path = template_filename + with open(template_filename, "r") as text_file: + self.ctx_json_data = text_file.readline().strip().removeprefix("{#").removesuffix("#}") + self.text = text_file.read() + else: + self.logger.error("No file passed!") + + def get_context(self) -> dict: + return json.loads(self.ctx_json_data) + + def get_sections(self) -> List[CommentSection]: + res = [] + found_pos = self.text.find(self.COMMENT) + while found_pos != -1: + info_start_pos = found_pos + len(self.COMMENT) + info_end_pos = self.text.find(self.END_INFO, info_start_pos) + section_start_index = info_end_pos + len(self.END_INFO) + info_json = self.text[info_start_pos:section_start_index].strip().removeprefix("{#").removesuffix("#}") + section_end_index = self.text.find(self.ENDCOMMENT, section_start_index) + section_data = self.text[section_start_index:section_end_index] + cmt = CommentSection(section_data, info_json) + res.append(cmt) + found_pos = self.text.find(self.COMMENT, section_end_index + len(self.ENDCOMMENT)) + return res + + def create_and_modify_files_if_need(self): + sections = self.get_sections() + if any(map(lambda x: x.isBlock(), sections)): + self.logger.info("Modifying file %s as it contains blocks overriding.", self.template_relative_path) + filename_without_extension = self.template_relative_path.removesuffix(".html") + self.base_file_relative_path = filename_without_extension + "_base.html" + os.rename(self.template_relative_path, self.base_file_relative_path) + with open(self.template_relative_path, "w") as text_file: + base_filename = os.path.basename(self.base_file_relative_path) + text_file.write("{% extends \"./" + base_filename + "\" %}\n\n") + for section in filter(lambda x: x.isBlock(), sections): + self.logger.debug("Writing block '%s'.", section.name) + text_file.write("{% block " + section.name + " %}\n") + text_file.write(os.linesep.join([s for s in section.inner.splitlines() if s]) + "\n") + text_file.write("{% endblock %}\n\n\n") + if any(map(lambda x: x.isInclude(), sections)): + self.logger.info("Creating files for %s as it contains include keywords.", self.template_relative_path) + dirpath_to_create_files = os.path.dirname(os.path.abspath(self.template_relative_path)) + self.logger.info("Will create files in %s directory.", dirpath_to_create_files) + for section in filter(lambda x: x.isInclude(), sections): + new_filepath = os.path.join(dirpath_to_create_files, section.name) + with open(new_filepath, "w") as text_file: + self.logger.debug("Writing file '%s'.", section.name) + text_file.write(os.linesep.join([s for s in section.inner.splitlines() if s]) + "\n") + self.created_included_files.append(new_filepath) + self.logger.info("Saved for future deletion %s.", new_filepath) + + def remove_created_files(self): + if self.base_file_relative_path is not None and os.path.exists(self.base_file_relative_path): + self.logger.info("Deleting base file %s...", self.base_file_relative_path) + os.remove(self.base_file_relative_path) + for filename in self.created_included_files: + if os.path.exists(filename): + self.logger.info("Deleting included file %s...", filename) + os.remove(filename) \ No newline at end of file diff --git a/cve/django/2022/CVE-2022-22818/grammars/HTMLLexer.g4 b/cve/django/2022/CVE-2022-22818/grammars/HTMLLexer.g4 new file mode 100644 index 0000000000000000000000000000000000000000..93af095380a83f3e87b962cb7d81d2ccae8181a8 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/grammars/HTMLLexer.g4 @@ -0,0 +1,285 @@ +lexer grammar HTMLLexer; + +DJ_OPEN + : '{% ' + ; + +DJ_CLOSE + : ' %}' + ; + +DJ_VARIABLE_OPEN + : '{{ ' + ; + +DJ_VARIABLE_CLOSE + : ' }}' + ; + +JSON_BRACE_OPEN + : '{ ' + ; + +JSON_BRACE_CLOSE + : ' }' + ; + +JSON_COMMA + : ',' + ; + +JSON_QUOTES + : '"' + ; + +JSON_COLON + : ':' + ; + +DJ_COMMENT_OPEN + : '{# ' + ; + +DJ_COMMENT_CLOSE + : ' #}' + ; + +DJ_FORCE_ESCAPE_FILTER + : '|force_escape' + ; + +DJ_NOW + : 'now ' + ; + +DJ_NOW_FORMAT + : '"SHORT_DATETIME_FORMAT"' + | '"DATETIME_FORMAT"' + | '"DATE_FORMAT"' + | '"SHORT_DATE_FORMAT"' + ; + +DJ_DEBUG + : DJ_OPEN 'debug' DJ_CLOSE + ; + +DJ_WITH_KEYWORD + : 'with ' + ; + +DJ_BLOCK_KEYWORD + : 'block ' + ; + +DJ_FOR_KEYWORD + : 'for ' + ; + +DJ_AS_KEYWORD + : 'as ' + ; + +DJ_INCLUDE_KEYWORD + : 'include ' + ; + +DJ_FOR_IN_KEYWORD + : ' in ' + ; + +DJ_CYCLE_KEYWORD + : 'cycle ' + ; + +DJ_FIRSTOF_KEYWORD + : 'firstof ' + ; + +DJ_WITH_EQUALS + : '=' + ; + +DJ_SPACE + : ' ' + ; + +DJ_VARIABLE + : 'var1' | 'var2' | 'var3' | 'somename' + ; + +DJ_VALUE + : '"123"' | '""' + ; + +DJ_INCLUDE_FILENAME + : '"./snippet.html"' + ; + +DJ_END_WITH + : DJ_OPEN 'endwith' DJ_CLOSE + ; + +DJ_START_COMMENT + : DJ_OPEN 'comment' DJ_CLOSE + ; + +DJ_END_COMMENT + : DJ_OPEN 'endcomment' DJ_CLOSE + ; + +DJ_END_FOR_LOOP + : DJ_OPEN 'endfor' DJ_CLOSE + ; + +DJ_START_SPACELESS + : DJ_OPEN 'spaceless' DJ_CLOSE + ; + +DJ_END_SPACELESS + : DJ_OPEN 'endspaceless' DJ_CLOSE + ; + +DJ_TEMPLATE_TAG + : DJ_OPEN 'templatetag ' DJ_TEMPLATE_TAG_OPTION DJ_CLOSE + ; + +DJ_TEMPLATE_TAG_OPTION + : 'openblock' | 'closeblock' | 'openvariable' | 'closevariable' | 'openbrace' | 'closebrace' | 'opencomment' | 'closecomment' + ; + +DJ_END_BLOCK + : DJ_OPEN 'endblock' DJ_CLOSE + ; + +DJ_BLOCK_NAME + : 'name1' | 'name2' | 'name3' + ; + +JSON_FIELD_NAME + : 'name' + ; + +JSON_FIELD_NAMED_TYPE + : JSON_QUOTES 'type' JSON_QUOTES + ; + +JSON_FIELD_NAMED_NAME + : JSON_QUOTES 'name' JSON_QUOTES + ; + +JSON_FIELD_VALUE_BLOCK + : JSON_QUOTES 'block' JSON_QUOTES + ; + +JSON_FIELD_VALUE_INCLUDE + : JSON_QUOTES 'include' JSON_QUOTES + ; + +JSON_FIELD_STRING_VALUE + : 'value' + ; + +JSON_FIELD_LIST_VALUE + : '["value","value"]' + ; + +NEWLINE + : '\n' + ; + +TAG_OPEN + : '<' -> pushMode(TAG) + ; + +HTML_TEXT + : TAG_NameStartChar+ + ; + +// +// tag declarations +// +mode TAG; + +TAG_CLOSE + : '>' -> popMode + ; + +TAG_SLASH_CLOSE + : '/>' -> popMode + ; + +TAG_SLASH + : '/' + ; + +// +// lexing mode for attribute values +// +TAG_EQUALS + : '=' -> pushMode(ATTVALUE) + ; + +TAG_NAME + : TAG_NameStartChar TAG_NameChar* + ; + +TAG_WHITESPACE + : [ ] -> skip + ; + +fragment +HEXDIGIT + : [a-fA-F0-9] + ; + +fragment +DIGIT + : [0-9] + ; + +fragment +TAG_NameChar + : TAG_NameStartChar + | '-' + | '_' + | '.' + | DIGIT + ; + +fragment +TAG_NameStartChar + : [a-zA-Z] + ; + +// +// attribute values +// +mode ATTVALUE; + +// an attribute value may have spaces b/t the '=' and the value +ATTVALUE_VALUE + : ATTRIBUTE -> popMode + ; + +ATTRIBUTE + : ATTCHARS + ; + +fragment ATTCHAR + : '-' + | '_' + | '.' + | '/' + | '+' + | ',' + | '?' + | '=' + | ':' + | ';' + | '#' + | [0-9a-zA-Z] + ; + +fragment ATTCHARS + : ATTCHAR+ ' '? + ; diff --git a/cve/django/2022/CVE-2022-22818/grammars/HTMLParser.g4 b/cve/django/2022/CVE-2022-22818/grammars/HTMLParser.g4 new file mode 100644 index 0000000000000000000000000000000000000000..116448803de9be68e3c8ee326a5e09c8e982f273 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/grammars/HTMLParser.g4 @@ -0,0 +1,285 @@ +// TEST-PROCESS: {grammar}Parser.g4 {grammar}Lexer.g4 -o {tmpdir} +// TEST-GENERATE: {grammar}Generator.{grammar}Generator -r htmlDocument -s {grammar}Generator.html_space_serializer -n 5 -o {tmpdir}/{grammar}G%d.html +// TEST-GENERATE: {grammar}CustomGenerator.{grammar}CustomGenerator -r htmlDocument -s {grammar}Generator.html_space_serializer -n 5 -o {tmpdir}/{grammar}C%d.html --sys-path ../fuzzer/ + +parser grammar HTMLParser; + +options { tokenVocab=HTMLLexer; language='Python3'; } + +@header { +from copy import deepcopy +} + +@parser::member { +def _endOfHtmlElement(self): + pass + +def _endOfDjangoWithBlock(self): + pass + +def _endOfDjangoForLoop(self): + pass + +def _startOfDjangoWithBlock(self): + pass + +def _flushState(self): + pass + +def _hasAtLeastOneWithVariableDefined(self) -> bool: + return False + +def _hasAtLeastOneContextStringVariableDefined(self) -> bool: + return False + +def _hasAtLeastOneForLoopVariable(self) -> bool: + return False + +def _hasAtLeastOneContextListVariableDefined(self) -> bool: + return False + +def _hasAtLeastOneCycleVariableDefined(self) -> bool: + return False + +def _hasAtLeastOneDefinedVariable(self) -> bool: + return (self._hasAtLeastOneContextStringVariableDefined() or + self._hasAtLeastOneWithVariableDefined() or + self._hasAtLeastOneForLoopVariable() or + self._hasAtLeastOneCycleVariableDefined()) + +is_in_comment_section: bool = False +} + +htmlDocument + : {self._flushState()} djangoContext NEWLINE htmlElements+ + ; + +htmlElements + : htmlElement htmlMisc + ; + +htmlElement + : TAG_OPEN open_tag=htmlTagName TAG_WHITESPACE htmlAttribute? TAG_CLOSE htmlContent TAG_OPEN TAG_SLASH htmlTagName {current.last_child = deepcopy($open_tag)} TAG_CLOSE {self._endOfHtmlElement()} + | django + ; + +django + : djangoSpaceless + | djangoWith + | djangoDebug + | djangoTemplateTag + | djangoNowTag + | djangoFirstOf + | {self._hasAtLeastOneContextListVariableDefined()}? djangoForLoop + | {self._hasAtLeastOneForLoopVariable()}? djangoCycle + | {self._hasAtLeastOneDefinedVariable()}? djangoVariable + | djangoBlock + | {not self.is_in_comment_section}? djangoIncludeTag {self.is_in_comment_section = True} djangoCommentedIncludingTemplate {self.is_in_comment_section = False} + | {not self.is_in_comment_section}? djangoOverriddenBlock {self.is_in_comment_section = True} djangoCommentedOverridingBlock {self.is_in_comment_section = False} + ; + +djangoDebug + : DJ_DEBUG + ; + +djangoWith + : {self._startOfDjangoWithBlock()} DJ_OPEN DJ_WITH_KEYWORD djangoWithVariables+ DJ_CLOSE htmlContent DJ_END_WITH {self._endOfDjangoWithBlock()} + ; + +djangoCommentedOverridingBlock + : DJ_START_COMMENT djangoCommentedOverridingBlockInfo htmlElement+ DJ_END_COMMENT + ; + +djangoCommentedIncludingTemplate + : DJ_START_COMMENT djangoCommentedIncludingTemplateInfo htmlElement+ DJ_END_COMMENT + ; + +djangoCommentedOverridingBlockInfo + : DJ_COMMENT_OPEN JSON_BRACE_OPEN + JSON_FIELD_NAMED_TYPE JSON_COLON JSON_FIELD_VALUE_BLOCK JSON_COMMA + JSON_FIELD_NAMED_NAME JSON_COLON JSON_QUOTES jsonOverridingBlockName JSON_QUOTES + JSON_BRACE_CLOSE DJ_COMMENT_CLOSE + ; + +djangoCommentedIncludingTemplateInfo + : DJ_COMMENT_OPEN JSON_BRACE_OPEN + JSON_FIELD_NAMED_TYPE JSON_COLON JSON_FIELD_VALUE_INCLUDE JSON_COMMA + JSON_FIELD_NAMED_NAME JSON_COLON JSON_QUOTES jsonIncludingTemplateName JSON_QUOTES + JSON_BRACE_CLOSE DJ_COMMENT_CLOSE + ; + +jsonOverridingBlockName + : DJ_BLOCK_NAME + ; + +jsonIncludingTemplateName + : JSON_FIELD_STRING_VALUE + ; + +djangoOverriddenBlock + : DJ_OPEN DJ_BLOCK_KEYWORD djangoOverriddenBlockName DJ_CLOSE htmlContent DJ_END_BLOCK + ; + +djangoIncludeTag + : DJ_OPEN DJ_INCLUDE_KEYWORD djangoIncludeFileNameWithQuotes DJ_CLOSE + ; + +djangoBlock + : DJ_OPEN DJ_BLOCK_KEYWORD djangoBlockName DJ_CLOSE htmlContent DJ_END_BLOCK + ; + +djangoSpaceless + : DJ_START_SPACELESS htmlContent DJ_END_SPACELESS + ; + +djangoNowTag + : DJ_OPEN DJ_NOW DJ_NOW_FORMAT DJ_CLOSE + ; + +djangoTemplateTag + : DJ_TEMPLATE_TAG + ; + +djangoForLoop + : DJ_OPEN DJ_FOR_KEYWORD djangoForLoopVariableName DJ_FOR_IN_KEYWORD djangoDefinedContextListVariable DJ_CLOSE NEWLINE htmlContent NEWLINE DJ_END_FOR_LOOP {self._endOfDjangoForLoop()} + ; + +djangoCycle + : DJ_OPEN DJ_CYCLE_KEYWORD djangoCycleValue DJ_SPACE (djangoCycleValue DJ_SPACE)+ (DJ_AS_KEYWORD djangoCycleVariableName)? DJ_CLOSE + ; + +djangoCycleValue + : {self._hasAtLeastOneContextStringVariableDefined()}? djangoDefinedContextVariable + | {self._hasAtLeastOneWithVariableDefined()}? djangoDefinedWithVariable + | djangoCycleStringValue + ; + +djangoWithVariables + : djangoWithVariable DJ_WITH_EQUALS djangoWithVariableValue DJ_SPACE + ; + +djangoFirstOf + : DJ_OPEN DJ_FIRSTOF_KEYWORD (djangoFirstOfVariable DJ_SPACE)+ DJ_VALUE? DJ_CLOSE + ; + +djangoFirstOfVariable + : DJ_VARIABLE + | {self._hasAtLeastOneDefinedVariable()}? djangoDefinedVariable + ; + +djangoVariable + : DJ_VARIABLE_OPEN djangoDefinedVariable DJ_FORCE_ESCAPE_FILTER DJ_VARIABLE_CLOSE + ; + +djangoDefinedVariable + : {self._hasAtLeastOneWithVariableDefined()}? djangoDefinedWithVariable + | {self._hasAtLeastOneContextStringVariableDefined()}? djangoDefinedContextVariable + | {self._hasAtLeastOneForLoopVariable()}? djangoDefinedLoopVariable + | {self._hasAtLeastOneCycleVariableDefined()}? djangoDefinedCycleVariable + ; + +djangoIncludeFileNameWithQuotes + : DJ_INCLUDE_FILENAME + ; + +djangoDefinedLoopVariable + : DJ_VARIABLE + ; + +djangoForLoopVariableName + : DJ_VARIABLE + ; + +djangoDefinedContextListVariable + : DJ_VARIABLE + ; + +djangoDefinedWithVariable + : DJ_VARIABLE + ; + +djangoDefinedContextVariable + : DJ_VARIABLE + ; + +djangoDefinedCycleVariable + : DJ_VARIABLE + ; + +djangoCycleVariableName + : DJ_VARIABLE + ; + +djangoWithVariable + : DJ_VARIABLE + ; + +djangoCycleStringValue + : DJ_VALUE + ; + +djangoWithVariableValue + : {self._hasAtLeastOneContextStringVariableDefined()}? djangoDefinedContextVariable + | DJ_VALUE + ; + +htmlContent + : htmlChardata? (htmlElement htmlChardata?)* + ; + +htmlAttribute + : htmlAttributeName TAG_EQUALS htmlAttributeValue + ; + +djangoContext + : DJ_COMMENT_OPEN JSON_BRACE_OPEN djangoContextVariableDefinition (JSON_COMMA djangoContextVariableDefinition)* JSON_BRACE_CLOSE DJ_COMMENT_CLOSE + ; + +djangoContextVariableDefinition + : JSON_QUOTES jsonFieldName JSON_QUOTES JSON_COLON jsonFieldValue + ; + +jsonFieldName + : JSON_FIELD_NAME + ; + +jsonFieldValue + : JSON_QUOTES jsonStringValue JSON_QUOTES + | jsonListValue + ; + +jsonStringValue + : JSON_FIELD_STRING_VALUE + ; + +jsonListValue + : JSON_FIELD_LIST_VALUE + ; + +htmlAttributeName + : TAG_NAME + ; + +htmlAttributeValue + : ATTVALUE_VALUE + ; + +djangoOverriddenBlockName + : DJ_BLOCK_NAME + ; + +djangoBlockName + : DJ_BLOCK_NAME + ; + +htmlTagName + : TAG_NAME + ; + +htmlChardata + : HTML_TEXT NEWLINE + ; + +htmlMisc + : NEWLINE + ; diff --git a/cve/django/2022/CVE-2022-22818/grammars/fuzzer/HTMLCustomGenerator.py b/cve/django/2022/CVE-2022-22818/grammars/fuzzer/HTMLCustomGenerator.py new file mode 100644 index 0000000000000000000000000000000000000000..57119c91325fa419e2f3e96c9d375801c0816188 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/grammars/fuzzer/HTMLCustomGenerator.py @@ -0,0 +1,264 @@ +import json +import random + +from os.path import dirname, join +import string +from typing import List + +from grammarinator.runtime import * + +from grammars.fuzzer.HTMLGenerator import HTMLGenerator +from itertools import chain + +with open(join(dirname(__file__), 'html.json')) as f: + tags = json.load(f) + +tag_names = list(tags.keys()) + + +class HTMLCustomGenerator(HTMLGenerator): + + django_context = dict() # Контекст шаблона. + last_json_field_name = [] # Буфер для создания новой переменной. + attr_stack = [] # Валидные значения html-атрибутов. + tag_stack = [] # Валидные html-тэги и их атрибуты. + for_variables_stack = [] # Имена переменных, которые используются в for-циклах. + cycle_variables_stack = [] # Имена, которые используются для сохранения значения cycle внутри for-циклов. + django_block_names: List[str] = [] # Уникальные имена блоков. + django_variables_block_stack: List[List[str]] = [] # Введённые через with-блоки переменные django. + overridden_block_names = [] # Имена блоков для переопределения. + include_filename: str = "" # Имя включаемого файла. + + # Очищение всего перед следующим тестом. + def _flushState(self): + self.django_context = dict() + self.last_json_field_name = [] + self.attr_stack = [] + self.tag_stack = [] + self.for_variables_stack = [] + self.cycle_variables_stack = [] + self.django_block_names = [] + self.django_variables_block_stack = [] + self.overridden_block_names = [] + self.include_filename = "" + + # Получение случайного значения для контекстной переменной django. + def __getRandomStringValue(self): + return random.choice(["123", "", "

HELLO!

"]) + + # Получение случайно сгенерированного списка. + def __getRandomListValue(self): + i: int = random.randint(1, 10) + return [self.__getRandomStringValue() for k in range(i)] + + # Получение контекстных переменных, значение которых является указанным типом. + def __getContextVariablesOfCertainType(self, type): + return list(item[0] for item in self.django_context.items() if isinstance(item[1], type)) + + @staticmethod + def __toSingleList(list_of_lists): + return list(chain.from_iterable(list_of_lists)) + + # Получение всех переменных определенных в with-блоках. + def __getAllDefinedWithVariables(self): + return self.__toSingleList(self.django_variables_block_stack) + + # Получение всех переменных определённых в cycle-тэгах. + def __getAllDefinedCycleVariables(self): + return self.__toSingleList(self.cycle_variables_stack) + + # Генерация уникального имени переменной django-контекста. + def jsonFieldName(self, parent=None): + current = UnparserRule(name='jsonFieldName', parent=parent) + name_length = 1 + name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length)) + while name in self.django_context.keys(): + name_length += 1 + name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length)) + UnlexerRule(src=name, parent=current) + self.last_json_field_name.append(name) + return current + + # Переменная django-контекста как строка: генерация + сохранение в контекст для дальнейшего использования. + def jsonStringValue(self, parent=None): + current = UnparserRule(name='jsonStringValue', parent=parent) + value = self.__getRandomStringValue() + UnlexerRule(src=value, parent=current) + new_field_name = self.last_json_field_name.pop() + self.django_context[new_field_name] = value + return current + + # Переменная django-контекста как список строк: генерация + сохранение в контекст для дальнейшего использования. + def jsonListValue(self, parent=None): + current = UnparserRule(name='jsonListValue', parent=parent) + value = self.__getRandomListValue() + UnlexerRule(src=json.dumps(value), parent=current) + new_field_name = self.last_json_field_name.pop() + self.django_context[new_field_name] = value + return current + + # Валидные имена тэгов. + def htmlTagName(self, parent=None): + current = UnparserRule(name='htmlTagName', parent=parent) + name = random.choice(tags[self.tag_stack[-1]]['children'] or tag_names if self.tag_stack else tag_names) + self.tag_stack.append(name) + UnlexerRule(src=name, parent=current) + return current + + # Уникальные имена блоков. + def djangoBlockName(self, parent=None): + current = UnparserRule(name='djangoBlockName', parent=parent) + name_length = 1 + name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length)) + while name in self.django_block_names: + name_length += 1 + name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length)) + self.django_block_names.append(name) + UnlexerRule(src=name, parent=current) + return current + + # Уникальные имена блоков + сохранение в стэк имени, так как блок будет переопределён. + def djangoOverriddenBlockName(self, parent=None): + current = UnparserRule(name='djangoOverriddenBlockName', parent=parent) + name_length = 1 + name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length)) + while name in self.django_block_names: + name_length += 1 + name = ''.join(random.choice(string.ascii_uppercase) for _ in range(name_length)) + self.django_block_names.append(name) + self.overridden_block_names.append(name) # абсолютно такой же как djangoBlockName за исключением вот этой вот строчки. + UnlexerRule(src=name, parent=current) + return current + + # Сохранение имён переменных django + проверка, чтобы они были уникальными. + def djangoWithVariable(self, parent=None): + current = UnparserRule(name='djangoWithVariable', parent=parent) + name = 'var' + str(sum(map(len, self.django_variables_block_stack))) + self.django_variables_block_stack[-1].append(name) + UnlexerRule(src=name, parent=current) + return current + + # Существующее имя django-переменной из with-блока. + def djangoDefinedWithVariable(self, parent=None): + current = UnparserRule(name='djangoDefinedWithVariable', parent=parent) + django_defined_variable_name = random.choice(self.__getAllDefinedWithVariables()) + UnlexerRule(src=django_defined_variable_name, parent=current) + return current + + # Существующее имя django-переменной из контекста. + def djangoDefinedContextVariable(self, parent=None): + current = UnparserRule(name='djangoDefinedContextVariable', parent=parent) + django_defined_variable_name = random.choice(self.__getContextVariablesOfCertainType(str)) + UnlexerRule(src=django_defined_variable_name, parent=current) + return current + + # Существующее имя django-переменной из контекста, которая является списком. + def djangoDefinedContextListVariable(self, parent=None): + current = UnparserRule(name='djangoDefinedContextListVariable', parent=parent) + django_defined_variable_name = random.choice(self.__getContextVariablesOfCertainType(list)) + UnlexerRule(src=django_defined_variable_name, parent=current) + return current + + # Генерируем + сохраняем имя переменной for-loop. + def djangoForLoopVariableName(self, parent=None): + current = UnparserRule(name='djangoForLoopVariableName', parent=parent) + name = 'loop_var' + str(len(self.for_variables_stack)) + UnlexerRule(src=name, parent=current) + self.for_variables_stack.append(name) + self.cycle_variables_stack.append([]) + return current + + # Генерируем + сохраняем имя переменной cycle. Это может возникнуть только внутри цикла, поэтому len(cycle_variables_stack) >0. + def djangoCycleVariableName(self, parent=None): + current = UnparserRule(name='djangoCycleVariableName', parent=parent) + name = 'cycle_var' + str(sum(map(len, self.cycle_variables_stack))) + UnlexerRule(src=name, parent=current) + self.cycle_variables_stack[-1].append(name) + return current + + # Существующее имя cycle переменной. + def djangoDefinedCycleVariable(self, parent=None): + current = UnparserRule(name='djangoDefinedCycleVariable', parent=parent) + django_defined_variable_name = random.choice(self.__getAllDefinedCycleVariables()) + UnlexerRule(src=django_defined_variable_name, parent=current) + return current + + # Имя for-loop переменной. + def djangoDefinedLoopVariable(self, parent=None): + current = UnparserRule(name='djangoDefinedLoopVariable', parent=parent) + django_defined_variable_name = random.choice(self.for_variables_stack) + UnlexerRule(src=django_defined_variable_name, parent=current) + return current + + # Имя переопределяемого блока. + def jsonOverridingBlockName(self, parent=None): + current = UnparserRule(name='jsonOverridingBlockName', parent=parent) + django_defined_name = self.overridden_block_names.pop() + UnlexerRule(src=django_defined_name, parent=current) + return current + + # Имя включаемого файла. + def jsonIncludingTemplateName(self, parent=None): + current = UnparserRule(name='jsonIncludingTemplateName', parent=parent) + UnlexerRule(src=self.include_filename, parent=current) + self.include_filename = "" + return current + + # Имя включаемого файла: генерация + сохранение. + def djangoIncludeFileNameWithQuotes(self, parent=None): + current = UnparserRule(name='djangoIncludeFileNameWithQuotes', parent=parent) + name_length = 10 + self.include_filename = "./t_" + ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(name_length)) + ".html" + UnlexerRule(src='"' + self.include_filename + '"', parent=current) + return current + + # Валидное имя атрибута. + def htmlAttributeName(self, parent=None): + current = UnparserRule(name='htmlAttributeName', parent=parent) + name = random.choice(list(tags[self.tag_stack[-1]]['attributes'].keys()) or ['""']) + self.attr_stack.append(name) + UnlexerRule(src=name, parent=current) + return current + + # Валидные значения атрибутов для текущего тэга и атрибутов. + def htmlAttributeValue(self, parent=None): + current = UnparserRule(name='htmlAttributeValue', parent=parent) + UnlexerRule(src=random.choice(tags[self.tag_stack[-1]]['attributes'].get(self.attr_stack.pop(), ['""']) or ['""']), parent=current) + return current + + # Конец определения html-тэга. + def _endOfHtmlElement(self): + self.tag_stack.pop() + + # Начало django блока with. + def _startOfDjangoWithBlock(self): + self.django_variables_block_stack.append([]) + + # Конец django блока with. + def _endOfDjangoWithBlock(self): + self.django_variables_block_stack.pop() + + # Конец django for-цикла. + def _endOfDjangoForLoop(self): + self.for_variables_stack.pop() + self.cycle_variables_stack.pop() + + # Есть ли хотя бы 1 определенная django-переменная из with-блока, которую можно использовать для вставки. + def _hasAtLeastOneWithVariableDefined(self) -> bool: + return len(self.django_variables_block_stack) > 0 + + # Есть ли хотя бы 1 определённая строковая django-переменная из контекста. + def _hasAtLeastOneContextStringVariableDefined(self) -> bool: + return len(self.__getContextVariablesOfCertainType(str)) > 0 + + # Есть ли хотя бы 1 определённая django-переменная из контекста, которая является списком. + def _hasAtLeastOneContextListVariableDefined(self) -> bool: + return len(self.__getContextVariablesOfCertainType(list)) > 0 + + # Есть ли хотя бы 1 переменная из цикла. + def _hasAtLeastOneForLoopVariable(self) -> bool: + return len(self.for_variables_stack) > 0 + + # Есть хотя бы 1 cycle-переменная. + def _hasAtLeastOneCycleVariableDefined(self) -> bool: + return len(self.__getAllDefinedCycleVariables()) > 0 diff --git a/cve/django/2022/CVE-2022-22818/grammars/fuzzer/html.json b/cve/django/2022/CVE-2022-22818/grammars/fuzzer/html.json new file mode 100644 index 0000000000000000000000000000000000000000..b0449a46128e8e119b5ee8bec2cd40e6332ae1b6 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/grammars/fuzzer/html.json @@ -0,0 +1,55 @@ +{ + "a": { + "attributes": { + "href": ["https://github.com/renatahodovan/grammarinator", "https://github.com/renatahodovan/fuzzinator"] + }, + "children": [] + }, + "address": { + "attributes": { + "contenteditable": ["true", "false"], + "dir": ["ltr", "rtl"], + "draggable": ["true", "false"] + }, + "children": ["a", "address", "article"] + }, + "article": { + "attributes": { + "style": ["\"color: #2ecc71\""] + }, + "children": ["a", "article"] + }, + "base": { + "attributes": { + "contenteditable": ["true", "false"], + "dir": ["ltr", "rtl"], + "draggable": ["true", "false"], + "translate": ["true", "false"] + }, + "children": [] + }, + "body": { + "attributes": { + "translate": ["true", "false"] + }, + "children": ["a", "address", "article"] + }, + "head": { + "attributes": { + "contenteditable": ["true", "false"], + "dir": ["ltr", "rtl"], + "draggable": ["true", "false"], + "translate": ["true", "false"] + }, + "children": ["base"] + }, + "html": { + "attributes": { + "contenteditable": ["true", "false"], + "dir": ["ltr", "rtl"], + "draggable": ["true", "false"], + "translate": ["true", "false"] + }, + "children": ["head", "body"] + } +} diff --git a/cve/django/2022/CVE-2022-22818/manage.py b/cve/django/2022/CVE-2022-22818/manage.py new file mode 100755 index 0000000000000000000000000000000000000000..a7da6671aab8568fe454d9e4bccdcdf661b6f7ff --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/cve/django/2022/CVE-2022-22818/mylogger.py b/cve/django/2022/CVE-2022-22818/mylogger.py new file mode 100644 index 0000000000000000000000000000000000000000..5c731df35a8799deb228ba98a1d19a3e31c4b3c4 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/mylogger.py @@ -0,0 +1,15 @@ +import logging + + +def setup_logger(logger: logging.Logger, to_level: int = logging.DEBUG, newline: str = ""): + """Устанавливает базовые настройки логгера. + + :param logger: Логгер. + :type logger: logging.Logger + """ + console_handler = logging.StreamHandler() + FORMAT = newline + "%(asctime)s — %(name)s — %(levelname)s — %(message)s" + format = logging.Formatter(FORMAT, validate=True) + console_handler.setFormatter(format) + logger.addHandler(console_handler) + logger.setLevel(to_level) \ No newline at end of file diff --git a/cve/django/2022/CVE-2022-22818/mysite/__init__.py b/cve/django/2022/CVE-2022-22818/mysite/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cve/django/2022/CVE-2022-22818/mysite/asgi.py b/cve/django/2022/CVE-2022-22818/mysite/asgi.py new file mode 100644 index 0000000000000000000000000000000000000000..674a5a5af4d12fd968808f8c390a03392260c083 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/mysite/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for mysite project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') + +application = get_asgi_application() diff --git a/cve/django/2022/CVE-2022-22818/mysite/settings.py b/cve/django/2022/CVE-2022-22818/mysite/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..578f297288345cf011f7aa9eef7f922a80955aca --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/mysite/settings.py @@ -0,0 +1,124 @@ +""" +Django settings for mysite project. + +Generated by 'django-admin startproject' using Django 4.0.1. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-ya3vdv0#u#e-=t1o4#)^)i09gweg90*a(ew=*&gnkb-1$7)r7n' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'polls.apps.PollsConfig', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'mysite.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'mysite.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.0/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/cve/django/2022/CVE-2022-22818/mysite/urls.py b/cve/django/2022/CVE-2022-22818/mysite/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..cb270f7971c33fc5b489fb0963c969f318171abc --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/mysite/urls.py @@ -0,0 +1,21 @@ +"""mysite URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('', include('polls.urls')), +] diff --git a/cve/django/2022/CVE-2022-22818/mysite/wsgi.py b/cve/django/2022/CVE-2022-22818/mysite/wsgi.py new file mode 100644 index 0000000000000000000000000000000000000000..48d46fe4cf84ba2e7d4aa9f808a849ba9dc495c7 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/mysite/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for mysite project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') + +application = get_wsgi_application() diff --git a/cve/django/2022/CVE-2022-22818/polls/__init__.py b/cve/django/2022/CVE-2022-22818/polls/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cve/django/2022/CVE-2022-22818/polls/admin.py b/cve/django/2022/CVE-2022-22818/polls/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/polls/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/cve/django/2022/CVE-2022-22818/polls/apps.py b/cve/django/2022/CVE-2022-22818/polls/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..5a5f94ca17168cdc07caa88325fbb36421bf4eef --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/polls/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PollsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'polls' diff --git a/cve/django/2022/CVE-2022-22818/polls/migrations/__init__.py b/cve/django/2022/CVE-2022-22818/polls/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cve/django/2022/CVE-2022-22818/polls/models.py b/cve/django/2022/CVE-2022-22818/polls/models.py new file mode 100644 index 0000000000000000000000000000000000000000..71a836239075aa6e6e4ecb700e9c42c95c022d91 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/polls/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/cve/django/2022/CVE-2022-22818/polls/templates/polls/base.html b/cve/django/2022/CVE-2022-22818/polls/templates/polls/base.html new file mode 100644 index 0000000000000000000000000000000000000000..35dd4e3e9a34646abf5be4dc3616debb261174d4 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/polls/templates/polls/base.html @@ -0,0 +1,23 @@ + + +Base part
+ +{% for var2 in list_var %} + +{% with var0="string_value" %} + +{% block main %} + +Var0: {{ var0 }}
{# with #} + +Var1: {{ var1 }}
{# контекст #} + +Var2: {{ var2 }}
{# for #} + +{% endblock %} + +{% endwith %} + +{% endfor %} + + diff --git a/cve/django/2022/CVE-2022-22818/polls/templates/polls/index.html b/cve/django/2022/CVE-2022-22818/polls/templates/polls/index.html new file mode 100644 index 0000000000000000000000000000000000000000..5ecf8e7d9ca86d2aebb8d81ad2a01fbc9eec6830 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/polls/templates/polls/index.html @@ -0,0 +1,6 @@ +{% with message1="" %} +
+ {% debug %} +
+Visible page. +{% endwith %} \ No newline at end of file diff --git a/cve/django/2022/CVE-2022-22818/polls/templates/polls/inherited.html b/cve/django/2022/CVE-2022-22818/polls/templates/polls/inherited.html new file mode 100644 index 0000000000000000000000000000000000000000..fe6d3ca7c0b748596dce1b086351af13b0f4020c --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/polls/templates/polls/inherited.html @@ -0,0 +1,13 @@ +{% extends './base.html' %} + +{% block main %} + +Inherited part
+ +Var0: {{ var0 }}
{# with #} + +Var1: {{ var1 }}
{# контекст #} + +Var2: {{ var2 }}
{# for #} + +{% endblock %} diff --git a/cve/django/2022/CVE-2022-22818/polls/tests.py b/cve/django/2022/CVE-2022-22818/polls/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/polls/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/cve/django/2022/CVE-2022-22818/polls/urls.py b/cve/django/2022/CVE-2022-22818/polls/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..3ef24d9717f6abdd8d356990d19b5c92b2ed0261 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/polls/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('', views.index, name='index'), +] \ No newline at end of file diff --git a/cve/django/2022/CVE-2022-22818/polls/views.py b/cve/django/2022/CVE-2022-22818/polls/views.py new file mode 100644 index 0000000000000000000000000000000000000000..d9cd37938768c5a8e8f75066fece535569ed8d80 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/polls/views.py @@ -0,0 +1,8 @@ +#from django.shortcuts import render +from django.template import loader +# Create your views here. +from django.http import HttpResponse +from django.shortcuts import render + +def index(request): + return render(request, 'polls/inherited.html', context={"var1": "string_another", "list_var": ["string_absolutely_new"]}) diff --git a/cve/django/2022/CVE-2022-22818/runfuzz.py b/cve/django/2022/CVE-2022-22818/runfuzz.py new file mode 100644 index 0000000000000000000000000000000000000000..8ab760822be212c9bf366bebf2ccf043c1824262 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/runfuzz.py @@ -0,0 +1,116 @@ +import django +import os +import logging +import subprocess +from django.conf import settings +import django +from django.template import Template, Context, loader +from django.template.loader import get_template +from selen import Driver +from djangocontext import ContextLoader +from progress.bar import IncrementalBar +from grammarinator.generate import * +from multiprocessing import Pool +from mylogger import setup_logger + +logger = logging.getLogger("Fuzzer") + +def django_setup(): + settings.configure(TEMPLATES=[ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': ['./polls/templates/polls'], + } + ]) + django.setup() + + +def generate_tests(num: int, g: Generator): + bar = IncrementalBar('Generate', max=num) + for i in range(num): + g(i) + bar.next() + bar.finish() + return True + + +def check_test(num: int, d: Driver) -> bool: + """Проверяет тесты. + + :param num: Кол-во тестов. + :type num: int + :param d: Драйвер проверки. + :type d: Driver + :return: True если нашёл. + :rtype: bool + """ + found: bool = False + bar = IncrementalBar('Check', max=num) + for i in range(num): + template_filepath: str = f"./polls/templates/polls/test_{i}.html" + output_rendered_name = f"./polls/templates/polls/rendered_test_{i}.html" + if not found: + ctx = ContextLoader(template_filepath) + ctx.create_and_modify_files_if_need() + t = get_template(f'test_{i}.html') + with open(output_rendered_name, "w") as text_file: + text_file.write(t.render(ctx.get_context())) + # if d.is_template_matched(template_filepath): + if d.is_alert_present(output_rendered_name): + bar.finish() + found = True + logger.info("Found!!!") + continue + bar.next() + ctx.remove_created_files() + if os.path.exists(output_rendered_name): + os.remove(output_rendered_name) + os.remove(template_filepath) + + if not found: + bar.finish() + return found + + +def run(): + d = Driver() + g = Generator(generator='grammars.fuzzer.HTMLCustomGenerator.HTMLCustomGenerator', rule='htmlDocument', out_format='/home/alex/Документы/django-example/polls/templates/polls/test_%d.html', + model='grammarinator.runtime.DefaultModel', max_depth=60, cleanup=False) + django_setup() + num_of_tests = 20 + found: bool = False + + while not found: + logger.info("Creating new pool...") + generate_tests(num_of_tests, g) + found = check_test(num_of_tests, d) + if not found: + logger.info(f"Not found. Have run {num_of_tests} tests...") + +def prepare_fuzzer(): + logger.info("Preparing fuzzer for generating tests...") + HTMLGenerator_modify_time = os.path.getmtime("grammars/fuzzer/HTMLGenerator.py") + HTMLLexer_modify_time = os.path.getmtime("grammars/HTMLLexer.g4") + HTMLParser_modify_time = os.path.getmtime("grammars/HTMLParser.g4") + if HTMLGenerator_modify_time > HTMLLexer_modify_time and HTMLGenerator_modify_time > HTMLParser_modify_time: + logger.info("Will not run grammarinator-process as grammar is already ready...") + return + result = subprocess.run(["grammarinator-process", "grammars/HTMLLexer.g4", "grammars/HTMLParser.g4", "-o", "grammars/fuzzer"], capture_output=True, text=True, check=True, timeout=10) + logger.info("Fuzzer prepared and grammar is ready!") + logger.debug('output: %s', result.stdout) + logger.debug('error: %s', result.stderr) + + +def clear_directory(): + templates_directory = "./polls/templates/polls" + only_files = [f for f in os.listdir(templates_directory) if os.path.isfile(os.path.join(templates_directory, f))] + for file in filter(lambda x: (x.startswith("test_") or x.startswith("rendered_test_") or x.startswith("t_")) and x.endswith(".html"), only_files): + logger.info("Found file from previous run: '%s'. Will be deleted!", file) + os.remove(os.path.join(templates_directory, file)) + +if __name__ == "__main__": + setup_logger(logger) + setup_logger(logging.getLogger("ContextLoader"), logging.WARNING) + prepare_fuzzer() + clear_directory() + run() diff --git a/cve/django/2022/CVE-2022-22818/selen.py b/cve/django/2022/CVE-2022-22818/selen.py new file mode 100644 index 0000000000000000000000000000000000000000..8fabe6672d0b38fe9557033477a5ba9dbad3112d --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/selen.py @@ -0,0 +1,55 @@ +import logging +from selenium import webdriver as wd +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.common.exceptions import UnexpectedAlertPresentException +from webdriver_manager.chrome import ChromeDriverManager +from selenium.common.exceptions import NoAlertPresentException +import os +from mylogger import setup_logger + +class Driver: + logger = logging.getLogger("Driver") + + def __init__(self): + setup_logger(self.logger, logging.INFO, "\n") + self.driver = wd.Chrome(ChromeDriverManager().install()) + self.wait = WebDriverWait(self.driver, 15) + + def is_alert_present(self, relative_path): + full_path = os.path.abspath(relative_path) + self.logger.debug("Testing rendered html at path %s.", full_path) + try: + self.driver.get(f"file://{full_path}") + try: + alert = self.driver.switch_to.alert + if alert is None: + return False + self.logger.warning("Found alert at: %s", relative_path) + return True + except NoAlertPresentException: + return False + except UnexpectedAlertPresentException: + self.logger.warning("UnexpectedAlertPresentException at: %s", relative_path) + return True + + def is_template_matched(self, template_filepath) -> bool: + """Возвращает True если файл шаблона соответствует искомому. + + Полезно, если надо проверить что грамматика может генерировать шаблоны + определённой структуры. + + :param template_filepath: Путь до файла шаблона. + :type template_filepath: str + :rtype: bool + """ + strings: list = [] + with open(template_filepath, "r") as text_file: + strings = text_file.readlines() + + searching_keyword = " comment " + result: bool = any(map(lambda s: s.count(searching_keyword), strings)) + if result: + self.logger.warning("Found searching template file at: %s", template_filepath) + return result \ No newline at end of file diff --git a/cve/django/2022/CVE-2022-22818/tests/README.md b/cve/django/2022/CVE-2022-22818/tests/README.md new file mode 100644 index 0000000000000000000000000000000000000000..80654850009610eb5680307df6769cda30706688 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/tests/README.md @@ -0,0 +1,11 @@ +# Тесты + +для проверки работы отдельных скриптов. + +## Запуск + +Из корневой директории: + +```bash +python tests/context.py +``` \ No newline at end of file diff --git a/cve/django/2022/CVE-2022-22818/tests/context.py b/cve/django/2022/CVE-2022-22818/tests/context.py new file mode 100644 index 0000000000000000000000000000000000000000..4867888a62154d6f0ea446fc5ad8d798505b6e76 --- /dev/null +++ b/cve/django/2022/CVE-2022-22818/tests/context.py @@ -0,0 +1,33 @@ +import sys +sys.path.append('.') +import unittest +from djangocontext import ContextLoader, CommentSection + + +class TestContextLoader(unittest.TestCase): + def setUp(self): + self.loader = ContextLoader(None) + + def test_get_context(self): + self.loader.ctx_json_data = "{ \"hello\": 123 }" + self.assertEqual(self.loader.get_context(), {"hello": 123}) + + def test_get_sections(self): + self.loader.text = """ + {% block Z %}{% endblock %}{% comment %}{# { "type":"block","name":"Z" } #}GKNUM + {% with var0=X %}{% block R %}xR + {% debug %}{% endblock %}D + {% endwith %}{% endcomment %} + """ + sections = self.loader.get_sections() + self.assertEqual(len(sections), 1) + section: CommentSection = (sections[0]) + self.assertEqual(section.name, "Z") + self.assertEqual(section.type, "block") + self.assertEqual(section.inner, """GKNUM + {% with var0=X %}{% block R %}xR + {% debug %}{% endblock %}D + {% endwith %}""") + +if __name__ == "__main__": + unittest.main() diff --git a/cve/django/2022/yaml/CVE-2022-22818.yaml b/cve/django/2022/yaml/CVE-2022-22818.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0f6df8755d0ce30f7ae21eccb8dce1ab6ce1022a --- /dev/null +++ b/cve/django/2022/yaml/CVE-2022-22818.yaml @@ -0,0 +1,22 @@ +id: CVE-2022-22818 +source: + https://github.com/Prikalel/django-xss-example +info: + name: Django 是一个高级的 Python 网络框架,可以快速开发安全和可维护的网站。由经验丰富的开发者构建,Django 负责处理网站开发中麻烦的部分,因此你可以专注于编写应用程序,而无需重新开发。 它是免费和开源的,有活跃繁荣的社区,丰富的文档,以及很多免费和付费的解决方案。 + severity: MEDIUM + description: | + Django 2.2(2.2.27之前)、3.2(3.2.12之前)和4.0(4.0.2之前)中的{% debug %}模板标签没有正确编码当前上下文。这可能导致XSS。 + scope-of-influence: + Django 2.2.0 prior to 2.2.27 + Django 3.2.0 prior to 3.2.12 + Django 4.0.0 prior to 4.0.2 + reference: + - https://nvd.nist.gov/vuln/detail/CVE-2022-22818 + classification: + cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N + cvss-score: 6.1 + cve-id: CVE-2022-22818 + cwe-id: CWE-79 + cnvd-id: None + kve-id: None + tags: XSS \ No newline at end of file diff --git a/openkylin_list.yaml b/openkylin_list.yaml index 7128c8a654f0511f8b057f1d715266e718f048c2..3712ef5f55bb9c0562df72d8e5c7b6b3a47cb0d9 100644 --- a/openkylin_list.yaml +++ b/openkylin_list.yaml @@ -38,7 +38,9 @@ cve: apache-unomi: - CVE-2020-13942 apache-struts: - - CVE-2019-0230 + - CVE-2019-0230 + Django: + - CVE-2022-22818 Influx-DB: - CVE-2019-20933 linux-kernel: