def test_override_var(self): code = ''' <?php $var1 = $_GET['param']; $var1 = 'blah'; $var2 = escapeshellarg($_GET['param2']); $var3 = 'blah'; if ($x){ $var3 = $_POST['param2']; } else{ $var3 = 'blah'.'blah'; } ?> ''' analyzer = PhpSCA(code) vars = analyzer.get_vars(usr_controlled=False) # 'var1' is safe var1 = vars[0] self.assertFalse(var1.controlled_by_user) # 'var2' is controlled by the user but is safe for OS-Commanding var2 = vars[1] self.assertTrue(var2.controlled_by_user) self.assertFalse(var2.is_tainted_for('OS_COMMANDING')) # 'var3' must still be controllable by user var3 = vars[2] self.assertTrue(var3.controlled_by_user)
def test_classes_2(self): code = ''' <?php class A { function foo($var) { $this->prop = $var; $this->baz(); } function bar() { include($this->prop); } function baz() { $this->bar(); echo $_GET[1]; } } $obj1 = new A(); $obj1->foo($_GET[1]); # XSS, FILE $obj1->bar('clean'); # FILE $obj2 = new A(); $obj2->bar(); # Clean $obj2->baz(); # XSS ?> ''' analyzer = PhpSCA(code) vulns = analyzer.get_vulns() self.assertEquals(2, len(vulns['XSS'])) self.assertEquals(2, len(vulns['FILE_INCLUDE']))
def test_vars(self): code = ''' <? $foo = $_GET['bar']; $spam = $_POST['blah']; $eggs = 'blah' . 'blah'; if ($eggs){ $xx = 'waka-waka'; $yy = $foo; } ?> ''' analyzer = PhpSCA(code) # Get all vars vars = analyzer.get_vars(usr_controlled=False) self.assertEquals(5, len(vars)) # Get user controlled vars usr_cont_vars = analyzer.get_vars(usr_controlled=True) self.assertEquals(3, len(usr_cont_vars)) # Test $foo foovar = usr_cont_vars[0] self.assertEquals('$foo', foovar.name) self.assertTrue(foovar.controlled_by_user) self.assertFalse(foovar.is_root) self.assertTrue(foovar.parents) # Test $spam spamvar = usr_cont_vars[1] self.assertEquals('$spam', spamvar.name) # Test $spam yyvar = usr_cont_vars[2] self.assertEquals('$yy', yyvar.name)
def main(): try: long_options = ['help', 'input-files='] opts, _ = getopt.getopt(sys.argv[1:], "hi:", long_options) except getopt.GetoptError: # print help information and exit: usage() return -3 input_file_list = None for o, a in opts: if o in ('-h', '--help'): usage() return 0 if o in ('-i', '--input-files='): input_file_list = a.split(',') if input_file_list is None: usage() return -3 for input_file in input_file_list: analyzer = PhpSCA(infile=input_file) for vulnerability_type in analyzer.get_vulns(): print vulnerability_type for vulnerability in analyzer.get_vulns()[vulnerability_type]: print ' ', vulnerability if len(analyzer.get_alerts()) > 0: print '' print 'Alerts:' for alert in analyzer.get_alerts(): print alert
def test_classes_3(self): code = ''' <?php class A { function foo($var) { $this->prop = $var; } function bar() { $var = 'bla' . somefunc($this->prop); echo $var; } } $obj1 = new A(); $obj1->foo($_GET[1]); $obj1->bar(); ?> ''' # Poperty to var test analyzer = PhpSCA(code) vulns = analyzer.get_vulns() self.assertEquals(1, len(vulns['XSS']))
def test_vuln_functions_2(self): code = ''' <? $foo = $_GET['bar']; system('ls ' . $foo); echo file_get_contents($foo); ?> ''' analyzer = PhpSCA(code) syscall, echocall = analyzer.get_func_calls() self.assertTrue('OS_COMMANDING' in syscall.vulntypes) self.assertTrue('FILE_DISCLOSURE' in echocall.vulntypes)
def test_include_require_2(self): analyzer = PhpSCA(infile = os.path.join(self.TEST_DIR, '2', 'a.php')) echo = analyzer.get_func_calls()[1] self.assertTrue('XSS' in echo.vulntypes) self.assertEquals('core' + os.sep + 'tests' + os.sep + 'test_include_require' + os.sep + '2' + os.sep + 'b.php', echo.get_file_name()) vulns = analyzer.get_vulns() self.assertEquals('core' + os.sep + 'tests' + os.sep + 'test_include_require' + os.sep + '2' + os.sep + 'a.php', vulns['XSS'][0][-1].get_file_name()) self.assertEquals(2, vulns['XSS'][0][-1].lineno)
def test_variable_taint_taint(self): code = ''' <? $foo = $_GET[2] .$_GET[1]; ?> ''' analyzer = PhpSCA(code) vars = analyzer.get_vars() foo_var = vars[0] self.assertTrue(foo_var.controlled_by_user) self.assertTrue(foo_var.is_tainted_for('XSS'))
def test_variable_no_taint_taint_no_taint_diff(self): code = ''' <? $foo = htmlspecialchars($_GET[1]) . $_GET[2] . htmlspecialchars($_GET[3]); ?> ''' analyzer = PhpSCA(code) vars = analyzer.get_vars() foo_var = vars[0] self.assertTrue(foo_var.controlled_by_user) self.assertTrue(foo_var.is_tainted_for('XSS'), code)
def test_vuln_func_get_sources_1(self): code = ''' <? $eggs = $_GET['bar']; $foo = func($eggs); $a = 'ls ' . $foo; exec($a); ?> ''' analyzer = PhpSCA(code) execfunc = analyzer.get_func_calls(vuln=True)[0] self.assertTrue( len(execfunc.vulnsources) == 1 and 'bar' in execfunc.vulnsources)
def test_vuln_functions_3(self): code = ''' <?php $var1 = escapeshellarg($_GET['param']); system($var1); system(escapeshellarg($_GET['param'])); system(myfunc(escapeshellarg($_GET['param']))); ?> ''' analyzer = PhpSCA(code) escapecall, syscall1, syscall2, syscall3 = analyzer.get_func_calls() # Both must be SAFE! self.assertEquals(0, len(syscall1.vulntypes)) self.assertEquals(0, len(syscall2.vulntypes)) self.assertEquals(0, len(syscall3.vulntypes))
def test_classes_1(self): code = ''' <?php class A { private $prop1 = 'ok'; function foo($var1) { echo $_GET[1]; $this->prop1 = $var1; } function bar($prop2 = 'default') { echo $this->prop1; $this->prop2 = $prop2; } function baz() { if (1) { system($this->prop2); } } } $obj1 = new A(); $obj1->foo($_GET[1]); #XSS $obj1->bar(); #XSS $obj1->baz(); $awsome = $_POST[1]; $obj2 = new A(); $obj2->foo('test'); #XSS $obj2->bar($awsome); $obj2->baz(); #OS COMMANDING $obj1->bar(); #XSS again ?>''' analyzer = PhpSCA(code) vulns = analyzer.get_vulns() self.assertEquals(4, len(vulns['XSS'])) self.assertEquals(1, len(vulns['OS_COMMANDING'])) self.assertEquals(18, vulns['OS_COMMANDING'][0][0].lineno) self.assertEquals('$awsome', vulns['OS_COMMANDING'][0][-1].name) self.assertEquals(28, vulns['OS_COMMANDING'][0][-1].lineno) objects = analyzer.get_objects(); self.assertTrue('$obj1' and '$obj2' in objects)
def test_function_scope_2(self): code = ''' <? $var = 1; echo $var; function foo($var, $var2) { echo $var; } foo($_GET[1], 4); ?> ''' analyzer = PhpSCA(code) echo_outside_func, echo_in_func = analyzer.get_func_calls() self.assertFalse('XSS' in echo_outside_func.vulntypes) self.assertTrue('XSS' in echo_in_func.vulntypes)
def analyze_(self, test_case): for input_file_obj in test_case.files: input_file_name = os.path.join(self.SAMATE_TEST_DIR, input_file_obj.file) analyzer = PhpSCA(infile=input_file_name) identified_vulns = [] for vuln_type in analyzer.get_vulns(): for vuln_func_call in analyzer.get_vulns()[vuln_type]: identified_vulns.append((vuln_type, vuln_func_call[0]._lineno)) expected_vulns = [] for flaw in input_file_obj.flaws: sca_name = SAMATE_TO_SCA[flaw.vuln_name] expected_vulns.append((sca_name, int(flaw.vuln_line_no))) #print set(expected_vulns), set(identified_vulns) assert set(expected_vulns) == set(identified_vulns)
def test_vuln_functions_1(self): code = ''' <?php $var = $_GET['bleh']; if ($x){ $var = 2; // not vuln! system($var); } // vuln for OS COMMANDING! system($var); ?> ''' analyzer = PhpSCA(code) sys1, sys2 = analyzer.get_func_calls() # First system call self.assertEquals(0, len(sys1.vulntypes)) # Second system call self.assertTrue('OS_COMMANDING' in sys2.vulntypes)
def test_vuln_functions_4(self): code = ''' <? $foo = $_GET['foo']; if ( $spam == $eggs ){ $foo = 'ls'; system($foo); } else{ echo $foo; system($foo); } ?> ''' analyzer = PhpSCA(code) sys1, echo, sys2 = analyzer.get_func_calls() self.assertEquals([], sys1.vulntypes) self.assertIn('XSS', echo.vulntypes) self.assertTrue('OS_COMMANDING' in sys2.vulntypes)
def test_var_comp_operators(self): code = ''' <?php $var0 = 'bleh'; $var1 = $_GET['param']; $var2 = 'blah'; ?> ''' analyzer = PhpSCA(code) vars = analyzer.get_vars(usr_controlled=False) code2 = ''' <?php $var0 = 'bleh'; $var1 = 'blah'; if ($x){ $var2 = $_POST['param2']; } else{ $var2 = 'blah'.'blah'; } ?> ''' analyzer = PhpSCA(code2) vars2 = analyzer.get_vars(usr_controlled=False) c1_var0 = vars[0] c2_var0 = vars2[0] c1_var0._scope = c2_var0._scope self.assertTrue(c2_var0 == c1_var0) c1_var1 = vars[1] c2_var1 = vars2[1] c2_var1._scope = c1_var1._scope self.assertTrue(c2_var1 > c1_var1) c1_var2 = vars[2] c2_var2 = vars2[2] self.assertTrue(c2_var2 > c1_var2)
def test_vars_dependencies(self): code = ''' <? $x1 = 'waca-waka'; $x2 = '#!~?#*' + $x1; $x3 = func($x2); $y = $_COOKIES['1']; $y2 = 'ls ' . $y; $z = $x2 + $x3; ?> ''' analyzer = PhpSCA(code) vars = analyzer.get_vars(usr_controlled=False) vars.sort(cmp=lambda x, y: cmp(x.lineno, y.lineno)) x1deps, x2deps, x3deps, ydeps, y2deps, zdeps = \ [[vd.name for vd in v.deps()] for v in vars] self.assertEquals([], x1deps) self.assertEquals(['$x1'], x2deps) self.assertEquals(['$x2', '$x1'], x3deps) self.assertEquals(['$_COOKIES'], ydeps) self.assertEquals(['$y', '$_COOKIES'], y2deps) self.assertEquals(['$x2', '$x3', '$x1'], zdeps)
def test_vuln_func_get_sources_2(self): code = '''<? echo file_get_contents($_REQUEST['file']); ?>''' analyzer = PhpSCA(code) execfunc = analyzer.get_func_calls(vuln=True)[0] self.assertTrue( len(execfunc.vulnsources) == 1 and 'file' in execfunc.vulnsources)
def test_vuln_func_get_sources_3(self): code = '''<? system($_GET['foo']); ?>''' analyzer = PhpSCA(code) execfunc = analyzer.get_func_calls(vuln=True)[0] self.assertTrue( len(execfunc.vulnsources) == 1 and 'foo' in execfunc.vulnsources)