├── README.md ├── color_log.py ├── dump_python.py ├── judge_injection.py ├── test10.py ├── test2fun.py ├── test3.py ├── test_cmd2.py ├── test_com.py ├── test_judge.py ├── test_lookuparg.py ├── test_xss.py ├── testclass.py ├── testif-return.py ├── testrequest.py └── testsql.py /README.md: -------------------------------------------------------------------------------- 1 | # 1,python的语法树 2 | 根据王垠的python静态分析工具[PySonar](https://github.com/yinwang0/pysonar2)得到静态语法树,这是一个庞大的dict结构,递归去除一些不必要的参数得到稍微简单点的一个语法树,以免影响后续分析。 3 | 这是文件test_lookuparg.py得到的解析树,body里面包含两个dict,每个dict都是一个函数,另外还有type字段表明这个节点的类型。 4 | 下图是一个函数的基本实现: 5 | 首先是”type”:”FunctionDef” 表明这一段代码是函数定义,函数中则会有args,表明函数的参数,lineno是代码所在的行,name是函数名。更详细的接口文档见https://greentreesnakes.readthedocs.org/en/latest/nodes.html 在这里包含了各个结构的定义,分析整个树就可以依照这个来实现。 6 | 1.2危险函数+可控参数 7 | 危险函数有eval,system,popen等系统函数,同时也有咱们自定义的包含这些危险函数的函数,如果这些函数的参数是可控的,就会认为这行代码是有注入风险的,那么这个函数也是有注入风险的。 8 | 可控参数,首先会从函数参数入手,认为函数参数是可控的,分析程序会根据前面的语法树去分析代码结构,发现有将函数参数赋值的操作,并且这个赋值是简单的转换,这些简单的转换包含如下类型: 9 | * (1) 简单的取属性,如get取值,对request单独处理,只认ä=acp#onPopupPost() 10 | ¸ºGET,POST,FILES可控,其他request字段如META,user,session,url等都是不可控的。 11 | * (2) 字符串拼接,被拼接的字符串中包含可控参数,则认为赋值后的值也是可控的 12 | * (3) 列表解析式,如果列表解析式是基于某个可控因子进行迭代的,则认为赋值后的列表也是可控的 13 | * (4) 分片符取值,一般认为分片后的值也是可控的,当然这个也不绝对。 14 | * (5) 一般的函数处理过程:a,函数是常见的字符串操作函数(str,encode,strip等)或者是简单的未过滤函数;b,处理属性;c,如果经过了未知的函数处理则将赋值后的值从可控列表中去掉。 15 | * (6) 如果代码中的if中有exists,isdigit等带可控参数的的return语句,则将该参数从可控参数列表中去掉(if not os.path.isdir(parentPath):return None),或者将可控参数定死在某个范围之内的(if type not in ["R", "B"]:return HttpResponse("2")) 16 | 17 | # 2,使用方法 18 | 使用方法如下: 19 | liaoxinxi$ python judge_injection.py -h 20 | Usage: judge_injection.py [options] 21 | 22 | Options: 23 | -h, --help show this help message and exit 24 | -d FILE_PATH, --dir=FILE_PATH 25 | files to be checked 26 | -c, --cmd cmd check 27 | -s, --sql sql check 28 | -a, --all cmd check and sql check 29 | -v, --verbose print all unsafe func 30 | 31 | # 3,代码结构 32 | 一个judge_injection类,首先是初始化得到一个简化的python语法树,提炼出代码中包含的函数语句,分析每一行语句,在碰到函数的时候会调用look_up_arg函数,该函数就是可以得出函数中的可变变量。如果可变变量在危险函数中出现了,则认为该函数是危险的。 33 | 34 | # 4,不足之处 35 | (1)目前只做了两层函数分析,对于django的web代码来说已经足够应付 36 | (2)对类的分析还不彻底 37 | (3)目前是基于单个文件来进行分析的,没有考虑模块导入 38 | (4)该模块还可以扩展为分析任意文件下载,任意文件删除等。 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /color_log.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2010, 2011 Vinay Sajip. All rights reserved. 3 | # 4 | import logging 5 | import os 6 | 7 | class ColorizingStreamHandler(logging.StreamHandler): 8 | # color names to indices 9 | color_map = { 10 | 'black': 0, 11 | 'red': 1, 12 | 'green': 2, 13 | 'yellow': 3, 14 | 'blue': 4, 15 | 'magenta': 5, 16 | 'cyan': 6, 17 | 'white': 7, 18 | } 19 | 20 | #levels to (background, foreground, bold/intense) 21 | level_map = { 22 | #logging.DEBUG: (None, 'blue', False), 23 | logging.DEBUG: (None, 'white', False), 24 | logging.INFO: (None, 'blue', False), 25 | logging.WARNING: (None, 'yellow', False), 26 | logging.ERROR: (None, 'red', False), 27 | logging.CRITICAL: ('red', 'white', True), 28 | } 29 | csi = '\x1b[' 30 | reset = '\x1b[0m' 31 | 32 | @property 33 | def is_tty(self): 34 | isatty = getattr(self.stream, 'isatty', None) 35 | return isatty and isatty() 36 | 37 | def emit(self, record): 38 | try: 39 | message = self.format(record) 40 | stream = self.stream 41 | stream.write(message) 42 | stream.write(getattr(self, 'terminator', '\n')) 43 | self.flush() 44 | except (KeyboardInterrupt, SystemExit): 45 | raise 46 | except: 47 | self.handleError(record) 48 | 49 | def output_colorized(self, message): 50 | self.stream.write(message) 51 | 52 | def colorize(self, message, record): 53 | if record.levelno in self.level_map: 54 | bg, fg, bold = self.level_map[record.levelno] 55 | params = [] 56 | if bg in self.color_map: 57 | params.append(str(self.color_map[bg] + 40)) 58 | if fg in self.color_map: 59 | params.append(str(self.color_map[fg] + 30)) 60 | if bold: 61 | params.append('1') 62 | if params: 63 | message = ''.join((self.csi, ';'.join(params), 64 | 'm', message, self.reset)) 65 | return message 66 | 67 | def format(self, record): 68 | message = logging.StreamHandler.format(self, record) 69 | if self.is_tty: 70 | # Don't colorize any traceback 71 | parts = message.split('\n', 1) 72 | parts[0] = self.colorize(parts[0], record) 73 | message = '\n'.join(parts) 74 | return message 75 | 76 | def main(): 77 | root = logging.getLogger() 78 | root.setLevel(logging.DEBUG) 79 | root.addHandler(ColorizingStreamHandler()) 80 | logging.debug('DEBUG') 81 | logging.info('INFO') 82 | logging.warning('WARNING') 83 | logging.error('ERROR') 84 | logging.critical('CRITICAL') 85 | 86 | def init_log(log_level): 87 | """log_level = logging.NOTSET logging.DEBUG logging.INFO logging.ERROR logging.CRITICAL""" 88 | root = logging.getLogger() 89 | root.setLevel(log_level) 90 | stream_handler = ColorizingStreamHandler() 91 | formatter = logging.Formatter('[%(funcName)-10s %(lineno)d %(levelname)-8s] %(message)s') 92 | #logging.StreamHandler.setFormatter(formatter) 93 | 94 | stream_handler.setFormatter(formatter) 95 | #root.addHandler(ColorizingStreamHandler()) 96 | root.addHandler(stream_handler) 97 | return root 98 | 99 | if __name__ == '__main__': 100 | # main() 101 | logger = init_log(logging.DEBUG) 102 | logger.debug('DEBUG..........................') 103 | logger.info('INFO----------------------------') 104 | logger.warning('WARNING======================') 105 | logger.error('ERROR**************************') 106 | logger.error('ERROR**************************%r' %({'value':'111'})) 107 | #logger.error('ERROR**************************', {'value':'111'}) 108 | -------------------------------------------------------------------------------- /dump_python.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import re 3 | import sys 4 | import codecs 5 | 6 | from json import JSONEncoder 7 | from ast import * 8 | 9 | 10 | # Is it Python 3? 11 | is_python3 = hasattr(sys.version_info, 'major') and (sys.version_info.major == 3) 12 | 13 | 14 | class AstEncoder(JSONEncoder): 15 | def default(self, o): 16 | if hasattr(o, '__dict__'): 17 | d = o.__dict__ 18 | # workaround: decode strings if it's not Python3 code 19 | if not is_python3: 20 | for k in d: 21 | if isinstance(d[k], str): 22 | if k == 's': 23 | d[k] = lines[d['start']:d['end']] 24 | else: 25 | d[k] = d[k].decode(enc) 26 | d['type'] = o.__class__.__name__ 27 | return d 28 | else: 29 | return str(o) 30 | 31 | 32 | enc = 'latin1' 33 | lines = '' 34 | 35 | def parse_dump(filename, output, end_mark): 36 | try: 37 | if is_python3: 38 | encoder = AstEncoder() 39 | else: 40 | encoder = AstEncoder(encoding=enc) 41 | 42 | tree = parse_file(filename) 43 | encoded = encoder.encode(tree) 44 | f = open(output, "w") 45 | f.write(encoded) 46 | f.close() 47 | finally: 48 | # write marker file to signal write end 49 | f = open(end_mark, "w") 50 | f.close() 51 | 52 | def parse_json(filename): 53 | try: 54 | if is_python3: 55 | encoder = AstEncoder() 56 | else: 57 | encoder = AstEncoder(encoding=enc) 58 | 59 | tree = parse_file(filename) 60 | encoded = encoder.encode(tree) 61 | return encoded 62 | except: 63 | return "" 64 | 65 | 66 | def parse_file(filename): 67 | global enc, lines 68 | enc, enc_len = detect_encoding(filename) 69 | f = codecs.open(filename, 'r', enc) 70 | lines = f.read() 71 | 72 | # remove BOM 73 | lines = re.sub(u'\ufeff', ' ', lines) 74 | 75 | # replace the encoding decl by spaces to fool python parser 76 | # otherwise you get 'encoding decl in unicode string' syntax error 77 | # print('enc:', enc, 'enc_len', enc_len) 78 | if enc_len > 0: 79 | lines = re.sub('#.*coding\s*[:=]\s*[\w\d\-]+', '#' + ' ' * (enc_len-1), lines) 80 | 81 | f.close() 82 | return parse_string(lines, filename) 83 | 84 | 85 | def parse_string(string, filename=None): 86 | tree = ast.parse(string) 87 | improve_ast(tree, string) 88 | if filename: 89 | tree.filename = filename 90 | return tree 91 | 92 | 93 | # short function for experiments 94 | def p(filename): 95 | parse_dump(filename, "json1", "end1") 96 | 97 | 98 | def detect_encoding(path): 99 | fin = open(path, 'rb') 100 | prefix = str(fin.read(80)) 101 | encs = re.findall('#.*coding\s*[:=]\s*([\w\d\-]+)', prefix) 102 | decl = re.findall('#.*coding\s*[:=]\s*[\w\d\-]+', prefix) 103 | 104 | if encs: 105 | enc1 = encs[0] 106 | enc_len = len(decl[0]) 107 | try: 108 | info = codecs.lookup(enc1) 109 | # print('lookedup: ', info) 110 | except LookupError: 111 | # print('encoding not exist: ' + enc1) 112 | return 'latin1', enc_len 113 | return enc1, enc_len 114 | else: 115 | return 'latin1', -1 116 | 117 | 118 | #------------------------------------------------------------- 119 | # improvements to the AST 120 | #------------------------------------------------------------- 121 | def improve_ast(node, s): 122 | build_index_map(s) 123 | improve_node(node, s) 124 | 125 | 126 | # build global table 'idxmap' for lineno <-> index oonversion 127 | def build_index_map(s): 128 | global line_starts 129 | idx = 0 130 | line_starts = [0] 131 | while idx < len(s): 132 | if s[idx] == '\n': 133 | line_starts.append(idx + 1) 134 | idx += 1 135 | 136 | 137 | # convert (line, col) to offset index 138 | def map_idx(line, col): 139 | return line_starts[line - 1] + col 140 | 141 | 142 | # convert offset index into (line, col) 143 | def map_line_col(idx): 144 | line = 0 145 | for start in line_starts: 146 | if idx < start: 147 | break 148 | line += 1 149 | col = idx - line_starts[line - 1] 150 | return (line, col) 151 | 152 | 153 | def improve_node(node, s): 154 | if isinstance(node, list): 155 | for n in node: 156 | improve_node(n, s) 157 | 158 | elif isinstance(node, AST): 159 | 160 | find_start(node, s) 161 | find_end(node, s) 162 | add_missing_names(node, s) 163 | 164 | for f in node_fields(node): 165 | improve_node(f, s) 166 | 167 | 168 | def find_start(node, s): 169 | ret = None # default value 170 | 171 | if hasattr(node, 'start'): 172 | ret = node.start 173 | 174 | elif isinstance(node, list): 175 | if node != []: 176 | ret = find_start(node[0], s) 177 | 178 | elif isinstance(node, Module): 179 | if node.body != []: 180 | ret = find_start(node.body[0], s) 181 | 182 | elif isinstance(node, BinOp): 183 | leftstart = find_start(node.left, s) 184 | if leftstart != None: 185 | ret = leftstart 186 | else: 187 | ret = map_idx(node.lineno, node.col_offset) 188 | 189 | elif hasattr(node, 'lineno'): 190 | if node.col_offset >= 0: 191 | ret = map_idx(node.lineno, node.col_offset) 192 | else: # special case for """ strings 193 | i = map_idx(node.lineno, node.col_offset) 194 | while i > 0 and i + 2 < len(s) and s[i:i + 3] != '"""' and s[i:i + 3] != "'''": 195 | i -= 1 196 | ret = i 197 | else: 198 | return None 199 | 200 | if ret == None and hasattr(node, 'lineno'): 201 | raise TypeError("got None for node that has lineno", node) 202 | 203 | if isinstance(node, AST) and ret != None: 204 | node.start = ret 205 | 206 | return ret 207 | 208 | 209 | def find_end(node, s): 210 | the_end = None 211 | 212 | if hasattr(node, 'end'): 213 | return node.end 214 | 215 | elif isinstance(node, list): 216 | if node != []: 217 | the_end = find_end(node[-1], s) 218 | 219 | elif isinstance(node, Module): 220 | if node.body != []: 221 | the_end = find_end(node.body[-1], s) 222 | 223 | elif isinstance(node, Expr): 224 | the_end = find_end(node.value, s) 225 | 226 | elif isinstance(node, Str): 227 | i = find_start(node, s) 228 | while s[i] != '"' and s[i] != "'": 229 | i += 1 230 | 231 | if i + 2 < len(s) and s[i:i + 3] == '"""': 232 | q = '"""' 233 | i += 3 234 | elif i + 2 < len(s) and s[i:i + 3] == "'''": 235 | q = "'''" 236 | i += 3 237 | elif s[i] == '"': 238 | q = '"' 239 | i += 1 240 | elif s[i] == "'": 241 | q = "'" 242 | i += 1 243 | else: 244 | print("illegal quote:", i, s[i]) 245 | q = '' 246 | 247 | if q != '': 248 | the_end = end_seq(s, q, i) 249 | 250 | elif isinstance(node, Name): 251 | the_end = find_start(node, s) + len(node.id) 252 | 253 | elif isinstance(node, Attribute): 254 | the_end = end_seq(s, node.attr, find_end(node.value, s)) 255 | 256 | elif isinstance(node, FunctionDef): 257 | the_end = find_end(node.body, s) 258 | 259 | elif isinstance(node, Lambda): 260 | the_end = find_end(node.body, s) 261 | 262 | elif isinstance(node, ClassDef): 263 | the_end = find_end(node.body, s) 264 | 265 | # print will be a Call in Python 3 266 | elif not is_python3 and isinstance(node, Print): 267 | the_end = start_seq(s, '\n', find_start(node, s)) 268 | 269 | elif isinstance(node, Call): 270 | start = find_end(node.func, s) 271 | if start != None: 272 | the_end = match_paren(s, '(', ')', start) 273 | 274 | elif isinstance(node, Yield): 275 | the_end = find_end(node.value, s) 276 | 277 | elif isinstance(node, Return): 278 | if node.value != None: 279 | the_end = find_end(node.value, s) 280 | else: 281 | the_end = find_start(node, s) + len('return') 282 | 283 | elif (isinstance(node, For) or 284 | isinstance(node, While) or 285 | isinstance(node, If) or 286 | isinstance(node, IfExp)): 287 | if node.orelse != []: 288 | the_end = find_end(node.orelse, s) 289 | else: 290 | the_end = find_end(node.body, s) 291 | 292 | elif isinstance(node, Assign) or isinstance(node, AugAssign): 293 | the_end = find_end(node.value, s) 294 | 295 | elif isinstance(node, BinOp): 296 | the_end = find_end(node.right, s) 297 | 298 | elif isinstance(node, BoolOp): 299 | the_end = find_end(node.values[-1], s) 300 | 301 | elif isinstance(node, Compare): 302 | the_end = find_end(node.comparators[-1], s) 303 | 304 | elif isinstance(node, UnaryOp): 305 | the_end = find_end(node.operand, s) 306 | 307 | elif isinstance(node, Num): 308 | the_end = find_start(node, s) + len(str(node.n)) 309 | 310 | elif isinstance(node, List): 311 | the_end = match_paren(s, '[', ']', find_start(node, s)); 312 | 313 | elif isinstance(node, Subscript): 314 | the_end = match_paren(s, '[', ']', find_start(node, s)); 315 | 316 | elif isinstance(node, Tuple): 317 | if node.elts != []: 318 | the_end = find_end(node.elts[-1], s) 319 | 320 | elif isinstance(node, Dict): 321 | the_end = match_paren(s, '{', '}', find_start(node, s)); 322 | 323 | elif ((not is_python3 and isinstance(node, TryExcept)) or 324 | (is_python3 and isinstance(node, Try))): 325 | if node.orelse != []: 326 | the_end = find_end(node.orelse, s) 327 | elif node.handlers != []: 328 | the_end = find_end(node.handlers, s) 329 | else: 330 | the_end = find_end(node.body, s) 331 | 332 | elif isinstance(node, ExceptHandler): 333 | the_end = find_end(node.body, s) 334 | 335 | elif isinstance(node, Pass): 336 | the_end = find_start(node, s) + len('pass') 337 | 338 | elif isinstance(node, Break): 339 | the_end = find_start(node, s) + len('break') 340 | 341 | elif isinstance(node, Continue): 342 | the_end = find_start(node, s) + len('continue') 343 | 344 | elif isinstance(node, Global): 345 | the_end = start_seq(s, '\n', find_start(node, s)) 346 | 347 | elif isinstance(node, Import): 348 | the_end = find_start(node, s) + len('import') 349 | 350 | elif isinstance(node, ImportFrom): 351 | the_end = find_start(node, s) + len('from') 352 | 353 | else: # can't determine node end, set to 3 chars after start 354 | start = find_start(node, s) 355 | if start != None: 356 | the_end = start + 3 357 | 358 | if isinstance(node, AST) and the_end != None: 359 | node.end = the_end 360 | 361 | return the_end 362 | 363 | 364 | def add_missing_names(node, s): 365 | if hasattr(node, 'extra_attr'): 366 | return 367 | 368 | if isinstance(node, list): 369 | for n in node: 370 | add_missing_names(n, s) 371 | 372 | elif isinstance(node, ClassDef): 373 | head = find_start(node, s) 374 | start = s.find("class", head) + len("class") 375 | if start != None: 376 | node.name_node = str_to_name(s, start) 377 | node._fields += ('name_node',) 378 | 379 | elif isinstance(node, FunctionDef): 380 | # skip to "def" because it may contain decorators like @property 381 | head = find_start(node, s) 382 | start = s.find("def", head) + len("def") 383 | if start != None: 384 | node.name_node = str_to_name(s, start) 385 | node._fields += ('name_node',) 386 | 387 | # keyword_start = find_start(node, s) 388 | # node.keyword_node = str_to_name(s, keyword_start) 389 | # node._fields += ('keyword_node',) 390 | 391 | if node.args.vararg != None: 392 | if len(node.args.args) > 0: 393 | vstart = find_end(node.args.args[-1], s) 394 | else: 395 | vstart = find_end(node.name_node, s) 396 | if vstart != None: 397 | vname = str_to_name(s, vstart) 398 | node.vararg_name = vname 399 | else: 400 | node.vararg_name = None 401 | node._fields += ('vararg_name',) 402 | 403 | if node.args.kwarg != None: 404 | if len(node.args.args) > 0: 405 | kstart = find_end(node.args.args[-1], s) 406 | else: 407 | kstart = find_end(node.vararg_name, s) 408 | if kstart: 409 | kname = str_to_name(s, kstart) 410 | node.kwarg_name = kname 411 | else: 412 | node.kwarg_name = None 413 | node._fields += ('kwarg_name',) 414 | 415 | elif isinstance(node, Attribute): 416 | start = find_end(node.value, s) 417 | if start is not None: 418 | name = str_to_name(s, start) 419 | node.attr_name = name 420 | node._fields = ('value', 'attr_name') # remove attr for node size accuracy 421 | 422 | elif isinstance(node, Compare): 423 | start = find_start(node, s) 424 | if start is not None: 425 | node.opsName = convert_ops(node.ops, s, start) 426 | node._fields += ('opsName',) 427 | 428 | elif (isinstance(node, BoolOp) or 429 | isinstance(node, BinOp) or 430 | isinstance(node, UnaryOp) or 431 | isinstance(node, AugAssign)): 432 | if hasattr(node, 'left'): 433 | start = find_end(node.left, s) 434 | else: 435 | start = find_start(node, s) 436 | if start is not None: 437 | ops = convert_ops([node.op], s, start) 438 | else: 439 | ops = [] 440 | if ops != []: 441 | node.op_node = ops[0] 442 | node._fields += ('op_node',) 443 | 444 | elif isinstance(node, Num): 445 | if isinstance(node.n, int) or (not is_python3 and isinstance(node.n, long)): 446 | type = 'int' 447 | node.n = str(node.n) 448 | elif isinstance(node.n, float): 449 | type = 'float' 450 | node.n = str(node.n) 451 | elif isinstance(node.n, complex): 452 | type = 'complex' 453 | node.real = node.n.real 454 | node.imag = node.n.imag 455 | node._fields += ('real', 'imag') 456 | 457 | node.num_type = type 458 | node._fields += ('num_type',) 459 | 460 | node.extra_attr = True 461 | 462 | 463 | #------------------------------------------------------------- 464 | # utilities used by improve AST functions 465 | #------------------------------------------------------------- 466 | 467 | # find a sequence in a string s, returning the start point 468 | def start_seq(s, pat, start): 469 | try: 470 | return s.index(pat, start) 471 | except ValueError: 472 | return len(s) 473 | 474 | 475 | # find a sequence in a string s, returning the end point 476 | def end_seq(s, pat, start): 477 | try: 478 | return s.index(pat, start) + len(pat) 479 | except ValueError: 480 | return len(s) 481 | 482 | 483 | # find matching close paren from start 484 | def match_paren(s, open, close, start): 485 | while start < len(s) and s[start] != open: 486 | start += 1 487 | if start >= len(s): 488 | return len(s) 489 | 490 | left = 1 491 | i = start + 1 492 | while left > 0 and i < len(s): 493 | if s[i] == open: 494 | left += 1 495 | elif s[i] == close: 496 | left -= 1 497 | i += 1 498 | return i 499 | 500 | 501 | # convert string to Name 502 | def str_to_name(s, start): 503 | i = start; 504 | while i < len(s) and not is_alpha(s[i]): 505 | i += 1 506 | name_start = i 507 | 508 | ret = [] 509 | while i < len(s) and is_alpha(s[i]): 510 | ret.append(s[i]) 511 | i += 1 512 | name_end = i 513 | 514 | id1 = ''.join(ret) 515 | if id1 == '': 516 | return None 517 | else: 518 | name = Name(id1, None) 519 | name.start = name_start 520 | name.end = name_end 521 | name.lineno, name.col_offset = map_line_col(name_start) 522 | return name 523 | 524 | 525 | def convert_ops(ops, s, start): 526 | syms = [] 527 | for op in ops: 528 | if type(op) in ops_map: 529 | syms.append(ops_map[type(op)]) 530 | else: 531 | print("[WARNING] operator %s is missing from ops_map, " 532 | "please report the bug on GitHub" % op) 533 | 534 | i = start 535 | j = 0 536 | ret = [] 537 | while i < len(s) and j < len(syms): 538 | oplen = len(syms[j]) 539 | if s[i:i + oplen] == syms[j]: 540 | op_node = Name(syms[j], None) 541 | op_node.start = i 542 | op_node.end = i + oplen 543 | op_node.lineno, op_node.col_offset = map_line_col(i) 544 | ret.append(op_node) 545 | j += 1 546 | i = op_node.end 547 | else: 548 | i += 1 549 | return ret 550 | 551 | 552 | # lookup table for operators for convert_ops 553 | ops_map = { 554 | # compare: 555 | Eq: '==', 556 | NotEq: '!=', 557 | LtE: '<=', 558 | Lt: '<', 559 | GtE: '>=', 560 | Gt: '>', 561 | NotIn: 'not in', 562 | In: 'in', 563 | IsNot: 'is not', 564 | Is: 'is', 565 | 566 | # BoolOp 567 | Or: 'or', 568 | And: 'and', 569 | Not: 'not', 570 | Invert: '~', 571 | 572 | # bit operators 573 | BitOr: '|', 574 | BitAnd: '&', 575 | BitXor: '^', 576 | RShift: '>>', 577 | LShift: '<<', 578 | 579 | 580 | # BinOp 581 | Add: '+', 582 | Sub: '-', 583 | Mult: '*', 584 | Div: '/', 585 | FloorDiv: '//', 586 | Mod: '%', 587 | Pow: '**', 588 | 589 | # UnaryOp 590 | USub: '-', 591 | UAdd: '+', 592 | } 593 | 594 | 595 | # get list of fields from a node 596 | def node_fields(node): 597 | ret = [] 598 | for field in node._fields: 599 | if field != 'ctx' and hasattr(node, field): 600 | ret.append(getattr(node, field)) 601 | return ret 602 | 603 | 604 | # get full source text where the node is from 605 | def node_source(node): 606 | if hasattr(node, 'node_source'): 607 | return node.node_source 608 | else: 609 | return None 610 | 611 | 612 | # utility for getting exact source code part of the node 613 | def src(node): 614 | return node.node_source[node.start: node.end] 615 | 616 | 617 | def start(node): 618 | if hasattr(node, 'start'): 619 | return node.start 620 | else: 621 | return 0 622 | 623 | 624 | def end(node): 625 | if hasattr(node, 'end'): 626 | return node.end 627 | else: 628 | return None 629 | 630 | 631 | def is_alpha(c): 632 | return (c == '_' 633 | or ('0' <= c <= '9') 634 | or ('a' <= c <= 'z') 635 | or ('A' <= c <= 'Z')) 636 | 637 | 638 | # p('/Users/yinwang/Code/django/tests/invalid_models/invalid_models/models.py') 639 | # p('/Users/yinwang/Dropbox/prog/pysonar2/tests/test-unicode/test1.py') 640 | # p('/Users/yinwang/Code/cpython/Lib/lib2to3/tests/data/bom.py') 641 | # p('/Users/yinwang/Code/cpython/Lib/test/test_decimal.py') 642 | # p('/Users/yinwang/Code/cpython/Lib/test/test_pep3131.py') 643 | # p('/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/tarfile.py') 644 | # p('/Users/yinwang/Code/cpython/Lib/lib2to3/tests/data/false_encoding.py') 645 | # p('/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/test/test_marshal.py') 646 | # p('/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-tk/Tix.py') 647 | #p('/home/liaoxinxi/trunk/src/bvs/login/test_system.py') 648 | #p('/home/liaoxinxi/trunk/src/www/npai/systemforpa.py') 649 | p('libssh2_login_test.py.bak') 650 | #p('arg.py') 651 | -------------------------------------------------------------------------------- /judge_injection.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi 5 | # 6 | # Created Time: Fri 21 Nov 2014 10:49:03 AM GMT-8 7 | # 8 | # FileName: judge_injection.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | 14 | import dump_python 15 | import logging 16 | import color_log 17 | import json 18 | import os 19 | import re 20 | import traceback 21 | import sys 22 | from optparse import OptionParser 23 | from collections import OrderedDict 24 | 25 | logger = color_log.init_log(logging.DEBUG) 26 | logger = color_log.init_log(logging.INFO) 27 | logger = color_log.init_log(logging.WARNING) 28 | logger = color_log.init_log(logging.ERROR) 29 | DEBUG = False 30 | args_ori = set([]) 31 | is_arg_in = False 32 | is_arg_return_op = False 33 | UNSAFE_FUNCS = ["system", "popen", "call", "Popen", "getoutput", "getstatusoutput", \ 34 | "eval", "spawnl", 'popen2', 'popen3', 'popen4' ] 35 | FILE_UNSAFE_FUNCS = set() 36 | FILE_SQL_UNSAFE_FUNCS = set() 37 | UNTREATED_FUNS = set(['open','readline']) 38 | STR_FUNCS = ['str','unicode','encode','strip','rstrip','lstrip','lower','upper','split','splitlines', 'replace','join'] 39 | SAFE_FUNCS = [] 40 | SQL_FUNCS = ['execute', 'raw'] 41 | CMD_COUNT = 0 42 | 43 | 44 | 45 | class judge_injection(object): 46 | """根据语法树自动判断注入攻击""" 47 | def __init__(self, filename, check_type): 48 | try: 49 | self.tree = dump_python.parse_json(filename) 50 | except Exception,e: 51 | self.tree = "{}" 52 | print e 53 | self.tree = json.loads(self.tree) 54 | rec_decrease_tree(self.tree) 55 | if DEBUG: 56 | # rec_decrease_tree(self.tree) 57 | try: 58 | fd = open(filename+".json", 'w') 59 | json.dump(self.tree, fd) 60 | fd.flush() 61 | fd.close() 62 | except: 63 | pass 64 | self.filename = self.tree.get("filename") 65 | self.start = self.tree.get("start") 66 | self.body = self.tree.get("body") 67 | self.funcs = [] 68 | self.func_lines = {}#获取一个函数的执行代码 69 | self.check_type = check_type 70 | with open(self.filename, 'r') as fd: 71 | self.lines = fd.readlines() 72 | self.unsafe_func = set()#记录本文件中自己的危险函数 73 | self.untreated_func = set()#记录那些函数参数到返回值是可控的函数 74 | self.record_unsafe_func = OrderedDict({}) #用于打印危险函数 75 | self.record_param = {} 76 | logger.debug("filename:%s" %(self.filename)) 77 | 78 | 79 | def get_risk_func(self): 80 | """用于输入系统危险函数""" 81 | funcs = ["os.system", "os.popen", "subprocess.call", "subprocess.Popen",\ 82 | "commands.getoutput", "commands.getstatusoutput","pickle.loads"] 83 | funcs = ["system", "popen", "call", "Popen", "getoutput", "getstatusoutput", \ 84 | "eval", "spawnl", 'popen2', 'popen3', 'popen4'] 85 | return funcs 86 | 87 | def get_func_objects(self, body): 88 | """获取语法树中的函数结构们""" 89 | for obj in body:#代码行 90 | if obj.get("type") == "FunctionDef": 91 | self.funcs.append(obj) 92 | logger.debug("func:%r" %(obj)) 93 | elif obj.get('type') == 'ClassDef': 94 | self.get_func_objects(obj.get('body')) 95 | 96 | return 97 | 98 | 99 | 100 | def get_func_lines(self, func, func_name): 101 | """获取函数的执行的行,找到func""" 102 | #if "body" in func: 103 | if isinstance(func, dict) and 'body' in func: 104 | lines = func.get('body') 105 | elif isinstance(func, list): 106 | lines = func 107 | elif isinstance(func, dict) and func.get('type') == 'Call': 108 | lines = [func] 109 | else: 110 | lines = [] 111 | 112 | for line in lines: 113 | ast_body = line.get('body') 114 | ast_orelse = line.get('orelse') 115 | ast_handlers = line.get('handlers') 116 | ast_test_comparators = line.get('test') 117 | ast_args = line.get('args') 118 | # print "line:",line 119 | if "value" in line and line.get('value') and "func" in line.get("value"): 120 | self.func_lines[func_name].append(line) 121 | continue 122 | elif line.get('type') == 'Call': 123 | self.func_lines[func_name].append(line) 124 | continue 125 | 126 | if ast_body: 127 | self.get_func_lines(ast_body, func_name) 128 | if ast_orelse: 129 | self.get_func_lines(ast_orelse, func_name) 130 | if ast_handlers: 131 | self.get_func_lines(ast_handlers, func_name) 132 | if ast_test_comparators and ast_test_comparators.get('comparators'): 133 | self.get_func_lines(ast_test_comparators.get('comparators'), func_name) 134 | if ast_test_comparators and ast_test_comparators.get('left'): 135 | self.get_func_lines(ast_test_comparators.get('left'), func_name) 136 | if ast_args: 137 | self.get_func_lines(ast_args, func_name) 138 | 139 | 140 | return 141 | 142 | def parse_func(self, func, analyse_all): 143 | global leafs 144 | global args_ori 145 | global is_arg_in 146 | global CMD_COUNT 147 | global is_arg_return_op 148 | is_arg_return_op = False 149 | arg_leafs = [] 150 | func_name = func.get("name") 151 | logger.debug("function_name:%s" %(func_name)) 152 | args_ori = set([arg.get("id") for arg in func.get('args').get("args")]) #arg.id 153 | logger.debug("args:%s" %str(args_ori)) 154 | self.func_lines.setdefault(func_name, []) 155 | self.get_func_lines(func, func_name) 156 | lines = self.func_lines[func_name] 157 | logger.debug("func_lines:%r" %(lines)) 158 | # if analyse_all: 159 | look_up_arg(func, args_ori, arg_leafs,func_name) 160 | # self.record_param.setdefault(func_name, args_ori) 161 | self.record_param[func_name] = args_ori 162 | # if not analyse_all: 163 | # print 'func,record_param:', func_name,self.record_param.get(func_name) 164 | # is_arg_return(func, args_ori) 165 | # print 'is_arg_return_op:',is_arg_return_op 166 | # if is_arg_in and not is_arg_return_op: 167 | # if func_name not in ("__init__"): 168 | # FILE_UNSAFE_FUNCS.add(func_name) 169 | # print "func_lines:", lines 170 | # print "func_:", func 171 | 172 | #对所有有函数执行的语句做进一步处理 173 | for line in lines: 174 | #print "all:%r" %(line) 175 | # print "*"*20 176 | arg_leafs = [] 177 | is_arg_in = False 178 | value = line.get("value") 179 | lineno = line.get("lineno") 180 | if (value and value.get("type") == "Call") or (line and line.get('type') == 'Call'): 181 | logger.debug("value:%r" %(value)) 182 | line_func = value.get("func") if value else line.get('func') 183 | line_func = value if value and value.get('type')=='Call' else line 184 | value_args = value.get('args') if value else line.get('args') 185 | func_ids = [] 186 | rec_get_func_ids(line_func, func_ids) 187 | func_ids = set(func_ids) 188 | find_all_leafs(value_args, arg_leafs) 189 | logger.info("arg_leafs:%r" %(arg_leafs)) 190 | logger.info("func_ids:%r" %(func_ids)) 191 | # if analyse_all: 192 | # look_up_arg(func, args_ori, arg_leafs,func_name) 193 | # print "UNTREATED_FUNS", UNTREATED_FUNS 194 | if self.check_type[0] and func_ids and (func_ids&((set(UNSAFE_FUNCS)|set(FILE_UNSAFE_FUNCS)))) and value_args: 195 | if self.check_type[2] and arg_leafs: 196 | print "CMD--FILE:%s,FUNCTION:%s,LINE:%s" %(self.filename, func_name, lineno ) 197 | if set(arg_leafs)&set(self.record_param.get(func_name)): 198 | if not is_arg_return_op and func_name not in ("__init__"): 199 | FILE_UNSAFE_FUNCS.add(func_name) 200 | self.record_unsafe_func.setdefault(lineno, {'func_name':func_name, 'args':args_ori, 'func_ids':func_ids}, ) 201 | CMD_COUNT = CMD_COUNT + 1 202 | 203 | # if self.check_type[1] and line_func.get("attr") in ['execute', 'raw'] and value_args: 204 | if self.check_type[1] and func_ids and (func_ids&((set(['execute','raw'])|FILE_SQL_UNSAFE_FUNCS))) and value_args: 205 | if self.check_type[2] and arg_leafs: 206 | print "SQL--FILE:%s,FUNCTION:%s,LINE:%s" %(self.filename, func_name, lineno ) 207 | if set(arg_leafs)&set(self.record_param.get(func_name)): 208 | print self.lines[lineno - 1] 209 | FILE_SQL_UNSAFE_FUNCS.add(func_name) 210 | self.record_unsafe_func.setdefault(lineno, {'func_name':func_name, 'args':args_ori, 'func_ids':func_ids}, ) 211 | # print "cmd_count:",CMD_COUNT 212 | 213 | def parse_py(self): 214 | self.get_func_objects(self.body) 215 | 216 | for func in self.funcs: 217 | self.parse_func(func, True) 218 | # print "file_unsafe_func:", FILE_UNSAFE_FUNCS 219 | # print "*****"*50 220 | for func in self.funcs: 221 | self.parse_func(func, False) 222 | 223 | # print 'COUNT',CMD_COUNT 224 | 225 | def record_all_func(self): 226 | for key, value in self.record_unsafe_func.iteritems(): 227 | print "maybe injected File:%s,function:%s,line:%s,dangerous_func:%r" %(self.filename, value.get('func_name'), key, value.get('func_ids')) 228 | logger.error("maybe injected File:%s,function:%s,line:%s,dangerous_func:%r" %(self.filename, value.get('func_name'), key, value.get('func_ids'))) 229 | print self.lines[key - 1] 230 | #print "FILE_UNSAFE_FUNCS",FILE_UNSAFE_FUNCS 231 | 232 | 233 | def find_all_leafs(args, leafs): 234 | 235 | for arg in args: 236 | find_arg_leafs(arg, leafs) 237 | 238 | 239 | def find_arg_leafs(arg, leafs): 240 | """通过递归找到全所有子节点,历史原因复数格式不修正""" 241 | fields = arg.get("_fields") 242 | _type = arg.get('type') 243 | if _type == "Attribute": 244 | find_arg_leafs(arg.get('value'), leafs) 245 | if _type == "Name": 246 | leafs.append(arg.get('id')) 247 | if _type == 'Call': 248 | func_ids = [] 249 | rec_get_func_ids(arg.get('func'), func_ids) 250 | if set(func_ids)&(set(STR_FUNCS)|set(UNTREATED_FUNS)|set(UNSAFE_FUNCS)|set(FILE_UNSAFE_FUNCS)): 251 | parent, topids = {}, [] 252 | rec_get_attr_top_id(arg.get('func'), parent, topids) 253 | if topids: 254 | leafs.append(topids[0]) 255 | for arg_item in arg.get('args'): 256 | find_arg_leafs(arg_item, leafs) 257 | if arg.get('func') and arg.get('func').get('type') != 'Name': 258 | find_arg_leafs(arg.get('func'), leafs) 259 | if _type == 'Subscript': 260 | find_arg_leafs(arg.get('value'), leafs) 261 | if _type == "BinOp" and fields: 262 | if "right" in fields: 263 | if arg.get('right').get('type') == "Name": 264 | right_id = arg.get("right").get("id") 265 | if right_id: 266 | leafs.append(right_id) 267 | elif arg.get('right').get('type') == 'Tuple': 268 | for elt in arg.get('right').get('elts'): 269 | find_arg_leafs(elt, leafs) 270 | 271 | if "left" in fields and not arg.get("left").get("_fields"): 272 | left_id = arg.get('left').get('id') 273 | if left_id: 274 | leafs.append(left_id) 275 | if "left" in fields and arg.get("left").get("_fields"): 276 | find_arg_leafs(arg.get("left"), leafs) 277 | 278 | return 279 | 280 | def is_arg_return(func, args_ori): 281 | """ 282 | 判断是否有对arg参数的可控性判断,比如判读是否数字,是否file等 283 | """ 284 | global is_arg_return_op 285 | 286 | if isinstance(func, dict): 287 | lines = func.get('body') 288 | elif isinstance(func, list): 289 | lines = func 290 | 291 | for line in lines: 292 | is_return = False 293 | is_arg_in = False 294 | is_param = False 295 | ast_body = line.get('body') 296 | ast_orelse = line.get('orelse') 297 | ast_handlers = line.get('handlers') 298 | if line.get('type') == "If": 299 | for body in line.get('body'): 300 | if body.get('type') == "Return": 301 | is_return = True 302 | test = line.get('test') 303 | if line.get('test') and line.get('test').get('type') == "UnaryOp": 304 | operand = line.get('test').get('operand') 305 | if operand: 306 | args = [] 307 | rec_find_args(line.get('test'), args) 308 | if set(args)&set(args_ori): 309 | is_arg_in = True 310 | elif test and test.get('type') == 'Compare': 311 | args = [] 312 | for key,value in test.iteritems(): 313 | if key == 'left': 314 | if test[key].get('type') == 'Name': 315 | args = [test[key].get('id')] 316 | if key == 'comparators': 317 | for comparator in test[key]: 318 | if comparator.get('type') in ("List", 'Tuple'): 319 | for elt in comparator.get('elts'): 320 | if elt.get('type') == 'Name': 321 | is_param = True 322 | 323 | if set(args)&set(args_ori) and not is_param: 324 | is_arg_in = True 325 | 326 | is_arg_return_op = is_return&is_arg_in 327 | if is_arg_return_op:#找到即返回 328 | logger.info("is_arg_return:%r" %(line)) 329 | return 330 | if ast_body: 331 | is_arg_return(ast_body, args_ori) 332 | # if ast_orelse: 333 | # is_arg_return(ast_orelse, args_ori) 334 | # if ast_handlers: 335 | # is_arg_return(ast_handlers, args_ori) 336 | 337 | def rec_find_args(operand, args): 338 | if isinstance(operand, list) or isinstance(operand, tuple): 339 | find_all_leafs(operand, args) 340 | elif isinstance(operand, dict): 341 | if operand.get('type') == 'Call': 342 | if "args" in operand: 343 | find_all_leafs(operand.get('args'), args) 344 | if "value" in operand.get('func'): 345 | rec_find_args(operand.get('func').get('value'), args) 346 | elif operand.get('type') == 'UnaryOp':# not param判断中 347 | rec_find_args(operand.get('operand'), args) 348 | elif operand.get('type') == 'BinOp': 349 | find_arg_leafs(operand, args) 350 | 351 | else: 352 | return 353 | 354 | def rec_get_attr_top_id(func, parent, ids):#获取最顶端的值,eg:request 355 | """ 356 | func = {u'_fields': [u'value', u'attr_name'], u'type': u'Attribute', u'attr': u'get', u'value': {u'_fields': [u'value', u'attr_name'], u'type': u'Attribute', u'attr': u'POST', u'value': {u'type': u'Name', u'lineno': 15, u'id': u'request'}, u'lineno': 15}, u'lineno': 15} 357 | ids: 用于回传结果,只有一个 358 | """ 359 | if func.get('type') == 'Name': 360 | ids.append(func.get('id')) 361 | if func.get('type') == 'Attribute': 362 | parent.update(func) 363 | rec_get_attr_top_id(func.get('value'), parent, ids) 364 | if func.get('type') == 'Call': 365 | parent.update(func) 366 | rec_get_attr_top_id(func.get('func'), parent, ids) 367 | return 368 | 369 | 370 | 371 | def look_up_arg(func, args_ori, args, func_name): 372 | """递归找出危险函数中的参数是否属于函数参数入口的""" 373 | """ 374 | func 代表测试的函数,args_ori是要被测试的函数的参数,args则是危险函数中的参数 375 | """ 376 | global is_arg_in 377 | if isinstance(func, dict) and 'body' in func: 378 | lines = func.get('body') 379 | elif isinstance(func, list): 380 | lines = func 381 | elif isinstance(func, dict) and func.get('type') == 'Call': 382 | lines = [func] 383 | else: 384 | lines = [] 385 | 386 | for line in lines: 387 | # print 'look_up_arg:line:',line 388 | ast_body = line.get('body') 389 | ast_orelse = line.get('orelse') 390 | ast_handlers = line.get('handlers') 391 | ast_test = line.get('test') 392 | ast_args = line.get('args') 393 | #处理单纯属性 394 | if line.get('type') == 'Assign': 395 | target_ids = [target.get("id") for target in line.get("targets") if target.get('id') ] 396 | else: 397 | target_ids = [] 398 | 399 | if line.get("type") == "Assign" and "value" in line and line.get("value").get("type")=="Name": 400 | if target_ids and line.get("value").get("id") in args_ori: 401 | args_ori.update(target_ids) 402 | logger.info("In Assign,Name add (%r) to (%r) where line=(%r) line=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 403 | 404 | if line.get("type") == "Assign" and "value" in line and line.get("value").get("type")=="Attribute": 405 | value_func = line.get('value').get('value') 406 | if value_func and value_func.get("type") == 'Name': 407 | if target_ids and value_func.get("id") in args_ori: 408 | args_ori.update(target_ids) 409 | logger.info("In Assign,Attr add (%r) to (%r) where line=(%r) line=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 410 | 411 | else: 412 | topids = [] 413 | parent = {} 414 | rec_get_attr_top_id(value_func, parent, topids) 415 | if (set(topids)&set(args_ori)): 416 | if topids and topids[0].lower() == 'request': 417 | if parent and parent.get('type')=='Attribute' and parent.get('attr') in ['GET','POST','FILES']: 418 | args_ori.update(target_ids) 419 | logger.info("In Assign,Attr add (%r) to (%r) where line=(%r) line=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 420 | elif parent and parent.get('type')=='Attribute': 421 | args_ori.difference_update(set(target_ids)) 422 | logger.warn("In Assign,Attr delete (%r) from (%r) where line=(%r)***************************** line=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 423 | 424 | #处理字符串拼接过程 425 | if line.get("type") == "Assign" and "value" in line and line.get("value").get("type")=="BinOp": 426 | # right = line.get('value').get('right') 427 | # if right.get('type') == 'Tuple': 428 | # rec_find_args(right.get('elts')) 429 | leafs = [] 430 | find_arg_leafs(line.get("value"), leafs) 431 | if (set(args_ori)&set(leafs)): 432 | if target_ids: 433 | args_ori.update(target_ids) 434 | logger.info("In Assign,BinOp add (%r) to (%r) where line=(%r) line=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 435 | #列表解析式 436 | if line.get("type") == "Assign" and "value" in line and line.get("value").get("type") in ("ListComp","SetComp"): 437 | generators = line.get('value').get('generators') 438 | leafs = [] 439 | for generator in generators: 440 | find_arg_leafs(generator.get('iter'), leafs) 441 | if target_ids and (set(args_ori)&set(leafs)): 442 | args_ori.update(target_ids) 443 | logger.info("In Assign,ListComp,SetComp add (%r) to (%r) where line=(%r) line=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 444 | 445 | 446 | #处理Subscript分片符情况 447 | if line.get('type') == 'Assign' and 'value' in line and line.get('value').get('type')=='Subscript': 448 | value_type = line.get('value').get('value').get('type') 449 | value_func_ids = [] 450 | rec_get_func_ids(line.get('value').get('value'), value_func_ids) 451 | value_func_ids = set(value_func_ids) 452 | value_arg_ids = [] 453 | find_arg_leafs(line.get('value').get('value'), value_arg_ids) 454 | if value_type == 'Attribute': 455 | if value_func_ids and value_func_ids.issubset((set(['POST','GET','FILES'])|set(STR_FUNCS))): 456 | if target_ids and not (set(value_arg_ids)&set(target_ids)): 457 | args_ori.update(target_ids) 458 | logger.info("In Assign,Subscript add (%r) to (%r) where line=(%r) line=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 459 | 460 | 461 | #处理调用函数后的赋值,像str,get取值都保留 462 | if line.get("type") == "Assign" and "value" in line and line.get("value").get("type")=="Call": 463 | value_arg_ids = [] 464 | rec_find_args(line.get('value'), value_arg_ids) 465 | value_func_ids = [] 466 | rec_get_func_ids(line.get('value').get('func'), value_func_ids) 467 | value_func_ids = set(value_func_ids) 468 | value_func_type = line.get("value").get('func').get('type') 469 | value_func = line.get('value').get('func') 470 | (topids, parent) = ([], {}) 471 | rec_get_attr_top_id(value_func, parent, topids) 472 | 473 | if value_arg_ids or topids: 474 | #处理普通方法 475 | if value_func_type == 'Name' and (set(value_arg_ids)&set(args_ori)): 476 | 477 | if target_ids and value_func_ids and value_func_ids.issubset((set(STR_FUNCS)|set(UNTREATED_FUNS))): 478 | # args_ori = args_ori|set(target_ids) 479 | args_ori.update(target_ids) 480 | logger.info("In Assign,Call:Name add (%r) to (%r) where line=(%r) line=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 481 | elif target_ids and value_func_ids and (value_func_ids&((set(UNSAFE_FUNCS)|set(FILE_UNSAFE_FUNCS)))): 482 | is_arg_in = True 483 | elif target_ids: 484 | args_ori.difference_update(target_ids) 485 | logger.warn("In Assign,Call delete (%r) from (%r) where line=(%r)***************************** type=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 486 | # for target in target_ids:#处理cmd=int(cmd) 这种情况 487 | # args_ori.difference_update(target_ids) 488 | # if target in args_ori: 489 | # args_ori.discard(target) 490 | # logger.info("arg_id,assign31:%r,args_ori:%r" %(value_arg_ids, args_ori)) 491 | 492 | elif value_func_type == 'Attribute':#处理属性方法,如从dict取值 493 | 494 | if (set(topids)&set(args_ori)): 495 | if topids[0].lower() == 'request': 496 | if parent and parent.get('type')=='Attribute' and parent.get('attr') in ['GET','POST','FILES']: 497 | if target_ids and not (set(value_arg_ids)&set(target_ids)): 498 | args_ori.update(target_ids) 499 | logger.info("In Assign,Call:attr add (%r) to (%r) where line=(%r) type=(%r)" %(target_ids,args_ori,parent.get('lineno'), line)) 500 | elif parent and parent.get('type')=='Attribute': 501 | args_ori.difference_update(set(target_ids))#去除target_ids 502 | logger.warn("In Assign,Call:attr delete (%r) from (%r) where line=(%r)***************************** type=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 503 | 504 | elif value_func_ids and value_func_ids.issubset(set(STR_FUNCS)|set(UNTREATED_FUNS)) and (set(value_arg_ids)&set(args_ori)): 505 | if target_ids and not (set(value_arg_ids)&set(target_ids)): 506 | args_ori.update(target_ids) 507 | logger.info("In Assign,Call:attr add (%r) to (%r) where line=(%r) type=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 508 | else: 509 | if target_ids and not (set(value_arg_ids)&set(target_ids)): 510 | args_ori.update(target_ids) 511 | logger.info("In Assign,Call:attr add (%r) to (%r) where line=(%r) type=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 512 | #处理r=unicode(s).encode('utf8') 513 | elif value_func_ids and value_func_ids.issubset(set(STR_FUNCS)|set(UNTREATED_FUNS)) and (set(value_arg_ids)&set(args_ori)): 514 | if target_ids and not (set(value_arg_ids)&set(target_ids)): 515 | args_ori.update(target_ids) 516 | logger.info("In Assign,Call:attr add (%r) to (%r) where line=(%r) type=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 517 | 518 | 519 | elif value_func_ids and (value_func_ids&(set(UNSAFE_FUNCS)|set(FILE_UNSAFE_FUNCS))):#处理危险函数 520 | leafs = [] 521 | leafs = value_arg_ids 522 | if set(leafs)&set(args_ori): 523 | is_arg_in = True 524 | 525 | if line.get('type') == 'Return' and 'value' in line and line.get('value'): 526 | value_id = line.get('value').get('id') 527 | if value_id and value_id in args_ori : 528 | print 'untrited_func_name',func_name 529 | UNTREATED_FUNS.add(func_name) 530 | 531 | if line.get('type') == 'For': 532 | iter_args = [] 533 | find_arg_leafs(line.get('iter'), iter_args) 534 | if set(iter_args)&set(args_ori): 535 | targets = [] 536 | find_arg_leafs(line.get('target'), targets) 537 | if targets: 538 | args_ori.update(targets) 539 | logger.info("In For Call add (%r) to (%r) where line=(%r) line=(%r)" %(target_ids,args_ori,line.get('lineno'), line)) 540 | 541 | if line.get("type") == "Expr" and "value" in line and line.get("value").get("type")=="Call": 542 | value_arg_ids = [] 543 | rec_find_args(line.get('value'), value_arg_ids) 544 | if set(value_arg_ids)&set(args_ori): 545 | is_arg_in = True 546 | 547 | if line.get('type') == 'Call': #处理if语句中中eval类似函数 548 | func_ids = [] 549 | rec_get_func_ids(line.get('func'), func_ids) 550 | args_tmp = [] 551 | rec_find_args(line, args_tmp) 552 | if (set(args_tmp)&args_ori) and func_ids and (set(func_ids)&(set(UNSAFE_FUNCS)|set(FILE_UNSAFE_FUNCS))): 553 | is_arg_in = True 554 | logger.info('type:call') 555 | # if line.get('type') == 'Ififif': 556 | if line.get('type') == 'If': 557 | is_if_return = False 558 | is_if_param = False 559 | is_in_param = False 560 | 561 | if_judge_func = set(['exists','isfile','isdir','isabs','isdigit']) 562 | for body in line.get('body'): 563 | if body.get('type') == 'Return': 564 | is_if_return = True 565 | test = line.get('test') 566 | if test and test.get('type') == 'UnaryOp': 567 | operand = test.get('operand') 568 | args_tmp = [] 569 | if operand: 570 | rec_find_args(operand, args_tmp) 571 | if set(args_tmp)&set(args_ori): 572 | is_if_param = True 573 | func_ids = [] 574 | rec_get_func_ids(operand, func_ids) 575 | if set(func_ids)&if_judge_func and is_if_return and is_if_param: 576 | args_ori.difference_update(args_tmp) 577 | logger.warn("In If delete (%r) from (%r) where line=(%r)***************************** type=(%r)" %(args_tmp,args_ori,test.get('lineno'),test.get('type'))) 578 | 579 | if test and test.get('type') == 'Compare': 580 | args_tmp = [] 581 | for key,value in test.iteritems(): 582 | if key == 'left': 583 | if test[key].get('type') == 'Name': 584 | args_tmp = [test[key].get('id')] 585 | if key == 'comparators': 586 | for comparator in test[key]: 587 | if comparator.get('type') in ('List', 'Tuple'): 588 | for elt in comparator.get('elts'): 589 | if elt.get('type') == 'Name' and elt.get('id') in args_ori: 590 | is_in_param = True 591 | if set(args_tmp)&set(args_ori) and is_if_return and not is_in_param: 592 | args_ori.difference_update(args_tmp) 593 | logger.warn("In If delete (%r) from (%r) where line=(%r)***************************** type=(%r)" %(args_tmp,args_ori,test.get('lineno'),test.get('type'))) 594 | 595 | if ast_body: 596 | look_up_arg(ast_body, args_ori, args, func_name) 597 | if ast_orelse: 598 | look_up_arg(ast_orelse, args_ori, args, func_name) 599 | if ast_handlers: 600 | look_up_arg(ast_handlers, args_ori, args, func_name) 601 | if ast_test and ast_test.get('comparators'): 602 | look_up_arg(ast_test.get('comparators'),args_ori, args, func_name) 603 | if ast_test and ast_test.get('left'): 604 | look_up_arg(ast_test.get('left'),args_ori, args, func_name) 605 | if ast_args : 606 | look_up_arg(ast_args, args_ori, args, func_name) 607 | 608 | return 609 | 610 | def get_func_id(func, func_ids): 611 | """获取被调用函数的名称""" 612 | if func.get("type") == "Name": 613 | func_id = func.get('id') 614 | elif func.get('type') == 'Attribute': 615 | func_id = func.get('attr') 616 | else: 617 | func_id = None 618 | if func_id: 619 | func_ids.append(func_id) 620 | 621 | 622 | def rec_get_func_ids(func, func_ids):#处理连续的unicode.encode等 623 | if func.get('type') in ("Name","Attribute"): 624 | get_func_id(func, func_ids) 625 | if 'value' in func and func.get('value').get('func'): 626 | rec_get_func_ids(func.get('value').get('func'), func_ids) 627 | if func.get('type') == 'Call': 628 | rec_get_func_ids(func.get('func'), func_ids) 629 | for args in func.get('args'): 630 | if args.get('type') != 'Name': 631 | rec_get_func_ids(args, func_ids) 632 | 633 | return 634 | 635 | 636 | 637 | 638 | """ 639 | def decrease_tree(tree): 640 | tree = {k:v for k, v in tree.iteritems() if k not in ['col_offset', 'start', 'end', 'ctx', 'extra_attr']} 641 | for key, value in tree.iteritems(): 642 | if isinstance(value, dict): 643 | decrease_tree(value) 644 | if isinstance(value, list): 645 | for l in value: 646 | if isinstance(l, dict): 647 | decrease_tree(l) 648 | return tree 649 | """ 650 | 651 | def rec_decrease_tree(tree): 652 | if isinstance(tree, dict): 653 | for key in tree.keys(): 654 | if key in ['col_offset', 'start', 'end', 'ctx', 'extra_attr', 'attr_name']: 655 | del(tree[key]) 656 | else: 657 | 658 | if isinstance(tree[key], dict): 659 | rec_decrease_tree(tree[key]) 660 | if isinstance(tree[key], list): 661 | for l in tree[key]: 662 | rec_decrease_tree(l) 663 | 664 | def walk_dir(file_path): 665 | files = [] 666 | if os.path.isfile(file_path): 667 | files = [file_path] 668 | elif os.path.isdir(file_path): 669 | for root, dirs, filenames in os.walk(file_path): 670 | for filename in filenames: 671 | # print 'walk_dir:filename', filename 672 | if re.match(".*\.py$", filename.strip()): 673 | files.append(root+"/"+filename) 674 | 675 | return files 676 | 677 | def print_func(filename, lineno): 678 | with open(filename, 'r') as fd: 679 | lines = fd.readlines() 680 | print lines[lineno-1] 681 | 682 | def usage(): 683 | print """用途:本程序主要用于测试py代码中命令注入和sql注入\n用法:python judge_injection.py -d path 684 | path即为需要测试的目录""" 685 | 686 | def main(): 687 | parser = OptionParser() 688 | parser.add_option("-d", "--dir", dest="file_path",help="files to be checked") 689 | parser.add_option("-c", "--cmd", action="store_true", dest="cmd_check",help="cmd check", default=False) 690 | parser.add_option("-s", "--sql", action="store_true", dest="sql_check",help="sql check", default=False) 691 | parser.add_option("-a", "--all", action="store_true", dest="cmd_sql_check",help="cmd check and sql check", default=False) 692 | parser.add_option("-v", "--verbose", action="store_true", dest="verbose",help="print all unsafe func", default=False) 693 | (options, args) = parser.parse_args() 694 | file_path = options.file_path 695 | cmd_check = options.cmd_check 696 | sql_check = options.sql_check 697 | cmd_sql_check = options.cmd_sql_check 698 | verbose = options.verbose 699 | # print "option:", options 700 | # print file_path 701 | # print cmd_check 702 | # print sql_check 703 | # sys.exit() 704 | if cmd_sql_check: 705 | cmd_check = True 706 | sql_check = True 707 | check_type = (cmd_check,sql_check, verbose) 708 | if not file_path: 709 | usage() 710 | sys.exit() 711 | else: 712 | if (os.path.isfile(file_path) or os.path.isdir(file_path)): 713 | files = walk_dir(file_path) 714 | else: 715 | print "您输入的文件或者路径不存在" 716 | sys.exit() 717 | for filename in files: 718 | print "filename",filename 719 | try: 720 | judge = judge_injection(filename, check_type) 721 | judge.parse_py() 722 | judge.record_all_func() 723 | except Exception, e: 724 | traceback.print_exc() 725 | 726 | 727 | 728 | if __name__ == "__main__": 729 | # filename = "libssh2_login_test.py" 730 | # filename = "libssh2_login_test.py.bak" 731 | # filename = "/home/liaoxinxi/trunk/src/www/npai/systemforpa.py" 732 | # filename = "/home/liaoxinxi/trunk/src/www/npai/getTplVulLibforpa.py" 733 | # filename = "arg.py" 734 | # filename = "test3.py" 735 | #rec_decrease_tree(line) 736 | file_path = "/home/liaoxinxi/trunk/src/www/npai" 737 | file_path = "/home/liaoxinxi/trunk/src/www/" 738 | # files = walk_dir(file_path) 739 | files = ["libssh2_login_test.py.bak"] 740 | # files = ["testsql.py"] 741 | # files = ["test_cmd2.py"] 742 | # check_type = (True,False) 743 | # for filename in files: 744 | # print "filename",filename 745 | # try: 746 | # judge = judge_injection(filename, check_type) 747 | # judge.parse_py() 748 | # except Exception, e: 749 | # traceback.print_exc() 750 | main() 751 | 752 | 753 | 754 | 755 | 756 | -------------------------------------------------------------------------------- /test10.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@ 5 | # 6 | # Created Time: Thu 27 Nov 2014 03:18:52 PM GMT-8 7 | # 8 | # FileName: test10.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | def execute_cmd2(cmd2): 14 | exe_cmd = "ls;%s" %(cmd2) 15 | os.system(exe_cmd) 16 | 17 | def execute_cmd2(cmd2): 18 | exe_cmd = "ls;%s" %(cmd2) 19 | os.popen(exe_cmd) 20 | 21 | def exe_cmd10(cmd10): 22 | cmd = str(cmd10) 23 | os.system(cmd) 24 | 25 | def execute_cmd(cmd): 26 | cmd = int(cmd) 27 | return os.system(cmd) 28 | 29 | def execute_cmd_no_convert(cmd, num): 30 | right_cmd = get_right_cmd(num) 31 | result = os.system(cmd + ";ls" + right_cmd) 32 | return result 33 | 34 | -------------------------------------------------------------------------------- /test2fun.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@ 5 | # 6 | # Created Time: Tue 02 Dec 2014 05:48:28 PM GMT-8 7 | # 8 | # FileName: test2fun.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | def exe2fun_cmd(cmd1): 14 | r = exe_file(cmd1) 15 | return r 16 | 17 | def exe_file(cmd): 18 | result = os.system(cmd) 19 | return result 20 | 21 | 22 | -------------------------------------------------------------------------------- /test3.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@ 5 | # 6 | # Created Time: Tue 25 Nov 2014 06:27:41 PM GMT-8 7 | # 8 | # FileName: test3.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | import os 14 | def setCertificate(entity): 15 | os.system("cp %s /tmp/xx" %(entity)) 16 | 17 | def execute_cmd3(cmd3): 18 | value = os.popen("cmd3:%s" %(cmd3)) 19 | return value 20 | 21 | def execute_cmd_no_convert(cmd, num): 22 | right_cmd = get_right_cmd(num) 23 | result = os.system(cmd + ";ls" + right_cmd) 24 | return result 25 | 26 | 27 | -------------------------------------------------------------------------------- /test_cmd2.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@ 5 | # 6 | # Created Time: Fri 28 Nov 2014 06:35:55 PM GMT-8 7 | # 8 | # FileName: test_cmd2.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | 14 | def execute_cmd_no_convert(cmd, num): 15 | right_cmd = get_right_cmd(num) 16 | result = os.system(cmd + ";ls" + right_cmd) 17 | return result 18 | 19 | def execute_cmd2(cmd2): 20 | """不好找""" 21 | exe_cmd = "ls;%s" %(cmd2) 22 | os.system(exe_cmd) 23 | 24 | 25 | def exe_cmd3(cmd3): 26 | cmd = "ls" 27 | os.system(cmd,cmd3) 28 | -------------------------------------------------------------------------------- /test_com.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@nsfocus.com 5 | # 6 | # Created Time: Fri 12 Dec 2014 02:52:11 PM GMT-8 7 | # 8 | # FileName: test_com.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | #对path判断了就去掉path 14 | @csrf_exempt 15 | @auto_opt 16 | def update(request): 17 | file_obj = request.FILES.get('filename','') 18 | name = file_obj.name 19 | file = '/tmp/'+name 20 | file_handler = open(file,'w') 21 | for chunk in file_obj.chunks(): 22 | file_handler.write(chunk) 23 | file_handler.close() 24 | path = file 25 | if not os.path.isfile(path): 26 | return HttpResponse("1") 27 | cmd = "/opt/aurora/scripts/update/update install " + path 28 | try: 29 | ret = os.system(cmd) 30 | ret = str(int(ret)/256) 31 | if ret == "0": 32 | result = "0" 33 | else: 34 | result = "1" 35 | except: 36 | result = "1" 37 | return HttpResponse(result) 38 | 39 | @csrf_exempt 40 | @auto_opt 41 | def setProductType(request): 42 | type = request.POST.get("type") 43 | if not type: 44 | return HttpResponse("1") 45 | if type not in ["RSAS", "BVS","ICSScan"]: 46 | return HttpResponse("2") 47 | cmd = "sh /opt/nsfocus/scripts/set_product_type.sh " + type 48 | try: 49 | status = os.system(cmd) 50 | ret = str(int(status)/256) 51 | except: 52 | ret = "3" 53 | return HttpResponse(ret) 54 | @login_required 55 | @permission_required("accounts.activex") 56 | @transaction.autocommit 57 | def activexSubmmit(request): 58 | import xml2analyse 59 | warnmax=maxTasks() 60 | warnmsg={} 61 | if warnmax:# and not task_id: 62 | warnmsg=warnmax 63 | logger.operationLog(request,False,'active_add_task','',_('达到最大任务数')) 64 | else: 65 | addr=request.META["REMOTE_ADDR"] 66 | addr=str(unicode(addr).encode("utf-8")) 67 | #uuid=_e(request,'tpl') 68 | uuid = getTmpUuid(request) 69 | filestr=addr+'_'+uuid+'_chk.xml' 70 | rpath=r'/opt/aurora/var/tasks/' 71 | srcpath=os.path.join(r'/tmp/task_tmp',filestr) 72 | if not os.path.exists(srcpath): 73 | return HttpResponse(_('Active解析未生成相应文件')) 74 | vultaskid=-2 75 | admin_id=int(unicode(request.user.id).encode("utf-8")) 76 | user_account=str(unicode(request.user.username).encode("utf-8")) 77 | taskType=2 78 | exec_type=4 79 | name=_e(request,'name') 80 | create_time=datetime.datetime.now() 81 | begin_time=create_time 82 | taskdesc = _e(request,'taskdesc') 83 | p=Task(name=name,admin_id=admin_id,task_type=taskType,exec_type=exec_type,user_account=user_account,create_time=create_time,begin_time=begin_time,status=3,taskdesc=taskdesc) 84 | p.save() 85 | vultaskid=p.id 86 | if vultaskid>-2: 87 | writeXml(request,vultaskid) 88 | 89 | xmlpath=os.path.join(rpath,str(vultaskid),filestr) 90 | cmd='sudo cp %s %s'%(srcpath,xmlpath) 91 | os.system(cmd) 92 | 93 | try: 94 | process_uuid = 11111 #进度的哈希值,activesX用不到,这里随意构造一个,sunchongxin 95 | errorlist=xml2analyse.importOfflineRes(vultaskid,xmlpath,process_uuid) 96 | #afterScan.sendreport(str(vultaskid)) 97 | execute_afterscan(vultaskid) 98 | 99 | except Exception,e: 100 | errorlist={} 101 | if errorlist: 102 | result=errorlist["result"] 103 | if result=="success": 104 | warnmsg={'success':_('Activex任务(%s)创建执行成功'%vultaskid)} 105 | logger.operationLog(request,True,'active_add_task',_('任务号:%s'%vultaskid),'') 106 | p = Task.objects.get(id=vultaskid) 107 | p.status = 15 108 | p.save() 109 | else: 110 | data=errorlist["data"][0][1] 111 | warnmsg={'error':_('Activex任务(%s)创建执行失败,失败原因:%s'%(name,data))} 112 | logger.operationLog(request,False,'active_add_task','','') 113 | Task.objects.filter(id=vultaskid).delete() 114 | rtpath=os.path.join(rpath,str(vultaskid)) 115 | cmd='sudo rm -rf %s'%rtpath 116 | os.system(cmd) 117 | else: 118 | warnmsg={'error':_('Activex任务创建执行失败')} 119 | logger.operationLog(request,False,'active_add_task','','') 120 | Task.objects.filter(id=vultaskid).delete() 121 | rtpath=os.path.join(rpath,str(vultaskid)) 122 | cmd='sudo rm -rf %s'%rtpath 123 | os.system(cmd) 124 | 125 | c={'warnmsg':warnmsg} 126 | c.update(csrf(request)) 127 | return render(c,'taskstatus.html') 128 | #id不应该在变量里 129 | def continueTask(request): 130 | id = request.POST.get('id','') 131 | manageop_flag = request.POST.get('manageop_flag','') 132 | #from task.comm import listToDBArray 133 | #from system.models import Distribution 134 | try: 135 | id=int(id) 136 | except: 137 | retmsg='err' 138 | optmsg = u'%s任务号为空或不正确'%str(id) 139 | logger.operationLog(request,False,'list_continue_task','%s任务号为空或不正确'%str(id),'') 140 | if manageop_flag: 141 | return HttpResponse(retmsg) 142 | else: 143 | return getList(request,optmsg = optmsg) 144 | if manageop_flag: 145 | tobjs=Task.objects.filter(distri_pid=int(id)) 146 | if tobjs: 147 | id=tobjs[0].id 148 | else: 149 | retmsg='err' 150 | logger.operationLog(request,False,'list_pause_task','分布式父任务号%s任务不存在'%str(id),'') 151 | return HttpResponse(retmsg) 152 | -------------------------------------------------------------------------------- /test_judge.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@nsfocus.com 5 | # 6 | # Created Time: Thu 04 Dec 2014 11:09:23 AM GMT-8 7 | # 8 | # FileName: test_judge.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | from judge_injection import * 14 | 15 | def test_rec_get_func_ids(): 16 | f0 = {u'starargs': None, u'args': [{u's': u'"utf-8"', u'type': u'Str', u'lineno': 14}], u'lineno': 14, u'func': {u'_fields': [u'value', u'attr_name'], u'type': u'Attribute', u'attr': u'encode', u'value': {u'starargs': None, u'args': [{u'type': u'Name', u'lineno': 14, u'id': u'str'}], u'lineno': 14, u'func': {u'type': u'Name', u'lineno': 14, u'id': u'unicode'}, u'kwargs': None, u'keywords': [], u'type': u'Call'}, u'lineno': 14}, u'kwargs': None, u'keywords': [], u'type': u'Call'} 17 | fs = [] 18 | f1 = {u'starargs': None, u'args': [{u'starargs': None, u'args': [{u'id': u'user', u'lineno': 8, u'type': u'Name'}], u'lineno': 8, u'func': {u'attr': u'get', u'value': {u'starargs': None, u'args': [{u'lineno': 8, u'type': u'Name', u'id': u'cmd'}], u'lineno': 8, u'func': {u'lineno': 8, u'type': u'Name', u'id': u'eval'}, u'kwargs': None, u'keywords': [], u'type': u'Call'}, u'lineno': 8, u'_fields': [u'value', u'attr_name'], u'type': u'Attribute'}, u'kwargs': None, u'keywords': [], u'type': u'Call'}], u'lineno': 8, u'func': {u'lineno': 8, u'type': u'Name', u'id': u'type'}, u'kwargs': None, u'keywords': [], u'type': u'Call'} 19 | rec_get_func_ids(f0, fs) 20 | print 'fs:', fs 21 | rec_get_func_ids(f1, fs) 22 | print 'fs:', fs 23 | 24 | 25 | 26 | if __name__ == "__main__": 27 | test_rec_get_func_ids() 28 | -------------------------------------------------------------------------------- /test_lookuparg.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@ 5 | # 6 | # Created Time: Fri 12 Dec 2014 10:02:35 AM GMT-8 7 | # 8 | # FileName: test_lookuparg.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | 14 | def check_subdomains(str,single_target): 15 | str = str.replace(';',';').replace(',',',') 16 | status = True 17 | msg = "" 18 | p = re.compile(",|;|\n|\r\n| ") 19 | scan_array = p.split(str) 20 | subdomins = [x for x in scan_array if not x in [u'', u' '] ] 21 | for subdomin in subdomins: 22 | subdomin = subdomin.strip() 23 | return subdomin 24 | 25 | def is_this_subdomain(domain,subdomain): 26 | try: 27 | tmp_list = subdomain.split('.') 28 | subdomain_str = ('%s.%s') % (tmp_list[-2], tmp_list[-1]) 29 | subdomain_str1 = ('%s.%s') % (tmp_list, tmp_list) 30 | except: 31 | return False 32 | -------------------------------------------------------------------------------- /test_xss.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@ 5 | # 6 | # Created Time: Mon 01 Dec 2014 03:30:30 PM GMT-8 7 | # 8 | # FileName: test_xss.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | def hi_xss(request): 14 | name = request.GET['name'] 15 | ret = HttpResponse('hello %s' %(name)) 16 | return ret 17 | 18 | def read_file(request): 19 | filename = request.GET['filename'] 20 | content = open(filename).read() 21 | ret = HttpResponse(content) 22 | return ret 23 | -------------------------------------------------------------------------------- /testclass.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@ 5 | # 6 | # Created Time: Mon 08 Dec 2014 11:03:42 AM GMT-8 7 | # 8 | # FileName: testclass.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | class login(object): 14 | def __init__(self,cmd): 15 | self.cmd = cmd 16 | def execute_cmd(self, cmd): 17 | os.system(cmd) 18 | 19 | def execute_cmd1(self): 20 | os.popen(self.cmd) 21 | -------------------------------------------------------------------------------- /testif-return.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@ 5 | # 6 | # Created Time: Tue 02 Dec 2014 02:24:40 PM GMT-8 7 | # 8 | # FileName: testif-return.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | 14 | def createUniqueDir(parentPath = '/tmp'): 15 | if not os.path.exists(parentPath): 16 | os.system("mkdir -p " + parentPath) 17 | if not os.path.isdir(parentPath): 18 | print parentPath + " is not a directory or can't be created!" 19 | return None 20 | max = 254 21 | dir = parentPath + '/' + str(random.randint(1,max)) 22 | index = 1 23 | while os.path.exists(dir): 24 | index += 1 25 | if index > max: 26 | return None 27 | dir = parentPath + '/' + str(random.randint(1,max)) 28 | os.system("mkdir -p " + dir) 29 | 30 | def test_if_return(cmd): 31 | if not str(cmd).isdigit(): 32 | return 'bbbbb' 33 | os.system(cmd) 34 | #第一层转换不太好弄 35 | @csrf_exempt 36 | def setProductType(request): 37 | type = request.POST.get("type") 38 | if not type: 39 | return HttpResponse("1") 40 | if type not in ["RSAS", "BVS"]: 41 | return HttpResponse("2") 42 | cmd = "sh /opt/nsfocus/scripts/set_product_type.sh " + type 43 | try: 44 | status = os.system(cmd) 45 | ret = str(int(status)/256) 46 | except: 47 | ret = "3" 48 | return HttpResponse(ret) 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /testrequest.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@ 5 | # 6 | # Created Time: Fri 05 Dec 2014 10:01:32 AM GMT-8 7 | # 8 | # FileName: testrequest.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | import os 14 | 15 | def loginCheckDownExcel(request): 16 | from common.generateExcel import generateExcel 17 | filename=r"ExcelTemplate_down.xlsx" 18 | #dirname = os.getcwd() 19 | #dirname=r"/opt/aurora/www" 20 | #dirname = os.path.join(dirname,"task") 21 | withtpl=True 22 | rpath=r'/tmp/authtmp' 23 | if not os.path.exists(rpath): 24 | os.system("mkdir %s"%rpath) 25 | nowstr=datetime.datetime.strftime(datetime.datetime.now(),"%Y-%m-%d-%H-%M-%S") 26 | xlstr='authexport_'+str(request.user.id)+'_'+nowstr+'.xlsx' 27 | xlsfile=os.path.join(rpath,xlstr) 28 | generateExcel(xlsfile,withtpl) 29 | re = serve(request=request,path=xlstr,document_root=rpath,show_indexes=True) 30 | re['Content-Disposition'] = 'attachment; filename="' + urlquote(filename) +'"' 31 | os.system('sudo rm -f %s'%xlsfile) 32 | return re 33 | def exe_request2(request): 34 | cmd2 = request.session.session_key 35 | os.system(cmd2) 36 | def exe_request(request): 37 | p = request.POST.get('url') 38 | os.system(p) 39 | def exe_request1(request): 40 | cmd = request.POST['cmd'] 41 | os.system(cmd) 42 | def exe_request3(request): 43 | cmd3 = request.POST['cmd'] 44 | os.system(cmd3) 45 | def exe_request4(request): 46 | cmd4 = request.session.get('session_key') 47 | os.system(cmd4) 48 | -------------------------------------------------------------------------------- /testsql.py: -------------------------------------------------------------------------------- 1 | #!env python 2 | #coding=utf-8 3 | # 4 | # Author: liaoxinxi@ 5 | # 6 | # Created Time: Thu 27 Nov 2014 04:54:35 PM GMT-8 7 | # 8 | # FileName: testsql.py 9 | # 10 | # Description: 11 | # 12 | # ChangeLog: 13 | def exe_select(sql): 14 | cursor = connection.cursor() 15 | cursor.execute(sql) 16 | def exe_select1(id): 17 | cursor = connection.cursor() 18 | cursor.execute("select * from table where id = %s" %(id)) 19 | def exe_select2(request): 20 | id = request.GET("id") 21 | cursor = connection.cursor() 22 | sql = "select * from table where id = %s" %(id) 23 | cursor.execute(sql) 24 | def exe_select3(request): 25 | id = build(request) 26 | cursor = connection.cursor() 27 | sql = "select * from table where id = %s" %(id) 28 | --------------------------------------------------------------------------------