Fix for Xss injector

  1. function page_xss_injector() {
  2.     for($i=0;$i<2;$i++) {
  3.       // Extract the forms url and id
  4.       $form_details = db_fetch_array(db_query("SELECT f.id,l.path FROM {crawler_forms} f INNER JOIN {crawler_links} l ON f.page_id = l.id WHERE status = 2 LIMIT 1"));
  5.       // Visit that url
  6.       $obj = new drupal_security_scanner_test();
  7.       $session_cookie = variable_get('security_scanner_cookie','');
  8.       $obj->curl_options = array(
  9.         CURLOPT_COOKIE => $session_cookie,
  10.       );
  11.       //$obj->drupalGet($form_details['path']);
  12.       $html = $obj->drupalGet('http://localhost/soc2008/?q=node/add/page');
  13.       $obj->parse();
  14.       // Selecting only textareas and input type = 'text' before seeding
  15.       // (already included inside handleFormModified, but repeated to build the array of the value to seed)
  16.       $all_inputs = $obj->elements->xpath("//input[@type='text']|//textarea");
  17.       //echo "<pre>".print_r($all_inputs,1)."</pre>";
  18.       foreach ($all_inputs as $input) {
  19.         $name = (string)$input->attributes()->name;
  20.         $edit[$name] = "<\script\>alert('xss');</\script\>";
  21.       }
  22.       echo "<pre>".print_r($edit,1)."</pre>";
  23.       // $obj->drupalPostModified($html, $form_details['id'], $form_state, TRUE);
  24.       $obj->drupalPostModified($html, 'edit-page-node-form', $edit, TRUE);
  25.       die;
  26.     }
  27.  
  28.  
  29.  
  30. // Function drupalPostModified and handleFormModified (I have to change its name)
  31.  
  32. function drupalPostModified($html, $form_id, $edit, $submit, $tamper = FALSE) {
  33.     $submit_matches = FALSE;
  34.     if ($this->parse()) {
  35.       $edit_save = $edit;
  36.       // Let's iterate over all the forms.
  37.       $forms = $this->elements->xpath("//input[@id='".$form_id."']/parent::*");
  38.       foreach ($forms as $form) {
  39.         if ($tamper) {
  40.           // @TODO: this will be Drupal specific. One needs to add the build_id
  41.           // and the token to $edit then $post that.
  42.         }
  43.         else {
  44.           // We try to set the fields of this form as specified in $edit.
  45.           $edit = $edit_save;
  46.           $post = array();
  47.           $upload = array();
  48.           $submit_matches = $this->handleFormModified($post, $edit, $upload, $submit, $form);
  49.           $action = isset($form['action']) ? $this->getAbsoluteUrl($form['action']) : $this->getUrl();
  50.         }
  51.         // We post only if we managed to handle every field in edit and the
  52.         // submit button matches.
  53.         if (!$edit && $submit_matches) {
  54.           // This part is not pretty. There is very little I can do.
  55.           if ($upload) {
  56.             foreach ($post as &$value) {
  57.               if (strlen($value) > 0 && $value[0] == '@') {
  58.                 $this->fail(t("Can't upload and post a value starting with @"));
  59.                 return FALSE;
  60.               }
  61.             }
  62.             foreach ($upload as $key => $file) {
  63.               $post[$key] = '@' . realpath($file);
  64.             }
  65.           }
  66.           else {
  67.             $post_array = $post;
  68.             $post = array();
  69.             foreach ($post_array as $key => $value) {
  70.               // Whether this needs to be urlencode or rawurlencode, is not
  71.               // quite clear, but this seems to be the better choice.
  72.               $post[] = urlencode($key) . '=' . urlencode($value);
  73.             }
  74.             $post = implode('&', $post);
  75.           }
  76.           $out = $this->curlExec(array(CURLOPT_URL => $action, CURLOPT_POSTFIELDS => $post, CURLOPT_POST => TRUE));
  77.           // Ensure that any changes to variables in the other thread are picked up.
  78.           $this->refreshVariables();
  79.           return $out;
  80.         }
  81.       }
  82.       // We have not found a form which contained all fields of $edit.
  83.       foreach ($edit as $name => $value) {
  84.         $this->fail(t('Failed to set field @name to @value', array('@name' => $name, '@value' => $value)));
  85.       }
  86.     }
  87.   }
  88.  
  89.  
  90. protected function handleFormModified(&$post, &$edit, &$upload, $submit, $form) {
  91.     // Retrieve the form elements.
  92.     $elements = $form->xpath("//input[@type='text']|//textarea");
  93.     $submit_matches = FALSE;
  94.     foreach ($elements as $element) {
  95.       // SimpleXML objects need string casting all the time.
  96.       $name = (string)$element['name'];
  97.       // This can either be the type of <input> or the name of the tag itself
  98.       // for <select> or <textarea>.
  99.       $type = isset($element['type']) ? (string)$element['type'] : $element->getName();
  100.       $value = isset($element['value']) ? (string)$element['value'] : '';
  101.       $done = FALSE;
  102.       if (isset($edit[$name])) {
  103.         switch ($type) {
  104.           case 'text':
  105.           case 'textarea':
  106.           case 'password':
  107.             $post[$name] = $edit[$name];
  108.             unset($edit[$name]);
  109.             break;
  110.           case 'radio':
  111.             if ($edit[$name] == $value) {
  112.               $post[$name] = $edit[$name];
  113.               unset($edit[$name]);
  114.             }
  115.             break;
  116.           case 'checkbox':
  117.             // To prevent checkbox from being checked.pass in a FALSE,
  118.             // otherwise the checkbox will be set to its value regardless
  119.             // of $edit.
  120.             if ($edit[$name] === FALSE) {
  121.               unset($edit[$name]);
  122.               continue 2;
  123.             }
  124.             else {
  125.               unset($edit[$name]);
  126.               $post[$name] = $value;
  127.             }
  128.             break;
  129.           case 'select':
  130.             $new_value = $edit[$name];
  131.             $index = 0;
  132.             $key = preg_replace('/\[\]$/', '', $name);
  133.             $options = $this->getAllOptions($element);
  134.             foreach ($options as $option) {
  135.               if (is_array($new_value)) {
  136.                 $option_value= (string)$option['value'];
  137.                 if (in_array($option_value, $new_value)) {
  138.                   $post[$key . '[' . $index++ . ']'] = $option_value;
  139.                   $done = TRUE;
  140.                   unset($edit[$name]);
  141.                 }
  142.               }
  143.               elseif ($new_value == $option['value']) {
  144.                 $post[$name] = $new_value;
  145.                 unset($edit[$name]);
  146.                 $done = TRUE;
  147.               }
  148.             }
  149.             break;
  150.           case 'file':
  151.             $upload[$name] = $edit[$name];
  152.             unset($edit[$name]);
  153.             break;
  154.         }
  155.       }
  156.       if (!isset($post[$name]) && !$done) {
  157.         switch ($type) {
  158.           case 'textarea':
  159.             $post[$name] = (string)$element;
  160.             break;
  161.           case 'select':
  162.             $single = empty($element['multiple']);
  163.             $first = TRUE;
  164.             $index = 0;
  165.             $key = preg_replace('/\[\]$/', '', $name);
  166.             $options = $this->getAllOptions($element);
  167.             foreach ($options as $option) {
  168.               // For single select, we load the first option, if there is a
  169.               // selected option that will overwrite it later.
  170.               if ($option['selected'] || ($first && $single)) {
  171.                 $first = FALSE;
  172.                 if ($single) {
  173.                   $post[$name] = (string)$option['value'];
  174.                 }
  175.                 else {
  176.                   $post[$key . '[' . $index++ . ']'] = (string)$option['value'];
  177.                 }
  178.               }
  179.             }
  180.             break;
  181.           case 'file':
  182.             break;
  183.           case 'submit':
  184.           case 'image':
  185.             if ($submit == $value) {
  186.               $post[$name] = $value;
  187.               $submit_matches = TRUE;
  188.             }
  189.             break;
  190.           case 'radio':
  191.           case 'checkbox':
  192.             if (!isset($element['checked'])) {
  193.               break;
  194.             }
  195.             // Deliberate no break.
  196.           default:
  197.             $post[$name] = $value;
  198.         }
  199.       }
  200.     }
  201.     return $submit_matches;
  202.   }