Popular Vulnerable Code

State –Defense in Depth

Details

Affected Software:Adrenalin C&C

Fixed in Version:Not Patched

Issue Type:Defense in Depth

Original Code:Found Here

Details

First,I’ll talk about a couple of interesting things about this bug that cannot be seen from just the code sample. When I received this sample,it was encoded with Zend Guard. While the Zend Guard encoding was easily defeated,it is interesting to see that these malware authors are interested in protecting their intellectual property. Once again,the malware industry doesn’t get a magical free pass on all the things traditional development shops face. Monetizing,feature requests,protecting IP,and even security problems are issues all dev shops face.


After the code was decoded,it was quickly apparent that this file contained several routines for dealing with uploding files to the web C&C. I pulled out a routine that I thought was particularly interesting for this week’s code sample. The sample takes several variables from user/attacker controlled parameters (lines 6,8,and 9). One of these variables ($logfolder) is passed directly to fopen(). Fopen is an interesting API. In this code sample,fopen() is intended to open a file from the local filesystem. There is no directory traversal check for $logfolder,so the attacker is free to pass a simple ../../../ in the $logfolder variable and control where the txt file gets written to. In addition to directory traversal bugs,fopen() can actually open more than just local files. fopen()supports a number of schemes such as:http://,ftp://,php://,ssh2://,and several others. A full list of protocols supported by fopen() can be found here:http://www.php.net/manual/en/wrappers.php. Because the $logfolder variable is the first variable passed to fopen(),the attacker can supply any of these protocols to fopen(). Using these protocols,the attacker can cause the C&C to make arbitrary requests to external servers. Full compromise of theC&C web server would be difficult,but information leakage can definitely be accomplished.

Vulnerable Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<?php
...snip...
function srch( )
{
  set_time_limit( 0 );
  $word = $_REQUEST['word'];
  $word2 = $word;
  $logfolder = $_REQUEST['infile'];
  $arch = $_REQUEST['xxx'];
  if ( $word != "" )
  {
    $word = explode( "\r\n", $word );
    $wordc = count( $word );
    $hl9 = fopen( $logfolder."/.out.txt", "w" );
    fclose( $hl9 );
    $dir = opendir( $logfolder );
    $finded = "";
    while ( $file = readdir( $dir ) )
    {
      if ( !( $file != "." ) || !( $file != ".." ) || !( $file != ".out.txt" ) || !( substr( $file, -4 ) == ".txt" ) )
      {
        $hl = fopen( $logfolder."/".$file, "rb" );
        $readsz = filesize( $logfolder."/".$file );
        if ( $readsz < 1041076 )
        {
          $readszR = $readsz;
        }
        else
        {
          $readszR = 1041076;
          $readsz -= 1041076;
        }
        while ( $data = fread( $hl, $readszR ) )
        {
          $pos = 0;
          $posC = 0;
          $posS = 0;
          while ( $pos = strpos( $data, "[IP:", $pos ) )
          {
            $pos = strpos( $data, "]", $pos ) + 1;
            if ( $pos < $posC )
            {
              break;
            }
            else
            {
              $posC = $pos;
              $lent = $pos - $posS;
              unset( $cutblock );
              $cutblock = substr( $data, $posS, $lent );
              $rd = 0;
              for ( ; $rd < $wordc; ++$rd )
              {
              }
              if ( !( $word[$rd] != "" ) || !( $ftmp = strpos( $cutblock, $word[$rd], 0 ) ) )
              {
                $hl9 = fopen( $logfolder."/.out.txt", "ab+" );
                fwrite( $hl9, $cutblock );
                fclose( $hl9 );
              }
            }
            unset( $rd );
            unset( $lent );
            unset( $ftmp );
            unset( $cutblock );
            $posS = $pos;
          }
          unset( $data );
          if ( $readsz < 1041076 )
          {
            $readszR = $readsz;
          }
          else
          {
            $readszR = 1041076;
            $readsz -= 1041076;
          }
        }
        unset( $data );
        fclose( $hl );
      }
    }
    if ( 0 < filesize( $logfolder."/.out.txt" ) )
    {
      $hl9 = fopen( $logfolder."/.out.txt", "r" );
      $finded = fread( $hl9, filesize( $logfolder."/.out.txt" ) );
      fclose( $hl9 );
      if ( $arch == 1 )
      {
        header( "Content-type:application/octet-stream" );
        $cl_Zip = new cl_zip( );
        $cl_Zip->onaddfile( $finded, "log".time( ).".txt" );
        header( "Content-Length:".strlen( $cl_Zip->ondumpfileout( ) ) );
        header( "Content-disposition:attachment;filename=log".time( ).".zip" );
        echo $cl_Zip->ondumpfileout( );
        exit( );
      }
      return $finded;
    }
  }
}
...snip...
?>

Action –Defense in Depth

Details

Affected Software:PixelPost

Fixed in Version:?

Issue Type:Insecure password reset functionality

Original Code:Found Here

Details

This week’s bug is more of a design issue as opposed to an implementation issue. I actually first heard about this code from SkullSecurity’s excellent articles on “Hacking Crappy Password Resets” articles published in late March. SkullSecurity does an excellent job of explaining that line 31(the line that does the actual password generation) is full of bad security design. First,the password reset code is using the MD5() function in PHP. MD5() takes a string and returns a MD5 hash of that string. In this password reset code,we see that we are hashing the value of:‘time’. rand(1,16000). SkullSecurity astutely points out that the password reset code is using the string ‘time’,NOT the function time() which would return the number of seconds since epoch. The string ‘time’ has a random number between 1 and 16000 appended to it and is passed to MD5(). Assuming rand() generates a truly random number between 1 and 16000,we only have 16000 possible combinations for the newly reset password. Unfortunately,in many circumstances,rand() doesn’t generate a truly random number. Many times,rand() will use system time as a seed value and produce a number that is predictable. In many cases,it may be possible to predict or leak system time for the machine generating the random password. Stefan Esser wrote an excellent article on the challenges of generating random numbers in PHP which covers many of these scenarios.

So what should the author do? Change ‘time’ to time()? Even if the author changed the string ‘time’ to the function time(),time() in PHP only returns seconds since epoch time. If an attacker can leak system time,they’ll still be able to predict the password reset value…

Developers Solution

<?php...snip...// forgot password?if(isset($_GET['x']) &&$_GET['x']=='passreminder'){echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><head><title>$admin_lang_pw_title</title><meta http-equiv="Content-Type"content="text/html;charset=utf-8"/></head><body><p style="border:solid 2px;padding:5px;color:red;font-weight:bold;font-size:11px;margin-left:auto;margin-right:auto;margin-top:10%;font-family:verdana,arial,sans-serif;text-align:center;">';if ($cfgrow['admin']!= $_POST['user']){echo "<span class=\"confirm\">$admin_lang_pw_wronguser</span><br />";echo "<br /><a href='index.php'>$admin_lang_pw_back</a></body></html>";die()}if ($cfgrow['email']== ""){echo "<span class=\"confirm\">$admin_lang_pw_noemail</span><br />";echo "<br /><a href='index.php' >$admin_lang_pw_back </a></body></html>";die()}if (strtolower($cfgrow['email'])==strtolower($_POST['reminderemail'])){// generate a random new pass$user_pass = substr( MD5('time' . rand(1,16000)),0,6);$query = "update ".$pixelpost_db_prefix."config set password=MD5('$user_pass') where admin='".$cfgrow['admin']."'";if(mysql_query($query)){$subject = "$admin_lang_pw_subject";$body = "$admin_lang_pw_text_1 \n\n";$body .= "$admin_lang_pw_usertext ".$cfgrow['admin']."\n";$body .= "$admin_lang_pw_mailtext ".$cfgrow['email']."\n\n";$body .= "$admin_lang_pw_newpw $user_pass";$body .= "\n\n$admin_lang_pw_text_7".$cfgrow['siteurl']."admin $admin_lang_pw_text_8";$headers = "Content-type:text/plain;charset=UTF-8\n";$headers .= "$admin_lang_pw_text_2 <".$cfgrow['email'].">\n";$recipient_email = $cfgrow['email'];if (mail($recipient_email,$subject,$body,$headers)){echo "$admin_lang_pw_text_3".$cfgrow['email']}else{echo "$admin_lang_pw_text_3"}echo "<br /><a href='index.php' >$admin_lang_pw_back </a></body></html>";die()}else{$dberror = mysql_error();echo "$admin_lang_pw_text_5 ".$dberror ."$admin_lang_pw_text_5 ";echo "<br /><a href='index.php' >$admin_lang_pw_back </a></body></html>";die()}}else{echo "<span class=\"confirm\">$admin_lang_pw_notsent</span><br />";echo "<br /><a href='index.php'>$admin_lang_pw_back </a></body></html>";die()}// end else (strtolower($cfgrow['email'])==strtolower($_POST['reminderemail']) &$cfgrow['email']!= "")} // end if($_GET['x']=='passreminder')?>

Spinning –Defense in Depth

Details

Affected Software:Dojo Toolkit

Fixed in Version:1.4.1

Issue Type:Defense in Depth

Original Code: Found Here

Description

This was a vulnerability affecting the Dojo toolkit.   Apparently,the dojo toolkit shipped with a SWF file that had a few vulnerabilities.  This particular vulnerability affected one of those SWF files.  First,SWF files are compiled files,however they can be decompiled.  Unlike traditional server side web application languages (PHP,ASP,JSP…etc),SWF files are downloaded and rendered on the clientside.  Decompiling the SWF file gives the attacker full access to the ActionScript source code for the SWF application.

In this particular SWF file,we see that the developers explicitly set the Security.allowDomain to “*”.  This makes it so SWF flies from other,external domains can include the Dojo toolkit SWF file and script/access its internal functionality.

The Dojo toolkit devs fixed this particular issue by removing the allowDomain call and adding an Externalinterface call checking to see if a particular wrapper was available in HTML.  If you’re interested in Flash security,an excellent presentation on Flash security given by Stefano Di Paola can be found here:

http://www.slideshare.net/guestb0af15/owasp-wasc-app-sec2007-san-jose-finding-vulnsin-flash-apps

Developers Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class FLVideo extends Sprite {
private var videoUrl:String;
private var video:Video;
private var connection:NetConnection;
private var autoPlay:Boolean;
private var videoStream:NetStream;
private var videoWidth:Number;
private var videoHeight:Number;
private var _currentVideo:VideoContainer;
private var preview:VideoContainer;
private var currentVolume:Number = 1;
private var isFullscreen:Boolean = false;
private var playlist:VideoPlaylist;
private var hasPlaylist:Boolean = false;
private var mode:String = "preview";

public function FLVideo() {
-    Security.allowDomain("*");
+    var secure:* = ExternalInterface.call("swfIsInHTML");
+    if(secure !== true){
+        return;
+    }
+    //Security.allowDomain("*");

stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.addEventListener(Event.RESIZE,onStageResize);
stage.addEventListener(FullScreenEvent.FULL_SCREEN ,onFullscreenChange);
stage.addEventListener(MouseEvent.CLICK,onClick);

var obj:Object = LoaderInfo(this.root.loaderInfo).parameters;
trace(obj)

if(!obj.videoUrl){
obj = {
autoPlay:true,
isDebug:true,
videoUrl:"demo_video.flv"
};
}


// ugh - booleans not coming through
if(obj.autoPlay===true || obj.autoPlay=="true"){
autoPlay = true;
}

if(obj.volume) {
currentVolume = obj.volume;
}

if(obj.isDebug===true || obj.isDebug=="true"){
console.isDebug(true);
Tracer.init({both:true})
Tracer.log("FLVideo initialized...")
}

+    Tracer.log("secure?::",secure)
MovieIdentity.identity = obj.id || "default";
this.playlist = new VideoPlaylist(autoPlay,currentVolume);

if(obj.videoUrl) {
videoUrl = obj.videoUrl;
}

preview = new VideoContainer(videoUrl,autoPlay,currentVolume);
addChild(preview);
provideCallbacks();
}


public function get currentVideo():VideoContainer{

if(mode=="playlist"&&hasPlaylist){
return this.playlist.current;
}else{
return preview;
}
}

Learning - Insecure Logging (Defense in Depth)Learning –Insecure Logging (Defense in Depth)

Details

Affected Software:AskApache Password Protect

Fixed in Version: 4.3.2

Issue Type:Insecure Logging (Defense in Depth)

Original Code: Found Here

Description

This week’s bug was discovered in the AskApache Password Protect plugin for WordPress.  Once again,we are examining “security software” that is designed to provide various security protection mechanisms for a deployed WordPress blog.  The description for the AskApache security plug-in is as follows:

Advanced Security:Password Protection,Anti-Spam,Anti-Exploits,more to come

A very noble effort indeed :)

This vulnerability was in the aa_pp_hashit() function. The aa_pp_hashit() function takes three arguments:$format,$user,and $pass.  The aa_pp_hashit() function then attempts to create a hash containing the creds.  Whenever I see functions utilizing crypto,I’m always reminded of this scene in Office Space .  In this particular patch,vulnerability was in this line:

aa_pp_mess(‘Created ‘.$format.’Hash for ‘.$user.’with Password ‘.$pass);

The aa_pp_mess() function actually logged the clear text username and password before putting it through a hashing function.  There is rarely a need to log a clear text password… in fact,I’m going to go out on a limb here and say there is NEVER a good time when you should log a clear text password.  Even password hashes or other weird representations of passwords shouldn’t be logged.  Logging sensitive data is always tricky.  If you’re logging sensitive data please consider the permissions required to access that sensitive data,ensure the file is properly ACL’d and conduct regular audits of log file access.  Most importantly,ask yourself:  Why do I need to log this data?

The vulnerability was fixed by removing references to user password (and even references to the user that called the function).  Now I just have to figure out why the AskApache devs are passing a default value for $pass :)

Developers Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// aa_pp_hashit
//-------------------------------------------------------------------------------------------
function aa_pp_hashit($format,$user='',$pass=''){
        global $aa_PP;
-   aa_pp_mess('Created '.$format.' Hash for '.$user.' with Password '.$pass);
+   aa_pp_mess('Created '.$format.' Hash');
    $hash='';
    switch ($format){
        case 'TEST':
                $hash=array();
                foreach($aa_PP['algorithms'] as $key=>$value)$hash[]=aa_pp_hashit($key,"test{$key}","test{$key}");
        return $hash;
        break;
        case 'PLAIN':
        $hash=$user.':'.$pass;
        break;
        case 'CRYPT':
        $seed = NULL;
        for ($i = 0;$i <8;$i++) {$seed .= substr('0123456789abcdef',rand(0,15),1);}
        $hash=$user.':'.crypt($pass,"$1$".$seed);
        break;
        case 'SHA1':
        $hash=$user.':{SHA}'.base64_encode(pack("H*",sha1($pass)));
        break;
        case 'MD5':// php.net/crypt.php#73619
        $saltt = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789"),0,8);
        $len = strlen($pass);$text = $pass.'$apr1$'.$saltt;$bin = pack("H32",md5($pass.$saltt.$pass));
        for($i = $len;$i >0;$i -= 16) { $text .= substr($bin,0,min(16,$i));}
        for($i = $len;$i >0;$i >>= 1) { $text .= ($i &1) ? chr(0):$pass{0};}
        $bin = pack("H32",md5($text));
        for($i=0;$i<1000;$i++) { $new = ($i &1) ? $pass:$bin;if ($i % 3) $new .= $saltt;if ($i % 7) $new .= $pass;$new .= ($i &1) ? $bin:$pass;$bin = pack("H32",md5($new));}
        for($i=0;$i<5;$i++) { $k = $i + 6;$j=$i + 12;if($j==16){ $j = 5;} $TRp = $bin[$i].$bin[$k].$bin[$j].$TRp;}
        $TRp = chr(0).chr(0).$bin[11].$TRp;
        $TRp = strtr(strrev(substr(base64_encode($TRp),2)),"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
        "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
        $hash="$user:$"."apr1"."$".$saltt."$".$TRp;
        break;
    }

    return $hash;
}//=========================================================================================================================
// aa_pp_show_encryptions
//-------------------------------------------------------------------------------------------
function aa_pp_show_encryptions($label,$type=0){
    global $aa_PP;
  
    if($type==0)
        {
        ?>
        <p><label><?php _e($label);?><br />
        <select name="aapassformat"id="aapassformat">
        <?php foreach($aa_PP['algorithms'] as $key=>$value){?>
                <option value="<?php echo $key;?>"<?php if($aa_PP['format']==$key)echo ' selected="selected"';elseif($aa_PP['algorithms'][$key]['enabled']!='1')echo ' disabled="disabled"';?>><?php echo $key;?>   </option>
        <?php }?>
        </select>
        </label></p>
     <?php
     }
         elseif($type==3)
         {
     ?>
        <p><label><?php _e($label);?><br />
        <input id="aapassformat"name="aapassformat"type="hidden"value="<?php echo $aa_PP['format'];?>"/></label></p>
        <ul>
        <?php foreach($aa_PP['algorithms'] as $key=>$value){?>
                <li><label><input name="aapassformat"id="aapassformat<?php echo strtolower($key);?>"type="radio"value="<?php echo $key;?>"<?php if($aa_PP['format']==$key)echo 'checked="checked"';
                elseif($aa_PP['algorithms'][$key]['enabled']!='1')echo 'disabled="disabled"';?>/><strong><?php echo $key;?></strong>-
            <?php echo $aa_PP['algorithms'][$key]['desc'];?></label></li>
        <?php }?>
        </ul>
    <?php
    }
    else if($type==4)
        {
     ?>
        <h4><?php _e($label);?></h4>
        <?php foreach($aa_PP['algorithms'] as $key=>$value){?>
                <p><strong><?php echo $key;?></strong>- <?php echo $aa_PP['algorithms'][$key]['desc'];?></p>
        <?php }?>
        <hr style="visibility:hidden;padding-top:.25em;clear:both;"/>
    <?php
    }
}//=========================================================================================================================

// aa_pp_mess
//-------------------------------------------------------------------------------------------
function aa_pp_mess($message=''){
        if(@defined('AA_PP_DEBUG_LOGFILE'))error_log($message,3,AA_PP_DEBUG_LOGFILE);
-        else error_log($message);
+  else if(AA_PP_DEBUG)error_log($message)
    if(AA_PP_DEBUG){ ?><div id="message"style="margin:1em auto;"><p><?php echo $message;?></p></div><?php }
}//=========================================================================================================================
 

More Than One Night - Defense in DepthMore Than One Night – Defense in Depth

Details

Affected Software:WordPress

Fixed in Version:2.1

Issue Type:Defense in Depth

Original Code: Found Here

Description

I found this issue interesting for a couple reasons. Upon first glance,the patch appears to be a defense against SQL Injection and in essence,it is. It seems that the $q[‘cat’] value is controlled by the user and is eventually used to help build a SQL statement. Before the $q[‘cat’] value is used in a SQL statement,it is actually sanitized by the following lines:

$q['cat'] = ”.urldecode($q['cat']).”;

$q['cat'] = addslashes_gpc($q['cat']);

Once the value is sanitized,it is used to build various SQL statements. Now this particular patch was developed by the WordPress team because they discovered that when a user/attacker passes a “.” (period character) to $q[‘cat’],it would cause a SQL error which would be displayed to the user. While a single period character doesn’t give the attacker the ability to execute arbitrary SQL,it does give the attacker an information disclosure bug. In an academic sense however,the attacker has convinced the database that their provided value should be interpreted as code as opposed to data (ala SQL Injection). The reason the period character slips through is because it is not defined as a special character in the addslashes() php function… this could be useful in other situations.

The WordPress prevented the information leak by checking to see if $q[‘cat’] is an integer value. The patch here is a single line fix.

Developers Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// Category stuff

if ((empty($q['cat'])) || ($q['cat'] == '0') ||

// Bypass cat checks if fetching specific posts

( $this->is_single || $this->is_page )) {

$whichcat='';

} else {

$q['cat'] = ''.urldecode($q['cat']).'';

$q['cat'] = addslashes_gpc($q['cat']);

$join = "LEFT JOIN $wpdb->post2cat ON ($wpdb->posts.ID = $wpdb->post2cat.post_id) ";

$cat_array = preg_split('/[,\s]+/',$q['cat']);

$in_cats = $out_cats = '';

foreach ( $cat_array as $cat ) {

+ $cat = intval($cat);

$in = strstr($cat,'-') ? false:true;

$cat = trim($cat,'-');

if ( $in )

$in_cats .= "$cat,". get_category_children($cat,'',',');

else

$out_cats .= "$cat,". get_category_children($cat,'',',');

}

$in_cats = substr($in_cats,0,-2);

$out_cats = substr($out_cats,0,-2);

if ( strlen($in_cats) >0 )

$in_cats = "AND category_id IN ($in_cats)";

if ( strlen($out_cats) >0 )

$out_cats = "AND category_id NOT IN ($out_cats)";

$whichcat = $in_cats . $out_cats;

$distinct = 'DISTINCT';

}

// Category stuff for nice URIs

global $cache_categories;

if ('' != $q['category_name']) {

Three Days – Defense in Depth

Details

Affected Software:WordPress

Fixed in Version:2.8.4

Issue Type:Defense in Depth

Original Code: Found Here

Description

This change was a defense in depth fix for WordPress.  The code for this page comes from the user registration page.  Throughout the code we see handling for various pieces of information associated with the newly created user.  Looking through the code,we see some very fragile security code however this particular patch addressed the handling of passwords in the database.  In the vulnerable version of the code,the WordPress passes the newly created user password directly into the database.  If the WordPress install is ever compromised,the attacker will now have access to the clear text passwords for every user!

The WordPress developers strengthened the security mechanisms around password handling by hashing the password before storing it in the database. 

Although not fixed in this particular patch… if you look closely,there are other vulnerabilities in the user registration code :)

Developers Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
$user_login = $HTTP_POST_VARS['user_login'];

$pass1 = $HTTP_POST_VARS['pass1'];

$pass2 = $HTTP_POST_VARS['pass2'];

$user_email = $HTTP_POST_VARS['user_email'];



if ($user_login == '') {

die ('<strong>ERROR</strong>:Please enter a login.');

}



if ($pass1 == '' || $pass2 == '') {

die ('<strong>ERROR</strong>:Please enter your password twice.');

}



if ($pass1 != $pass2) {

die ('<strong>ERROR</strong>:Please type the same password in the two password fields.');

}

$user_nickname = $user_login;



if ($user_email == '') {

die ('<strong>ERROR</strong>:Please type your e-mail address.');

} else if (!is_email($user_email)) {

die ('<strong>ERROR</strong>:The email address isn\'t correct.');

}



$result = $wpdb->get_results("SELECT user_login FROM $tableusers WHERE user_login = '$user_login'");

if (count($result) >= 1) {

die ('<strong>ERROR</strong>:This login is already registered,please choose another one.');

}

$user_ip = $HTTP_SERVER_VARS['REMOTE_ADDR'] ;

$user_domain = gethostbyaddr($HTTP_SERVER_VARS['REMOTE_ADDR'] );

$user_browser = $HTTP_SERVER_VARS['HTTP_USER_AGENT'];

$user_login = addslashes($user_login);

$pass1 = addslashes($pass1);

$user_nickname = addslashes($user_nickname);

$now = current_time('mysql');

$result = $wpdb->query("INSERT INTO $tableusers

(user_login,user_pass,user_nickname,user_email,user_ip,user_domain,user_browser,dateYMDhour,user_level,user_idmode)

VALUES

-('$user_login','$pass1','$user_nickname','$user_email','$user_ip','$user_domain','$user_browser','$now','$new_users_can_blog','nickname')");

+('$user_login',MD5('$pass1'),'$user_nickname','$user_email','$user_ip','$user_domain','$user_browser','$now','$new_users_can_blog','nickname')");

if ($result == false) {

die ('<strong>ERROR</strong>:Couldn\'t register you... please contact the <a href="mailto:'.$admin_email.'">webmaster</a>!');

}

$stars = '';

for ($i = 0;$i <strlen($pass1);$i = $i + 1) {

$stars .= '*';

}

$message = "New user registration on your blog $blogname:\r\n\r\n";

$message .= "Login:$user_login\r\n\r\nE-mail:$user_email";

@mail($admin_email,"[$blogname] New User Registration",$message);

Slinky - Defense in DepthSlinky –Defense in Depth

Details

Affected Software:Apache webservices

Fixed in Version:revision 697031

Issue Type:Defense in Depth

Original Code: Found Here

Description

This is a defense in depth fix checked into the Apache SVN under web services.  This particular issue prevented a username enumeration/disclosure bug that would occur if a null password was sent with a value username.  The developers fixed this particular issue by logging the failure in the debug log and displaying a generic authentication failure message back to the user.

throw new WSSecurityException(WSSecurityException.FAILURE,
“noPassword”,new Object[]{user})

The defense in depth fix is pretty straightforward… what’s interesting however,is the code surrounding the fix.  A quick examination shows that there is a real possibility that clear text passwords are being logged in the debug log file.  Take the following code for example:

log.debug(“UsernameToken callback password ”+ origPassword);

I hope origPassword doesn’t actually represent a user’s password!

Developers Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java?p2=/webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java&p1=/webservices/wss4j/trunk/src/org/apache/ws/security/processor/UsernameTokenProcessor.java&r1=697031&r2=697030&pathrev=697031&view=diff&diff_format=l

People are like slinkys,they get on your nerves,but its still fun to push them down some stairs.
By Anonymous

public WSUsernameTokenPrincipal handleUsernameToken(Element token,CallbackHandler cb)
throws WSSecurityException {
ut = new UsernameToken(token);
String user = ut.getName();
String password = ut.getPassword();
String nonce = ut.getNonce();
String createdTime = ut.getCreated();
String pwType = ut.getPasswordType();
if (log.isDebugEnabled()) {
log.debug("UsernameToken user "+ user);
log.debug("UsernameToken password "+ password);
}

Callback[] callbacks = new Callback[1];
String origPassword = null;

//
// If the UsernameToken is hashed,then retrieve the password from the callback handler
// and compare directly. If the UsernameToken is in plaintext or of some unknown type,
// then delegate authentication to the callback handler
//
if (ut.isHashed()) {
if (cb == null) {
throw new WSSecurityException(WSSecurityException.FAILURE,"noCallback");
}

WSPasswordCallback pwCb = new WSPasswordCallback(user,WSPasswordCallback.USERNAME_TOKEN);
callbacks[0] = pwCb;
try {
cb.handle(callbacks);
} catch (IOException e) {
if (log.isDebugEnabled()) {
log.debug(e);
}
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
} catch (UnsupportedCallbackException e) {
if (log.isDebugEnabled()) {
log.debug(e);
}
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
}
origPassword = pwCb.getPassword();
if (log.isDebugEnabled()) {
log.debug("UsernameToken callback password "+ origPassword);
}
if (origPassword == null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Callback supplied no password for:"+ user);
+                }
+                throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);

-                throw new WSSecurityException(WSSecurityException.FAILURE,
-                        "noPassword",new Object[]{user});
}
String passDigest = UsernameToken.doPasswordDigest(nonce,createdTime,origPassword);
if (!passDigest.equals(password)) {
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
}
ut.setRawPassword(origPassword);
} else {
if (cb == null) {
throw new WSSecurityException(WSSecurityException.FAILURE,"noCallback");
} else if (!WSConstants.PASSWORD_TEXT.equals(pwType) &&!handleCustomPasswordTypes) {
if (log.isDebugEnabled()) {
log.debug("Authentication failed as handleCustomUsernameTokenTypes is false");
}
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
}
WSPasswordCallback pwCb = new WSPasswordCallback(user,password,
pwType,WSPasswordCallback.USERNAME_TOKEN_UNKNOWN);
callbacks[0] = pwCb;
try {
cb.handle(callbacks);
} catch (IOException e) {
if (log.isDebugEnabled()) {
log.debug(e);
}
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
} catch (UnsupportedCallbackException e) {
if (log.isDebugEnabled()) {
log.debug(e);
}
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
}
ut.setRawPassword(password);
}
WSUsernameTokenPrincipal principal = new WSUsernameTokenPrincipal(user,ut.isHashed());
principal.setNonce(nonce);
principal.setPassword(password);
principal.setCreatedTime(createdTime);
principal.setPasswordType(pwType);

return principal;
}

Elephant –Defense in Depth

Details

Affected Software:Drupal

Fixed in Version:1.2.0

Issue Type:Defense in Depth

Original Code: Found Here

Description

This was a simple change to a security message shown to a user. Although not a critical security fix,this can help users make better decisions with regards to choosing an appropriate password. The code sample provided below shows that Durpal checks for lowercase,uppercase,numbers,and punctuation in the user’s password.

var weaknesses = 0,strength = 100,msg = [];

var hasLowercase = password.match(/[a-z]+/);

var hasUppercase = password.match(/[A-Z]+/);

var hasNumbers = password.match(/[0-9]+/);

var hasPunctuation = password.match(/[^a-zA-Z0-9]+/);

In addition to the checks above,the passwords length is also checked. Durpal assigns the password a “score” of 100 and subtracts from this score if weaknesses are detected. In this case,the Durpal decided that if the password was less than six characters,they would subtract 10 points from the password strength for every character short of 6.

strength -= (6 –password.length) * 10;

Later,the password is put through a “strength scale” where every password that has a score above “80” gets a rating of “strong”.

} else if (strength <80){

indicatorText = translate.good;

} else if (strength <100){

indicatorText = translate.strong;

Using this logic,it was possible for a user to select a 4 character password that was rated as strong. To combat this,the Durpal developers penalized the user 30 points if they selected a password with less than 6 characters,guaranteeing that a password with less than 6 characters would never be rated “strong”.

The safer way to go about this might be to assign a password strength of zero and build up the strength value as conditions are met.

Developers Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65


Drupal.evaluatePasswordStrength = function (password,translate) {

var weaknesses = 0,strength = 100,msg = [];
var hasLowercase = password.match(/[a-z]+/);
var hasUppercase = password.match(/[A-Z]+/);
var hasNumbers = password.match(/[0-9]+/);
var hasPunctuation = password.match(/[^a-zA-Z0-9]+/);

// If there is a username edit box on the page,compare password to that,otherwise
// use value from the database.

var usernameBox = $('input.username');
var username = (usernameBox.length >0) ? usernameBox.val():translate.username;

-// Lose 10 points for every character less than 6.
+// Lose 5 points for every character less than 6,plus a 30 point penalty.
if (password.length <6) {
msg.push(translate.tooShort);
-  strength -= (6 - password.length) * 10;
+  strength -= ((6 - password.length) * 5) + 30;
}

// Count weaknesses.
if (!hasLowercase) {
msg.push(translate.addLowerCase);
weaknesses++;
}

if (!hasUppercase) {
msg.push(translate.addUpperCase);
weaknesses++;
}
if (!hasNumbers) {
msg.push(translate.addNumbers);
weaknesses++;
}

if (!hasPunctuation) {
msg.push(translate.addPunctuation);
weaknesses++;
}

...

// Based on the strength,work out what text should be shown by the password strength meter.
if (strength <60) {
indicatorText = translate.weak;
} else if (strength <70) {
indicatorText = translate.fair;
} else if (strength <80) {
indicatorText = translate.good;
-} else if (strength <100) {
+} else if (strength <= 100) {
indicatorText = translate.strong;
}

// Assemble the final message.
msg = translate.hasWeaknesses + '<ul><li>' + msg.join('</li><li>') + '</li></ul>';
return { strength:strength,message:msg,indicatorText:indicatorText }