1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
| chall.py
from exec_utils import safe_exec
def my_safe_exec(__source): assert __source.isascii(), "ascii check failed" blacklist = ["match", "case", "async", "def", "class", "frame", "_", "byte", "coding"] for x in blacklist: assert x not in __source, f"{x} is banned" return safe_exec(__source)
if __name__ == "__main__": __source = "" print("Enter code: ") try: while (inp := input()) != "#EOF": __source += inp + "\n" except EOFError: pass try: my_safe_exec(__source) except AssertionError as err: print(err) exec_utils.py
import ast import copy from types import CodeType, ModuleType from typing import Any, Dict, Mapping, Sequence, Union
ALLOWED_IMPORTS = { "math", "time", "datetime", "pandas", "numpy", "matplotlib", "plotly", "seaborn", }
def _restricted_import( name: str, globals: Union[Mapping[str, object], None] = None, locals: Union[Mapping[str, object], None] = None, fromlist: Sequence[str] = (), level: int = 0, ) -> ModuleType: if name in ALLOWED_IMPORTS: return __import__(name, globals, locals, fromlist, level) raise ImportError(f"Import of module '{name}' is not allowed")
ALLOWED_BUILTINS = { "abs": abs, "all": all, "any": any, "ascii": ascii, "bin": bin, "bool": bool, "bytearray": bytearray, "bytes": bytes, "chr": chr, "complex": complex, "divmod": divmod, "enumerate": enumerate, "filter": filter, "float": float, "format": format, "frozenset": frozenset, "hash": hash, "hex": hex, "int": int, "isinstance": isinstance, "issubclass": issubclass, "iter": iter, "len": len, "list": list, "map": map, "max": max, "min": min, "next": next, "oct": oct, "open": open, "ord": ord, "pow": pow, "print": print, "range": range, "repr": repr, "reversed": reversed, "round": round, "set": set, "slice": slice, "sorted": sorted, "staticmethod": staticmethod, "sum": sum, "tuple": tuple, "type": type, "zip": zip, "len": len, "str": str, "True": True, "False": False, "None": None, "__import__": _restricted_import, }
def _get_restricted_globals(__globals: Union[dict, None]) -> Any: restricted_globals = copy.deepcopy(ALLOWED_BUILTINS) if __globals: restricted_globals.update(__globals) return restricted_globals
vulnerable_code_snippets = [ "os.", ]
class DunderVisitor(ast.NodeVisitor): def __init__(self) -> None: self.has_access_to_private_entity = False self.has_access_to_disallowed_builtin = False builtins = globals()["__builtins__"].keys() self._builtins = builtins def visit_Name(self, node: ast.Name) -> None: if node.id.startswith("_"): self.has_access_to_private_entity = True if node.id not in ALLOWED_BUILTINS and node.id in self._builtins: self.has_access_to_disallowed_builtin = True self.generic_visit(node) def visit_Attribute(self, node: ast.Attribute) -> None: if node.attr.startswith("_"): self.has_access_to_private_entity = True if node.attr not in ALLOWED_BUILTINS and node.attr in self._builtins: self.has_access_to_disallowed_builtin = True self.generic_visit(node)
def _contains_protected_access(code: str) -> bool: imports_modules = False tree = ast.parse(code) for node in ast.walk(tree): if isinstance(node, ast.Import): imports_modules = True elif isinstance(node, ast.ImportFrom): imports_modules = True else: continue dunder_visitor = DunderVisitor() dunder_visitor.visit(tree) for vulnerable_code_snippet in vulnerable_code_snippets: if vulnerable_code_snippet in code: dunder_visitor.has_access_to_disallowed_builtin = True return ( dunder_visitor.has_access_to_private_entity or dunder_visitor.has_access_to_disallowed_builtin or imports_modules )
def _verify_source_safety(__source: Union[str, bytes, CodeType]) -> None: """ Verify that the source is safe to execute. For now, this means that it does not contain any references to private or dunder methods. """ if isinstance(__source, CodeType): raise RuntimeError("Direct execution of CodeType is forbidden!") if isinstance(__source, bytes): __source = __source.decode() if _contains_protected_access(__source): raise RuntimeError( "Execution of code containing references to private or dunder methods, " "disallowed builtins, or any imports, is forbidden!" )
def safe_eval( __source: Union[str, bytes, CodeType], __globals: Union[Dict[str, Any], None] = None, __locals: Union[Mapping[str, object], None] = None, ) -> Any: _verify_source_safety(__source) return eval(__source, _get_restricted_globals(__globals), __locals)
def safe_exec( __source: Union[str, bytes, CodeType], __globals: Union[Dict[str, Any], None] = None, __locals: Union[Mapping[str, object], None] = None, ) -> None: _verify_source_safety(__source) return exec(__source, _get_restricted_globals(__globals), __locals)
|