PageRenderTime 57ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/scanner/Lib/VulnerabilityScanner.php

https://bitbucket.org/heinep/thaps
PHP | 2067 lines | 1765 code | 206 blank | 96 comment | 292 complexity | d91656c21398cac35237b507c2e4c9be MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. $bodyTraverser = new PHPParser_NodeTraverser;
  3. class THAPS_PHPParser_Node_LoopExpanded extends PHPParser_NodeAbstract
  4. {
  5. public function __construct(array $items = array(), $line = -1, $docComment = null)
  6. {
  7. parent::__construct(
  8. array(
  9. 'items' => $items
  10. ),
  11. $line, $docComment
  12. );
  13. }
  14. }
  15. class THAPS_PHPParser_Node_SwitchExpanded extends PHPParser_NodeAbstract
  16. {
  17. public function __construct(array $cases = array(), $line = -1, $docComment = null)
  18. {
  19. parent::__construct(
  20. array(
  21. 'cases' => $cases
  22. ),
  23. $line, $docComment
  24. );
  25. }
  26. }
  27. class BodyVisitor extends PHPParser_NodeVisitorAbstract
  28. {
  29. /**
  30. * @var bool decides if full tree should be used, or equal branches should be removed.
  31. */
  32. private static $fullTree = false;
  33. public static function useFullTree($on)
  34. {
  35. self::$fullTree = $on;
  36. }
  37. private static $postCleanSQL = false;
  38. private static $postCleanXSS = false;
  39. private static $getCleanSQL = false;
  40. private static $getCleanXSS = false;
  41. private static $cookieCleanSQL = false;
  42. private static $cookieCleanXSS = false;
  43. private static $requestCleanSQL = false;
  44. private static $requestCleanXSS = false;
  45. public static function useCleanGetPost($sql, $xss)
  46. {
  47. self::$postCleanSQL = $sql;
  48. self::$postCleanXSS = $xss;
  49. self::$getCleanSQL = $sql;
  50. self::$getCleanXSS = $xss;
  51. self::$cookieCleanSQL = $sql;
  52. self::$cookieCleanXSS = $xss;
  53. self::$requestCleanSQL = $sql;
  54. self::$requestCleanXSS = $xss;
  55. }
  56. /**
  57. * @var int Defined how many times loops should be unrolled
  58. */
  59. private $loopExpandTimes = 1;
  60. public function setLoopExpandTimes($times)
  61. {
  62. $this->loopExpandTimes = $times;
  63. }
  64. /**
  65. * @var VulnerabilityStorage
  66. */
  67. public $vulnerabilities;
  68. public function setVulnerabilityStorage(VulnerabilityStorage &$vulnerabilities)
  69. {
  70. $this->vulnerabilities = $vulnerabilities;
  71. }
  72. public function getVulnerabilities()
  73. {
  74. return $this->vulnerabilities;
  75. }
  76. /**
  77. * @var VariableStorage
  78. */
  79. private $vScope;
  80. public function setVScope(VariableStorage $storage)
  81. {
  82. $this->vScope = $storage;
  83. }
  84. public function getVScope()
  85. {
  86. return $this->vScope;
  87. }
  88. /**
  89. * @var array
  90. */
  91. private $dependencies = null;
  92. public function setDependencies($deps)
  93. {
  94. $this->dependencies = $deps;
  95. }
  96. /**
  97. * @var VariableValue
  98. */
  99. private $taint;
  100. public function getTaint()
  101. {
  102. return $this->taint;
  103. }
  104. /**
  105. * When an if is left, this is used to determine if the scopes is unnessary.
  106. * @var VariableStorage[][]
  107. */
  108. private $ifScopes = array();
  109. public static $lastNode = -1;
  110. public function beforeTraverse(array $nodes)
  111. {
  112. if ($this->dependencies == null) {
  113. $this->dependencies = array();
  114. }
  115. $this->taint = new VariableValue();
  116. }
  117. public function afterTraverse(array $nodes)
  118. {
  119. return $this->vulnerabilities->get();
  120. }
  121. private static $ignoreProgress = false;
  122. public function enterNode(PHPParser_Node $node)
  123. {
  124. global $totalNodeCount, $stepSize;
  125. if ($node->getLine() == 143) {
  126. //echo "najs";
  127. }
  128. if (!self::$ignoreProgress && $node->getNodeNumber() != -1 && $node->getNodeNumber() % $stepSize == 0 && self::$lastNode != $node->getNodeNumber()) {
  129. echo "Progress @ " . date("H:i:s") . " - " . $node->getNodeNumber() . "/" . $totalNodeCount . "\n";
  130. self::$lastNode = $node->getNodeNumber();
  131. }
  132. /*
  133. * Control flow structures
  134. * Currently only IF/THEN/ELSE
  135. */
  136. if ($node instanceof PHPParser_Node_Stmt_If) {
  137. self::$ignoreProgress = true;
  138. // Create the new scopes for each if/elseif and else in the proper locatations
  139. $possiblePlaces = $this->vScope->getVariableValueConfigurations($this->dependencies);
  140. $this->ifScopes[] = $possiblePlaces;
  141. // Make accessment of condition, it might secure some variables
  142. $conditionVisitor = new ConditionVisitor();
  143. $conditionTranverser = new PHPParser_NodeTraverser();
  144. $conditionTranverser->addVisitor($conditionVisitor);
  145. $conditionTranverser->traverse(array($node->cond));
  146. $cleanVars = $conditionVisitor->getCleanedVars();
  147. foreach ($possiblePlaces as $possiblePlace) {
  148. $cond = $node->getFilename() . ":" . $node->getLine() . ":if (" . printNode($node->cond, true) . ")";
  149. // For each of the cleaned vars lets check if they are dangerous
  150. $storage = $possiblePlace->addSubstorage($cond);
  151. $taintTraverser = new PHPParser_NodeTraverser;
  152. $taintVisitor = new BodyVisitor;
  153. $taintVisitor->setVScope($storage);
  154. $taintVisitor->setDependencies($this->dependencies);
  155. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  156. $taintTraverser->addVisitor($taintVisitor);
  157. if (is_array($cleanVars))
  158. foreach ($cleanVars as $cleanVar) {
  159. $taintTraverser->traverse(array($cleanVar));
  160. $taint = $taintVisitor->getTaint();
  161. if ($taint instanceof VariableValue && $taint->userInput) {
  162. $taint->xss = false;
  163. $taint->sql = false;
  164. $taint->value = str_replace(array("_SQL", "_XSS"), "", $taint->value);
  165. $assignArray = $this->findVarNameAndDim($cleanVar, $taintTraverser, $taintVisitor);
  166. array_unshift($assignArray, $taint);
  167. call_user_func_array(array($storage, 'setVariableValue'), $assignArray);
  168. }
  169. }
  170. if ($node->elseifs !== null) {
  171. foreach ($node->elseifs as $elseif) {
  172. $cond = $elseif->getFilename() . ":" . $elseif->getLine() . ":elseif (" . printNode($elseif->cond, true) . ")";
  173. $storage = $possiblePlace->addSubstorage($cond);
  174. // Make accessment of condition, it might secure some variables
  175. $conditionVisitor = new ConditionVisitor();
  176. $conditionTranverser = new PHPParser_NodeTraverser();
  177. $conditionTranverser->addVisitor($conditionVisitor);
  178. $conditionTranverser->traverse(array($elseif->cond));
  179. $cleanVars = $conditionVisitor->getCleanedVars();
  180. $taintTraverser = new PHPParser_NodeTraverser;
  181. $taintVisitor = new BodyVisitor;
  182. $taintVisitor->setVScope($storage);
  183. $taintVisitor->setDependencies($this->dependencies);
  184. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  185. $taintTraverser->addVisitor($taintVisitor);
  186. if (is_array($cleanVars))
  187. foreach ($cleanVars as $cleanVar) {
  188. $taintTraverser->traverse(array($cleanVar));
  189. $taint = $taintVisitor->getTaint();
  190. if ($taint instanceof VariableValue && $taint->userInput) {
  191. $taint->xss = false;
  192. $taint->sql = false;
  193. $taint->value = str_replace(array("_SQL", "_XSS"), "", $taint->value);
  194. $assignArray = $this->findVarNameAndDim($cleanVar, $taintTraverser, $taintVisitor);
  195. array_unshift($assignArray, $taint);
  196. call_user_func_array(array($storage, 'setVariableValue'), $assignArray);
  197. }
  198. }
  199. }
  200. }
  201. if (($else = $node->else) !== null) {
  202. $cond = $else->getFilename() . ":" . $else->getLine() . ":else";
  203. $possiblePlace->addSubstorage($cond, true);
  204. }
  205. }
  206. // Set the current dependencies for the futher scan
  207. $this->dependencies[] = $node->getFilename() . ":" . $node->getLine() . ":if (" . printNode($node->cond, true) . ")";
  208. self::$ignoreProgress = false;
  209. }
  210. elseif ($node instanceof PHPParser_Node_Stmt_ElseIf) {
  211. // Pop the old dependency which will be the if or a prior elseif and add the elseif
  212. array_pop($this->dependencies);
  213. $this->dependencies[] = $node->getFilename() . ":" . $node->getLine() . ":elseif (" . printNode($node->cond, true) . ")";
  214. }
  215. elseif ($node instanceof PHPParser_Node_Stmt_Else) {
  216. // Pop the old dependency which will be the if or an elseif and add the else
  217. array_pop($this->dependencies);
  218. $this->dependencies[] = $node->getFilename() . ":" . $node->getLine() . ":else";
  219. }
  220. elseif ($node instanceof PHPParser_Node_Stmt_Switch) {
  221. $possiblePlaces = $this->vScope->getVariableValueConfigurations($this->dependencies);
  222. $this->ifScopes[] = $possiblePlaces;
  223. $remove = array();
  224. $merge = array();
  225. $concat = array();
  226. $default = false;
  227. foreach ($node->cases as $index => $case) {
  228. //
  229. if (count($case->stmts) == 0) {
  230. $merge[] = $case;
  231. $remove[] = $index;
  232. if ($case->cond == null) {
  233. $case->cond = new PHPParser_Node_Scalar_String("__default__", $case->getLine(), $case->getFilename(), $case->getDocComment());
  234. $default = true;
  235. }
  236. }
  237. else if ($case->stmts[count($case->stmts) - 1] instanceof PHPParser_Node_Stmt_Break) {
  238. /**
  239. * add stmts to earliers cases without break;
  240. */
  241. if (count($concat) > 0) {
  242. foreach ($concat as $tmpcase) {
  243. $tmpcase->stmts = array_merge($tmpcase->stmts, unserialize(serialize($case->stmts)));
  244. }
  245. $concat = array();
  246. }
  247. if ($case->cond == null) {
  248. $case->cond = new PHPParser_Node_Scalar_String("__default__", $case->getLine(), $case->getFilename(), $case->getDocComment());
  249. $default = true;
  250. }
  251. /**
  252. * Merge empty
  253. */
  254. if (count($merge) > 0) {
  255. foreach ($merge as $tmpcase) {
  256. $case->cond = new PHPParser_Node_Expr_LogicalOr($tmpcase->cond, $case->cond, $tmpcase->getLine(), $tmpcase->getFilename(), $tmpcase->getDocComment());
  257. }
  258. $merge = array();
  259. }
  260. $cond = $case->getFilename() . ":" . $case->getLine() . ":case " . printNode($case->cond, true);
  261. foreach ($possiblePlaces as $possiblePlace) {
  262. $possiblePlace->addSubstorage($cond, $default);
  263. }
  264. } else {
  265. $concat[] = $case;
  266. if ($case->cond == null) {
  267. $case->cond = new PHPParser_Node_Scalar_String("__default__", $case->getLine(), $case->getFilename(), $case->getDocComment());
  268. $default = true;
  269. }
  270. $cond = $case->getFilename() . ":" . $case->getLine() . ":case " . printNode($case->cond, true);
  271. foreach ($possiblePlaces as $possiblePlace) {
  272. $possiblePlace->addSubstorage($cond, $default);
  273. }
  274. }
  275. }
  276. foreach ($remove as $index) {
  277. array_splice($node->cases, $index, 1);
  278. }
  279. // Add empty dependency to remove at first case
  280. $this->dependencies[] = "FAKE FOR SWITCH CASE";
  281. }
  282. elseif ($node instanceof PHPParser_Node_Stmt_Case) {
  283. array_pop($this->dependencies);
  284. $cond = $node->getFilename() . ":" . $node->getLine() . ":case " . printNode($node->cond, true);
  285. $this->dependencies[] = $cond;
  286. }
  287. elseif ($node instanceof PHPParser_Node_Stmt_For) {
  288. // Expand the for loop
  289. $nodes = array_merge($node->init, $node->stmts, $node->loop);
  290. for ($i = 1; $i < $this->loopExpandTimes; $i++) {
  291. $nodes = array_merge($nodes, $node->stmts);
  292. $nodes = array_merge($nodes, $node->loop);
  293. }
  294. $tmp = new THAPS_PHPParser_Node_LoopExpanded($nodes, $node->getLine(), $node->getFilename(), $node->getDocComment());
  295. return $tmp;
  296. }
  297. elseif ($node instanceof PHPParser_Node_Stmt_While) {
  298. // Expand the while loop
  299. $nodes = $node->stmts;
  300. for ($i = 1; $i < $this->loopExpandTimes; $i++) {
  301. $nodes = array_merge($nodes, $node->stmts);
  302. }
  303. $tmp = new THAPS_PHPParser_Node_LoopExpanded($nodes, $node->getLine(), $node->getFilename(), $node->getDocComment());
  304. return $tmp;
  305. }
  306. elseif ($node instanceof PHPParser_Node_Stmt_Foreach) {
  307. // Lets detect if the foreach is a specific clean for
  308. $taintTraverser = new PHPParser_NodeTraverser;
  309. $taintVisitor = new BodyVisitor;
  310. $taintVisitor->setVScope($this->vScope);
  311. $taintVisitor->setDependencies($this->dependencies);
  312. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  313. $taintTraverser->addVisitor($taintVisitor);
  314. $taintTraverser->traverse(array($node->expr));
  315. $varTaint = $taintVisitor->getTaint();
  316. // might be an array - we only care for the first value, it might be dangerous
  317. while (is_array($varTaint)) {
  318. $varTaint = array_pop($varTaint);
  319. }
  320. //
  321. // Detech foreach (..$_POST/GET.. ) $_POST/GET = ...
  322. //
  323. if ($varTaint instanceof VariableValue && $varTaint->userInput && count($varTaint->flowpath) == 0 &&
  324. count($node->stmts) == 1 && $node->stmts[0] instanceof PHPParser_Node_Expr_Assign && $node->stmts[0]->var instanceof PHPParser_Node_Expr_ArrayDimFetch &&
  325. ($node->stmts[0]->var->var->name == "_POST" || $node->stmts[0]->var->var->name == "_GET" || $node->stmts[0]->var->var->name == "_COOKIE" || $node->stmts[0]->var->var->name == "_REQUEST")
  326. ) {
  327. $funcCallTraverser = new PHPParser_NodeTraverser();
  328. $funcCallVisitor = new FunctionCallResolver();
  329. $funcCallTraverser->addVisitor($funcCallVisitor);
  330. $funcCallTraverser->traverse($node->stmts);
  331. global $SECURING_ALL, $SECURING_XSS, $SECURING_SQL;
  332. $all = count(array_intersect($SECURING_ALL, $funcCallVisitor->getFuncCallList())) != 0;
  333. $xss = count(array_intersect($SECURING_XSS, $funcCallVisitor->getFuncCallList())) != 0;
  334. $sql = count(array_intersect($SECURING_SQL, $funcCallVisitor->getFuncCallList())) != 0;
  335. if ($node->stmts[0]->var->var->name == "_POST") {
  336. if ($sql || $all) {
  337. self::$postCleanSQL = true;
  338. }
  339. if ($xss || $all) {
  340. self::$postCleanXSS = true;
  341. }
  342. } else if ($node->stmts[0]->var->var->name == "_GET") {
  343. if ($sql || $all) {
  344. self::$getCleanSQL = true;
  345. }
  346. if ($xss || $all) {
  347. self::$getCleanXSS = true;
  348. }
  349. } else if ($node->stmts[0]->var->var->name == "_COOKIE") {
  350. if ($sql || $all) {
  351. self::$cookieCleanSQL = true;
  352. }
  353. if ($xss || $all) {
  354. self::$cookieCleanXSS = true;
  355. }
  356. } else if ($node->stmts[0]->var->var->name == "_REQUEST") {
  357. if ($sql || $all) {
  358. self::$requestCleanSQL = true;
  359. }
  360. if ($xss || $all) {
  361. self::$requestCleanXSS = true;
  362. }
  363. }
  364. return false;
  365. } else {
  366. // TODO: expand the foreach loop
  367. }
  368. }
  369. /**
  370. * Remove the boolean expr, as they do the same stuff, nothing
  371. */
  372. elseif ($node instanceof PHPParser_Node_Expr_Equal ||
  373. $node instanceof PHPParser_Node_Expr_NotEqual ||
  374. $node instanceof PHPParser_Node_Expr_Greater ||
  375. $node instanceof PHPParser_Node_Expr_GreaterOrEqual ||
  376. $node instanceof PHPParser_Node_Expr_Smaller ||
  377. $node instanceof PHPParser_Node_Expr_SmallerOrEqual ||
  378. $node instanceof PHPParser_Node_Expr_BooleanAnd ||
  379. $node instanceof PHPParser_Node_Expr_BooleanNot ||
  380. $node instanceof PHPParser_Node_Expr_BooleanOr ||
  381. $node instanceof PHPParser_Node_Expr_Identical
  382. ) {
  383. $this->taint = new VariableValue();
  384. return false;
  385. }
  386. elseif ($node instanceof PHPParser_Node_Expr_LogicalOr ||
  387. $node instanceof PHPParser_Node_Expr_LogicalAnd ||
  388. $node instanceof PHPParser_Node_Expr_LogicalXor
  389. ) {
  390. $taintTraverser = new PHPParser_NodeTraverser;
  391. $taintVisitor = new BodyVisitor();
  392. $taintVisitor->setVScope($this->vScope);
  393. $taintVisitor->setDependencies($this->dependencies);
  394. $taintVisitor->setDependencies($this->dependencies);
  395. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  396. $taintTraverser->addVisitor($taintVisitor);
  397. $taintTraverser->traverse(array($node->left));
  398. $leftTaint = $taintVisitor->getTaint();
  399. $taintTraverser->traverse(array($node->right));
  400. $rightTaint = $taintVisitor->getTaint();
  401. $this->taint = new VariableValue();
  402. if ($leftTaint === null && $rightTaint !== null) {
  403. $this->taint = $rightTaint;
  404. } elseif ($leftTaint !== null && $rightTaint === null) {
  405. $this->taint = $leftTaint;
  406. } elseif ($leftTaint !== null && $rightTaint !== null) {
  407. if ($leftTaint instanceof VariableValue && $rightTaint instanceof VariableValue) {
  408. $this->taint->userInput = $leftTaint->userInput || $rightTaint->userInput;
  409. $this->taint->xss = $leftTaint->xss || $rightTaint->xss;
  410. $this->taint->sql = $leftTaint->sql || $rightTaint->sql;
  411. } else {
  412. // This is wrong, need to be better if left and right are complex
  413. $this->taint = $leftTaint;
  414. }
  415. }
  416. return false;
  417. }
  418. /**
  419. * INT, BOOL and DOUBLE casts makes value safe
  420. */
  421. elseif ($node instanceof PHPParser_Node_Expr_Cast_Bool ||
  422. $node instanceof PHPParser_Node_Expr_Cast_Double ||
  423. $node instanceof PHPParser_Node_Expr_Cast_Int
  424. ) {
  425. $taintTraverser = new PHPParser_NodeTraverser;
  426. $taintVisitor = new BodyVisitor;
  427. $taintVisitor->setVScope($this->vScope);
  428. $taintVisitor->setDependencies($this->dependencies);
  429. $taintVisitor->setDependencies($this->dependencies);
  430. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  431. $taintTraverser->addVisitor($taintVisitor);
  432. $taintTraverser->traverse(array($node->expr));
  433. $varTaint = $taintVisitor->getTaint();
  434. $this->taint = new VariableValue();
  435. $this->taint->value = str_replace(array("USERINPUT", "_XSS", "_SQL"), array("USERCASTEDINPUT", "", ""), $varTaint->value);
  436. return false;
  437. }
  438. /**
  439. * Get variable values - these should only be hit where the vScope is a single target,
  440. * as they are a part of the taint evaluation - maybe that is ;)
  441. * - Torben
  442. */
  443. elseif ($node instanceof PHPParser_Node_Expr_Variable) {
  444. $val = $this->vScope->getVariableValue($node->name);
  445. if ($val !== null) {
  446. $this->taint = $val;
  447. } else if ($node->name == "_POST") {
  448. $var = new VariableValue();
  449. $var->userInput = true;
  450. $var->xss = self::$postCleanXSS;
  451. $var->sql = self::$postCleanSQL;
  452. $this->taint = array($var);
  453. } else if ($node->name == "_GET") {
  454. $var = new VariableValue();
  455. $var->userInput = true;
  456. $var->xss = self::$getCleanXSS;
  457. $var->sql = self::$getCleanSQL;
  458. $this->taint = array($var);
  459. } else if ($node->name == "_COOKIE") {
  460. $var = new VariableValue();
  461. $var->userInput = true;
  462. $var->xss = self::$cookieCleanXSS;
  463. $var->sql = self::$cookieCleanSQL;
  464. $this->taint = array($var);
  465. } else if ($node->name == "_REQUEST") {
  466. $var = new VariableValue();
  467. $var->userInput = true;
  468. $var->xss = self::$requestCleanXSS;
  469. $var->sql = self::$requestCleanSQL;
  470. $this->taint = array($var);
  471. }
  472. return false;
  473. }
  474. elseif ($node instanceof PHPParser_Node_Expr_ArrayDimFetch) {
  475. // Find the value of an array dim fetch
  476. $taintTraverser = new PHPParser_NodeTraverser;
  477. $taintVisitor = new BodyVisitor;
  478. $taintVisitor->setVScope($this->vScope);
  479. $taintVisitor->setDependencies($this->dependencies);
  480. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  481. $taintTraverser->addVisitor($taintVisitor);
  482. $taintTraverser->traverse(array($node->dim));
  483. $dimVal = $taintVisitor->getTaint()->value;
  484. $dim = array($dimVal);
  485. $target = $node;
  486. // If multidimension is hit
  487. while ($target->var instanceof PHPParser_Node_Expr_ArrayDimFetch) {
  488. $target = $target->var;
  489. $taintTraverser->traverse(array($target->dim));
  490. $dimVal = $taintVisitor->getTaint()->value;
  491. array_unshift($dim, $dimVal);
  492. }
  493. array_unshift($dim, $target->var->name);
  494. $val = call_user_func_array(array($this->vScope, 'getVariableValue'), $dim);
  495. if ($val !== null) {
  496. $this->taint = $val;
  497. } else if ($target->var->name == "_POST") {
  498. // If the value is not found and we are talking POST or GET variables then "invent" them
  499. $this->taint->userInput = true;
  500. $this->taint->value = "{USERINPUT" . (self::$postCleanSQL ? "" : "_SQL") . (self::$postCleanXSS ? "" : "_XSS") . "}";
  501. $this->taint->xss = !self::$postCleanXSS;
  502. $this->taint->sql = !self::$postCleanSQL;
  503. } else if ($target->var->name == "_GET") {
  504. $this->taint->userInput = true;
  505. $this->taint->value = "{USERINPUT" . (self::$getCleanSQL ? "" : "_SQL") . (self::$getCleanXSS ? "" : "_XSS") . "}";
  506. $this->taint->xss = !self::$getCleanXSS;
  507. $this->taint->sql = !self::$getCleanSQL;
  508. } else if ($target->var->name == "_COOKIE") {
  509. $this->taint->userInput = true;
  510. $this->taint->value = "{USERINPUT" . (self::$cookieCleanSQL ? "" : "_SQL") . (self::$cookieCleanXSS ? "" : "_XSS") . "}";
  511. $this->taint->xss = !self::$cookieCleanXSS;
  512. $this->taint->sql = !self::$cookieCleanSQL;
  513. } else if ($target->var->name == "_REQUEST") {
  514. $this->taint->userInput = true;
  515. $this->taint->value = "{USERINPUT" . (self::$requestCleanSQL ? "" : "_SQL") . (self::$requestCleanXSS ? "" : "_XSS") . "}";
  516. $this->taint->xss = !self::$requestCleanXSS;
  517. $this->taint->sql = !self::$requestCleanSQL;
  518. }
  519. return false;
  520. }
  521. elseif ($node instanceof PHPParser_Node_Expr_PropertyFetch) {
  522. // Get a property of an object
  523. $class = $this->vScope->getVariableValue($node->var->name);
  524. if (!($class instanceof ClassDescription)) {
  525. return false;
  526. }
  527. $property = $class->getProperty($node->name);
  528. if ($property !== null) {
  529. $this->taint = $this->vScope->getVariableValue($node->var->name, $node->name);
  530. }
  531. return false;
  532. }
  533. elseif ($node instanceof PHPParser_Node_Expr_StaticPropertyFetch) {
  534. // Static variables are saved on class name level
  535. $this->taint = $this->vScope->getVariableValue(VAR_REP_STATIC . $node->class->parts[0], $node->name);
  536. return false;
  537. }
  538. elseif ($node instanceof PHPParser_Node_Expr_ClassConstFetch) {
  539. $className = $node->class->parts[0];
  540. $name = $node->name;
  541. if ($className == "self") {
  542. global $parsedClass;
  543. $className = $parsedClass;
  544. }
  545. if (($class = ClassStorage::getClass($className)) !== null) {
  546. if (isset($class->constants[$name])) {
  547. $this->taint = new VariableValue();
  548. $this->taint->value = $class->constants[$name]->value;
  549. }
  550. }
  551. return false;
  552. }
  553. elseif ($node instanceof PHPParser_Node_Expr_ConstFetch) {
  554. $target = $this->vScope;
  555. while ($target->parentScope !== null) {
  556. $target = $target->parentScope;
  557. }
  558. if (defined(end($node->name->parts))) {
  559. $this->taint = new VariableValue();
  560. eval('$this->taint->value=' . end($node->name->parts) . ';');
  561. } else {
  562. $this->taint = $target->getVariableValue(VAR_REP_CONST . end($node->name->parts));
  563. if ($this->taint === null) {
  564. // Constants that are not defined are apparently defaulted to a string
  565. $this->taint = new VariableValue();
  566. $this->taint->value = end($node->name->parts);
  567. }
  568. }
  569. return false;
  570. }
  571. elseif ($node instanceof PHPParser_Node_Expr_Concat) {
  572. // Concat, simple taint traverse both sides and .. concat.
  573. $taintTraverser = new PHPParser_NodeTraverser;
  574. $taintVisitor = new BodyVisitor();
  575. $taintVisitor->setVScope($this->vScope);
  576. $taintVisitor->setDependencies($this->dependencies);
  577. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  578. $taintTraverser->addVisitor($taintVisitor);
  579. $taintTraverser->traverse(array($node->left));
  580. $leftTaint = $taintVisitor->getTaint();
  581. $taintTraverser->traverse(array($node->right));
  582. $partTaint = $taintVisitor->getTaint();
  583. $this->taint->userInput = $leftTaint->userInput || $partTaint->userInput;
  584. $this->taint->xss = $leftTaint->xss || $partTaint->xss;
  585. $this->taint->sql = $leftTaint->sql || $partTaint->sql;
  586. if ($this->taint->userInput) {
  587. if ($leftTaint->userInput && !$partTaint->userInput) {
  588. $this->taint->flowpath = $leftTaint->flowpath;
  589. $this->taint->dependencies = $leftTaint->dependencies;
  590. } else if (!$leftTaint->userInput) {
  591. $this->taint->flowpath = $partTaint->flowpath;
  592. $this->taint->dependencies = $partTaint->dependencies;
  593. } else {
  594. $this->taint->flowpath = array_merge($leftTaint->flowpath, $partTaint->flowpath);
  595. $this->taint->dependencies = array_merge($leftTaint->dependencies, $partTaint->dependencies);
  596. }
  597. }
  598. $this->taint->value = $leftTaint->value . $partTaint->value;
  599. return false;
  600. }
  601. elseif ($node instanceof PHPParser_Node_Scalar_Encapsed) {
  602. // String with included variables - i.e. "hello $name"
  603. $taintTraverser = new PHPParser_NodeTraverser;
  604. $taintVisitor = new BodyVisitor();
  605. $taintVisitor->setVScope($this->vScope);
  606. $taintVisitor->setDependencies($this->dependencies);
  607. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  608. $taintTraverser->addVisitor($taintVisitor);
  609. for ($i = 0; $i < count($node->parts); $i++) {
  610. if ($node->parts[$i] instanceof PHPParser_Node) {
  611. $taintTraverser->traverse(array($node->parts[$i]));
  612. $partTaint = $taintVisitor->getTaint();
  613. $old = $this->taint->userInput;
  614. $this->taint->userInput = $this->taint->userInput || $partTaint->userInput;
  615. $this->taint->xss = $this->taint->xss || $partTaint->xss;
  616. $this->taint->sql = $this->taint->sql || $partTaint->sql;
  617. if ($this->taint->userInput) {
  618. if (!$old) {
  619. $this->taint->flowpath = $partTaint->flowpath;
  620. $this->taint->dependencies = $partTaint->dependencies;
  621. } else {
  622. $this->taint->flowpath = array_merge($this->taint->flowpath, $partTaint->flowpath);
  623. $this->taint->dependencies = array_merge($this->taint->dependencies, $partTaint->dependencies);
  624. }
  625. }
  626. $this->taint->value = $this->taint->value . $partTaint->value;
  627. } else {
  628. $this->taint->value = $this->taint->value . $node->parts[$i];
  629. }
  630. }
  631. return false;
  632. }
  633. elseif ($node instanceof PHPParser_Node_Scalar_FileConst) {
  634. $this->taint->value = $node->getFilename();
  635. return false;
  636. }
  637. elseif ($node instanceof PHPParser_Node_Scalar_String ||
  638. $node instanceof PHPParser_Node_Scalar_LNumber
  639. ) {
  640. $this->taint->value = $node->value;
  641. return false;
  642. }
  643. elseif ($node instanceof PHPParser_Node_Expr_Array) {
  644. $array = array();
  645. foreach ($node->items as $item) {
  646. $taintTraverser = new PHPParser_NodeTraverser;
  647. $taintVisitor = new BodyVisitor();
  648. $taintVisitor->setVScope($this->vScope);
  649. $taintVisitor->setDependencies($this->dependencies);
  650. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  651. $taintTraverser->addVisitor($taintVisitor);
  652. $taintTraverser->traverse(array($item->value));
  653. $val = $taintVisitor->getTaint();
  654. if ($item->key !== null) {
  655. $taintTraverser->traverse(array($item->key));
  656. $key = $taintVisitor->getTaint()->value;
  657. $array[$key] = $val;
  658. } else {
  659. $array[] = $val;
  660. }
  661. }
  662. $this->taint = $array;
  663. return false;
  664. }
  665. /**
  666. * Function and method calls
  667. */
  668. elseif ($node instanceof PHPParser_Node_Expr_FuncCall) {
  669. global $functions;
  670. $funcName = $node->name->parts[0];
  671. global $SECURING_ALL, $SECURING_XSS, $SECURING_SQL, $INSECURING_USERINPUT;
  672. if (in_array($funcName, $SECURING_XSS)) {
  673. $taintTraverser = new PHPParser_NodeTraverser;
  674. $taintVisitor = new BodyVisitor();
  675. $taintVisitor->setDependencies($this->dependencies);
  676. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  677. $taintTraverser->addVisitor($taintVisitor);
  678. $args = $node->args;
  679. foreach ($args as $nr => $arg) {
  680. $varValueConfigurations = $this->vScope->getVariableValueConfigurations($this->dependencies);
  681. foreach ($varValueConfigurations as $varValueConfiguration) {
  682. $taintVisitor->setVScope($varValueConfiguration);
  683. $taintTraverser->traverse(array($arg->value));
  684. $this->taint = $taintVisitor->getTaint();
  685. }
  686. }
  687. $this->taint->xss = false;
  688. $this->taint->value = str_replace("_XSS", "", $this->taint->value);
  689. } else if (in_array($funcName, $SECURING_SQL)) {
  690. $taintTraverser = new PHPParser_NodeTraverser;
  691. $taintVisitor = new BodyVisitor();
  692. $taintVisitor->setDependencies($this->dependencies);
  693. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  694. $taintTraverser->addVisitor($taintVisitor);
  695. $args = $node->args;
  696. foreach ($args as $nr => $arg) {
  697. $varValueConfigurations = $this->vScope->getVariableValueConfigurations($this->dependencies);
  698. foreach ($varValueConfigurations as $varValueConfiguration) {
  699. $taintVisitor->setVScope($varValueConfiguration);
  700. $taintTraverser->traverse(array($arg->value));
  701. $this->taint = $taintVisitor->getTaint();
  702. }
  703. }
  704. $this->taint->sql = false; //$oldTaint->sql;
  705. $this->taint->value = str_replace("_SQL", "", $this->taint->value);
  706. } else if (in_array($funcName, $SECURING_ALL)) {
  707. $taintTraverser = new PHPParser_NodeTraverser;
  708. $taintVisitor = new BodyVisitor();
  709. $taintVisitor->setDependencies($this->dependencies);
  710. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  711. $taintTraverser->addVisitor($taintVisitor);
  712. $args = $node->args;
  713. foreach ($args as $nr => $arg) {
  714. $varValueConfigurations = $this->vScope->getVariableValueConfigurations($this->dependencies);
  715. foreach ($varValueConfigurations as $varValueConfiguration) {
  716. $taintVisitor->setVScope($varValueConfiguration);
  717. $taintTraverser->traverse(array($arg->value));
  718. $this->taint = $taintVisitor->getTaint();
  719. }
  720. }
  721. if ($this->taint instanceof VariableValue) {
  722. $this->taint->xss = false; //$oldTaint->xss;
  723. $this->taint->sql = false; //$oldTaint->sql;
  724. $this->taint->value = str_replace("_SQL", "", $this->taint->value);
  725. $this->taint->value = str_replace("_XSS", "", $this->taint->value);
  726. } else {
  727. $this->taint = new VariableValue();
  728. }
  729. } else if (in_array($funcName, $INSECURING_USERINPUT)) {
  730. $taintTraverser = new PHPParser_NodeTraverser;
  731. $taintVisitor = new BodyVisitor();
  732. $taintVisitor->setDependencies($this->dependencies);
  733. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  734. $taintTraverser->addVisitor($taintVisitor);
  735. $args = $node->args;
  736. foreach ($args as $nr => $arg) {
  737. $varValueConfigurations = $this->vScope->getVariableValueConfigurations($this->dependencies);
  738. foreach ($varValueConfigurations as $varValueConfiguration) {
  739. $taintVisitor->setVScope($varValueConfiguration);
  740. $taintTraverser->traverse(array($arg->value));
  741. $this->taint = $taintVisitor->getTaint();
  742. }
  743. }
  744. if ($this->taint->userInput) {
  745. $this->taint->xss = true;
  746. $this->taint->sql = true;
  747. $this->taint->value = str_replace("{USERINPUT", "{USERINPUT_XSS", $this->taint->value);
  748. $this->taint->value = str_replace("{USERINPUT", "{USERINPUT_SQL", $this->taint->value);
  749. }
  750. } elseif (isset($functions[$funcName])) {
  751. $function = $functions[$funcName];
  752. $this->taint->userInput = false;
  753. $this->taint->xss = false;
  754. $this->taint->sql = false;
  755. if (count($function->returnAlwaysVulnerable) > 0) {
  756. foreach ($function->returnAlwaysVulnerable as $vuln) {
  757. if ($vuln->xss) {
  758. $this->taint->xss = true;
  759. $this->taint->userInput = true;
  760. }
  761. if ($vuln->sql) {
  762. $this->taint->sql = true;
  763. $this->taint->userInput = true;
  764. }
  765. if ($vuln->xss || $vuln->sql) {
  766. $this->taint->flowpath = array_merge($this->taint->flowpath, $vuln->flowpath);
  767. $this->taint->dependencies = array_merge($this->taint->dependencies, $vuln->dependencies);
  768. }
  769. }
  770. }
  771. if (count($function->alwaysVulnerable) > 0) {
  772. foreach ($function->alwaysVulnerable as $toCloneVuln) {
  773. $vuln = clone $toCloneVuln;
  774. $vuln->flowpath = array_merge($vuln->flowpath, array(printNode($node)));
  775. $vuln->dependencies = array_merge($vuln->dependencies, $this->dependencies);
  776. $this->vulnerabilities->add($vuln);
  777. }
  778. }
  779. if (count($function->globalVulnerable) > 0) {
  780. foreach ($function->globalVulnerable as $global => $vulns) {
  781. $varValueConfigurations = $this->vScope->getVariableValueConfigurations($this->dependencies);
  782. foreach ($varValueConfigurations as $varValueConfiguration) {
  783. $tmpTaint = $varValueConfiguration->getVariableValue($global);
  784. if ($tmpTaint !== null) {
  785. foreach ($vulns as $vuln) {
  786. if ($tmpTaint->xss && $vuln->xss ||
  787. $tmpTaint->sql && $vuln->sql
  788. ) {
  789. $vuln->flowpath = array_merge($vuln->flowpath, array(printNode($node)), $tmpTaint->flowpath);
  790. $vuln->dependencies = array_merge($vuln->dependencies, $tmpTaint->dependencies);
  791. $this->vulnerabilities->add($vuln);
  792. }
  793. }
  794. }
  795. }
  796. }
  797. }
  798. if (count($function->returnGlobalVulnerable) > 0) {
  799. foreach ($function->returnGlobalVulnerable as $global => $vulns) {
  800. $varValueConfigurations = $this->vScope->getVariableValueConfigurations($this->dependencies);
  801. foreach ($varValueConfigurations as $varValueConfiguration) {
  802. $tmpTaint = $varValueConfiguration->getVariableValue($global);
  803. if ($tmpTaint !== null) {
  804. foreach ($vulns as $vuln) {
  805. if ($tmpTaint->xss && $vuln->xss) {
  806. $this->taint->xss = true;
  807. $this->taint->userInput = true;
  808. }
  809. if ($tmpTaint->sql && $vuln->sql) {
  810. $this->taint->sql = true;
  811. $this->taint->userInput = true;
  812. }
  813. if (($tmpTaint->xss && $vuln->xss) || ($tmpTaint->sql && $vuln->sql)) {
  814. $this->taint->flowpath = array_merge($this->taint->flowpath, $vuln->flowpath, $tmpTaint->flowpath);
  815. $this->taint->dependencies = array_merge($this->taint->dependencies, $vuln->dependencies, $tmpTaint->dependencies);
  816. }
  817. }
  818. }
  819. }
  820. }
  821. }
  822. if (count($function->globalSideEffectAlways) > 0) {
  823. $varValueConfigurations = $this->vScope->getVariableValueConfigurations($this->dependencies);
  824. foreach ($varValueConfigurations as $varValueConfiguration) {
  825. foreach ($function->globalSideEffectAlways as $global => $taint) {
  826. $varValueConfiguration->setVariableValue($taint, $global);
  827. }
  828. }
  829. }
  830. if (count($function->vulnerableParameters) > 0 ||
  831. count($function->returnVulnerableParameters) > 0 ||
  832. count($function->globalSideEffectParameter) > 0
  833. ) {
  834. $taintTraverser = new PHPParser_NodeTraverser;
  835. $taintVisitor = new BodyVisitor();
  836. $taintVisitor->setDependencies($this->dependencies);
  837. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  838. $taintTraverser->addVisitor($taintVisitor);
  839. $args = $node->args;
  840. foreach ($args as $nr => $arg) {
  841. $varValueConfigurations = $this->vScope->getVariableValueConfigurations($this->dependencies);
  842. foreach ($varValueConfigurations as $varValueConfiguration) {
  843. $taintVisitor->setVScope($varValueConfiguration);
  844. $taintTraverser->traverse(array($arg->value));
  845. $taintInfo = $taintVisitor->getTaint();
  846. if (isset($function->vulnerableParameters[$nr])) {
  847. foreach ($function->vulnerableParameters[$nr] as $vulnParm) {
  848. if (($taintInfo->xss && $vulnParm->xss) || ($taintInfo->sql && $vulnParm->sql)) {
  849. $vulnParm->flowpath = array_merge($vulnParm->flowpath, array(printNode($node)), $taintInfo->flowpath);
  850. $vulnParm->dependencies = array_merge($vulnParm->dependencies, $taintInfo->dependencies);
  851. $this->vulnerabilities->add($vulnParm);
  852. }
  853. elseif ($vulnParm->sql && $taintInfo->userInput) {
  854. if (substr($taintInfo, 0, 1) !== "{" &&
  855. substr($taintInfo, -1) !== "}" &&
  856. preg_match_all("/(.*){USERINPUT[_XSQL]+}/mUs", $taintInfo->value, $matches) > 0
  857. ) {
  858. $acc = 0;
  859. foreach ($matches[1] as $match) {
  860. $acc += substr_count($match, "'");
  861. $acc += substr_count($match, '"');
  862. if ($acc % 2 == 0) {
  863. array_unshift($taintInfo->flowpath, printNode($node));
  864. $taintInfo->dependencies = array_merge($this->dependencies, $taintInfo->dependencies);
  865. $vulnerability = new VulnerabilityDescription();
  866. $vulnerability->sql = true;
  867. $vulnerability->flowpath = $taintInfo->flowpath;
  868. $vulnerability->dependencies = $taintInfo->dependencies;
  869. $vulnerability->description = "SQL VULNERABILITY FOUND AT mysql_query WITH MISSING QUOTING IN FILE " . $node->getFilename() . " LINE " . $node->getLine();
  870. $this->vulnerabilities->add($vulnerability);
  871. }
  872. }
  873. }
  874. }
  875. }
  876. }
  877. if (isset($function->returnVulnerableParameters[$nr])) {
  878. foreach ($function->returnVulnerableParameters[$nr] as $vulnParm) {
  879. if ($taintInfo->xss && $vulnParm->xss) {
  880. $this->taint->xss = true;
  881. $this->taint->userInput = true;
  882. }
  883. if ($taintInfo->sql && $vulnParm->sql) {
  884. $this->taint->sql = true;
  885. $this->taint->userInput = true;
  886. }
  887. if (($taintInfo->xss && $vulnParm->xss) || ($taintInfo->sql && $vulnParm->sql)) {
  888. $this->taint->flowpath = array_merge($this->taint->flowpath, $taintInfo->flowpath, $vulnParm->flowpath);
  889. $this->taint->dependencies = array_merge($this->taint->dependencies, $vulnParm->dependencies, $taintInfo->dependencies);
  890. }
  891. }
  892. }
  893. if (isset($function->globalSideEffectParameter[$nr])) {
  894. foreach ($function->globalSideEffectParameter[$nr] as $global => $parmTaint) {
  895. if ($taintInfo->xss || $taintInfo->sql || $taintInfo->userInput) {
  896. $varValueConfiguration->setVariableValue($parmTaint, $global);
  897. $parmTaint->flowpath = array_merge($parmTaint->flowpath, $taintInfo->flowpath, array(printNode($node)));
  898. $parmTaint->dependencies = array_merge($parmTaint->dependencies, $taintInfo->dependencies);
  899. }
  900. }
  901. }
  902. }
  903. }
  904. }
  905. } else if ($funcName == "dirname") {
  906. $taintTraverser = new PHPParser_NodeTraverser;
  907. $taintVisitor = new BodyVisitor();
  908. $taintVisitor->setDependencies($this->dependencies);
  909. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  910. $taintTraverser->addVisitor($taintVisitor);
  911. $args = $node->args;
  912. $varValueConfigurations = $this->vScope->getVariableValueConfigurations($this->dependencies);
  913. foreach ($varValueConfigurations as $varValueConfiguration) {
  914. $taintVisitor->setVScope($varValueConfiguration);
  915. $taintTraverser->traverse(array($args[0]->value));
  916. $taintInfo = $taintVisitor->getTaint();
  917. $this->taint->value = dirname($taintInfo->value);
  918. }
  919. } else if ($funcName == "array_keys") {
  920. $taintTraverser = new PHPParser_NodeTraverser;
  921. $taintVisitor = new BodyVisitor();
  922. $taintVisitor->setVScope($this->vScope);
  923. $taintVisitor->setDependencies($this->dependencies);
  924. $taintVisitor->setVulnerabilityStorage($this->vulnerabilities);
  925. $taintTraverser->addVisitor($taintVisitor);
  926. $args = $node->args;
  927. $varValueConfigurations = $this->vScope->getVariableValueConfigurations($this->dependencies);
  928. foreach ($varValueConfigurations a

Large files files are truncated, but you can click here to view the full file