Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pysecsan/command_injection.py: 11%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

45 statements  

1# Copyright 2022 Google LLC 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# 

15################################################################################ 

16"""Sanitizers for capturing code injections.""" 

17 

18from typing import Optional 

19from pysecsan import sanlib 

20 

21 

22def get_all_substr_prefixes(main_str, sub_str): 

23 """Yields all strings prefixed with sub_str in main_str.""" 

24 idx = 0 

25 while True: 

26 idx = main_str.find(sub_str, idx) 

27 if idx == -1: 

28 return 

29 yield main_str[0:idx] 

30 # Increase idx the length of the substring from the current position 

31 # where an occurence of the substring was found. 

32 idx += len(sub_str) 

33 

34 

35# pylint: disable=unsubscriptable-object 

36def check_code_injection_match(elem, check_unquoted=False) -> Optional[str]: 

37 """identify if elem is an injection match.""" 

38 # Check exact match 

39 if elem == 'exec-sanitizer': 

40 return 'Explicit command injection found.' 

41 

42 # Check potential for injecting into a string 

43 if 'FROMFUZZ' in elem: 

44 if check_unquoted: 

45 # return true if any index is unquoted 

46 for sub_str in get_all_substr_prefixes(elem, 'FROMFUZZ'): 

47 if sub_str.count('\"') % 2 == 0: 

48 return 'Fuzzer controlled content in data. Code injection potential.' 

49 

50 # Return None if all fuzzer taints were quoted 

51 return None 

52 return 'Fuzzer-controlled data in command string. Injection potential.' 

53 return None 

54 

55 

56# pylint: disable=invalid-name 

57def hook_pre_exec_subprocess_Popen(cmd, **kwargs): 

58 """Hook for subprocess.Popen.""" 

59 

60 arg_shell = 'shell' in kwargs and kwargs['shell'] 

61 

62 # Command injections depend on whether the first argument is a list of 

63 # strings or a string. Handle this now. 

64 # Example: tests/poe/ansible-runner-cve-2021-4041 

65 if isinstance(cmd, str): 

66 res = check_code_injection_match(cmd, check_unquoted=True) 

67 if res is not None: 

68 # if shell arg is true and string is tainted and unquoted that's a 

69 # definite code injection. 

70 if arg_shell is True: 

71 sanlib.abort_with_issue('Code injection in Popen', 'Command injection') 

72 

73 # It's a maybe: will not report this to avoid false positives. 

74 # TODO: add more precise detection here. 

75 

76 # Check for hg command injection 

77 # Example: tests/poe/libvcs-cve-2022-21187 

78 if cmd[0] == 'hg': 

79 # Check if the arguments are controlled by the fuzzer, and this given 

80 # arg is not preceded by -- 

81 found_dashes = False 

82 for idx in range(1, len(cmd)): 

83 if cmd[0] == '--': 

84 found_dashes = True 

85 if not found_dashes and check_code_injection_match(cmd[idx]): 

86 sanlib.abort_with_issue( 

87 'command injection likely by way of mercurial. The following' 

88 f'command {str(cmd)} is executed, and if you substitute {cmd[idx]} ' 

89 'with \"--config=alias.init=!touch HELLO_PY\" then you will ' 

90 'create HELLO_PY', 'Command injection') 

91 

92 

93def hook_pre_exec_os_system(cmd): 

94 """Hook for os.system.""" 

95 res = check_code_injection_match(cmd) 

96 if res is not None: 

97 sanlib.abort_with_issue(f'code injection by way of os.system\n{res}', 

98 'Command injection') 

99 

100 

101def hook_pre_exec_eval(cmd, *args, **kwargs): 

102 """Hook for eval. Experimental atm.""" 

103 res = check_code_injection_match(cmd, check_unquoted=True) 

104 if res is not None: 

105 sanlib.abort_with_issue(f'Potential code injection by way of eval\n{res}', 

106 'Command injection')