r36281 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r36280‎ | r36281 | r36282 >
Date:09:43, 14 June 2008
Author:tstarling
Status:old
Tags:
Comment:
Scanner for classic register_globals vulnerabilities. Also detects some kinds of plain XSS vulnerability.
Modified paths:
  • /trunk/tools/rg-vuln-check (added) (history)
  • /trunk/tools/rg-vuln-check/README (added) (history)
  • /trunk/tools/rg-vuln-check/conf.php.sample (added) (history)
  • /trunk/tools/rg-vuln-check/rg-vuln-check.php (added) (history)

Diff [purge]

Index: trunk/tools/rg-vuln-check/conf.php.sample
@@ -0,0 +1,12 @@
 2+<?php
 3+
 4+# Copy this file to conf.php and change the following variables
 5+
 6+# Set this to the base URL where all the scripts to be tested are kept. It
 7+# should be configured with register_globals, display_errors and open_basedir
 8+# enabled.
 9+$jailUrl = 'http://localhost/jail';
 10+
 11+# Set this to the local directory corresponding to the URL
 12+$jailDir = '/var/www/mediawiki';
 13+
Index: trunk/tools/rg-vuln-check/rg-vuln-check.php
@@ -0,0 +1,209 @@
 2+#!/usr/bin/env php
 3+<?php
 4+
 5+if ( php_sapi_name() != 'cli' ) {
 6+ echo "This script must be run on the command line\n";
 7+ exit( 1 );
 8+}
 9+
 10+$options = array();
 11+
 12+if ( in_array( '-v', $argv ) ) {
 13+ $options['verbose'] = true;
 14+ $argv = array_diff( $argv, array( '-v' ) );
 15+}
 16+if ( in_array( '--opcodes', $argv ) ) {
 17+ $options['opcodes'] = true;
 18+ $argv = array_diff( $argv, array( '--opcodes' ) );
 19+}
 20+
 21+if ( count( $argv ) <= 1 ) {
 22+ echo "Usage: php {$argv[0]} [-v] [--opcodes] <filename> [<filename> ...]\n";
 23+ exit( 1 );
 24+}
 25+
 26+$confFile = dirname( __FILE__ ) . '/conf.php';
 27+if ( !file_exists( $confFile ) ) {
 28+ echo "Configuration file not found\n";
 29+ echo "Copy conf.php.sample to conf.php, and change the settings to suit your installation.\n";
 30+ exit( 1 );
 31+}
 32+
 33+$cvc = new ClassicVulnerabilityCheck( $options );
 34+$cvc->readConf( $confFile );
 35+
 36+array_shift( $argv );
 37+$good = true;
 38+foreach ( $argv as $file ) {
 39+ $good = $good && $cvc->check( $file );
 40+}
 41+
 42+exit( $good ? 0 : 1 );
 43+
 44+class ClassicVulnerabilityCheck {
 45+ /**
 46+ * Set this to the base URL where all the scripts to be tested are kept. It
 47+ * should be configured with register_globals, display_errors and open_basedir
 48+ * enabled.
 49+ */
 50+ var $jailDir = '/home/tstarling/src/mediawiki';
 51+
 52+ /**
 53+ * Set this to the local directory corresponding to the URL
 54+ */
 55+ var $jailUrl = 'http://shimmer/jail';
 56+
 57+ /*
 58+ * Be verbose
 59+ */
 60+ var $verbose = false;
 61+
 62+ /**
 63+ * Dump parsekit output
 64+ */
 65+ var $opcodes = false;
 66+
 67+ function __construct( $options ) {
 68+ foreach ( $options as $name => $value ) {
 69+ $this->$name = $value;
 70+ }
 71+ }
 72+
 73+ function readConf( $filename ) {
 74+ $jailDir = $this->jailDir;
 75+ $jailUrl = $this->jailUrl;
 76+ include( $filename );
 77+ $this->jailDir = $jailDir;
 78+ $this->jailUrl = $jailUrl;
 79+ }
 80+
 81+ function compile( $filename ) {
 82+ // This call needs to be in its own function, otherwise it segfaults
 83+ return array( $parseInfo, $errors );
 84+ }
 85+
 86+ function check( $filename ) {
 87+ $errors = false;
 88+ $parseInfo = parsekit_compile_file( $filename, $errors );
 89+
 90+ if ( $errors ) {
 91+ echo "Errors encountered:\n";
 92+ print_r( $errors );
 93+ return false;
 94+ }
 95+
 96+ if ( $this->opcodes ) {
 97+ var_dump( $parseInfo );
 98+ }
 99+
 100+ $globals = array_keys( $this->getGlobalsFromParseInfo( $parseInfo ) );
 101+
 102+ if ( !$globals ) {
 103+ if ( $this->verbose ) {
 104+ print "$filename: SECURE (No globals referenced)\n";
 105+ }
 106+ return true;
 107+ }
 108+
 109+ if ( $this->verbose ) {
 110+ print "Globals referenced: " . implode( ', ', $globals ) . "\n";
 111+ }
 112+
 113+ $filename = realpath( $filename );
 114+ if ( substr( $filename, 0, strlen( $this->jailDir ) + 1 ) !== $this->jailDir . '/' ) {
 115+ echo "The file specified is not in the jail directory\n";
 116+ return false;
 117+ }
 118+
 119+ $ret = true;
 120+
 121+ // Need to do each global separately because some globals will
 122+ // generate a fatal when set to a string, masking the vulnerability
 123+ foreach ( $globals as $i => $global ) {
 124+ $url = $this->jailUrl . substr( $filename, strlen( $this->jailDir ) ) . '?';
 125+ $url .= urlencode( $global ) . '=' . urlencode( "/TEST_GLOBAL_$i<>" );
 126+ if ( $this->verbose ) {
 127+ echo "Fetching $url\n";
 128+ }
 129+
 130+ $curl = curl_init( $url );
 131+ curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
 132+ $html = curl_exec( $curl );
 133+ $lines = explode( "\n", $html );
 134+
 135+ if ( $this->verbose ) {
 136+ echo str_repeat( '-', 76 );
 137+ echo "\n$html\n";
 138+ echo str_repeat( '-', 76 ) . "\n";
 139+ }
 140+
 141+ if ( strpos( $html, "TEST_GLOBAL_$i<>" ) ) {
 142+ echo "$filename XSS VULNERABILITY \$$global\n";
 143+ $ret = false;
 144+ }
 145+ foreach ( $lines as $line ) {
 146+ if ( preg_match( "/TEST_GLOBAL_$i.*failed to open stream/", $line ) ) {
 147+ echo "$filename INCLUSION VULNERABILITY \$$global\n";
 148+ $ret = false;
 149+ }
 150+ }
 151+ }
 152+ if ( $ret ) {
 153+ if ( $this->verbose ) {
 154+ echo "$filename SECURE\n";
 155+ }
 156+ }
 157+ return $ret;
 158+ }
 159+
 160+ function getGlobalsFromFunction( $opArray ) {
 161+ $globals = array();
 162+ foreach ( $opArray as $opLine ) {
 163+ if ( $opLine['opcode_name'] == 'ZEND_FETCH_W'
 164+ && $opLine['op1']['type_name'] == 'IS_CONST' )
 165+ {
 166+ $globals[$opLine['op1']['constant']] = true;
 167+ }
 168+ }
 169+ return $globals;
 170+ }
 171+
 172+ function getGlobalsFromTop( $opArray ) {
 173+ // Start with ZEND_FETCH_W calls
 174+ $globals = $this->getGlobalsFromFunction( $opArray );
 175+ // Now add local variable references
 176+ foreach ( $opArray as $opLine ) {
 177+ if ( isset( $opLine['op1']['varname'] ) ) {
 178+ $globals[$opLine['op1']['varname']] = true;
 179+ }
 180+ if ( isset( $opLine['op2']['varname'] ) ) {
 181+ $globals[$opLine['op2']['varname']] = true;
 182+ }
 183+ }
 184+ return $globals;
 185+ }
 186+
 187+ function getGlobalsFromParseInfo( $parseInfo ) {
 188+ // Get globals referenced in the file scope
 189+ $globals = $this->getGlobalsFromTop( $parseInfo['opcodes'] );
 190+
 191+ // Get globals referenced in global functions
 192+ if ( isset( $parseInfo['function_table'] ) ) {
 193+ foreach ( $parseInfo['function_table'] as $function ) {
 194+ $globals += $this->getGlobalsFromFunction( $function['opcodes'] );
 195+ }
 196+ }
 197+
 198+ // Get globals from class member functions
 199+ if ( isset( $parseInfo['class_table'] ) ) {
 200+ foreach ( $parseInfo['class_table'] as $class ) {
 201+ if ( isset( $class['function_table'] ) ) {
 202+ foreach ( $class['function_table'] as $function ) {
 203+ $globals += $this->getGlobalsFromFunction( $function['opcodes'] );
 204+ }
 205+ }
 206+ }
 207+ }
 208+ return $globals;
 209+ }
 210+}
Property changes on: trunk/tools/rg-vuln-check/rg-vuln-check.php
___________________________________________________________________
Added: svn:eol-style
1211 + native
Added: svn:executable
2212 + *
Index: trunk/tools/rg-vuln-check/README
@@ -0,0 +1,23 @@
 2+This is a tool to check for the classic register_globals/url_fopen vulnerability
 3+in any PHP script. Such a vulnerability looks like this:
 4+
 5+<?php
 6+require_once( "$IP/includes/SpecialPage.php" );
 7+?>
 8+
 9+When register_globals is enabled, an attacker can pass any value they like for
 10+$IP, and when url_fopen is enabled, or if the server is Windows with SMB client
 11+capabilities, the script can be fetched from a remote source, making this an
 12+arbitrary script execution vulnerability.
 13+
 14+This test script uses the parsekit extension to analyse a file and extract its
 15+referenced global variables. Then it invokes the file via a specially configured
 16+webserver, which has register_globals enabled. The script is invoked with a
 17+value for each global in turn, and the output is analysed for evidence of XSS
 18+and inclusion vulnerabilities.
 19+
 20+The configuration of the webserver should be appropriate for your level of trust
 21+in the code you are testing. It might be open_basedir/safemode, chroot CGI or
 22+a firewalled virtual server. The configuration variables register_globals and
 23+display_errors must be enabled for the tester to work, and open_basedir is
 24+recommended.

Status & tagging log