Make WordPress Core

Opened 17 years ago

Closed 17 years ago

Last modified 17 years ago

#4690 closed defect (bug) (fixed)

Wordpress options.php SQL Injection Vulnerability

Reported by: benjaminflesch's profile BenjaminFlesch Owned by: nazgul's profile Nazgul
Milestone: 2.0.11 Priority: high
Severity: major Version: 2.2.1
Component: Security Keywords: has-patch commit
Focuses: Cc:

Description

Read here http://mybeni.rootzilla.de/mybeNi/2007/wordpress_zeroday_vulnerability_roundhouse_kick_and_why_i_nearly_wrote_the_first_blog_worm/ beginning from the second point, in short:

in options.php the parameter page_options isnt filtered, patch:

case 'update':

$any_changed = 0;

check_admin_referer('update-options');

  • if ( preg_match("/['\"<>]/", $_POSTpage_options?) )
  • wp_die(('Cheatin&#8217; uh?'));

add the lines marked with a star in options.php.

Additionally, because of this Persistant XSS and information disclosure by opening options.php directly in the browser may occur. Better stop the database dump.

Attachments (2)

4690.diff (563 bytes) - added by Nazgul 17 years ago.
4690.002.diff (1.6 KB) - added by markjaquith 17 years ago.
Patches to update_option/add_option

Download all attachments as: .zip

Change History (12)

#1 @Nazgul
17 years ago

  • Keywords needs-patch added
  • Milestone set to 2.3 (trunk)
  • Priority changed from highest omg bbq to high
  • Severity changed from critical to major

First, there is a nonce protecting that page, so it can't be exploited remotely.
Second, you need the "manage_options" capability which by default is only given to Administrators.

Administrators can do all sorts of "bad things" to their own blog by design. It should be fixed asap, but isn't critical in my opinion.

@Nazgul
17 years ago

#2 @Nazgul
17 years ago

  • Keywords has-patch needs-testing added; needs-patch removed
  • Owner changed from anonymous to Nazgul
  • Status changed from new to assigned

Patch adds the missing $wpdb->escapes, which should fix this issue.

It could use some extensive testing for regression bugs though.

#3 @BenjaminFlesch
17 years ago

Okay I know, but XSS flaws are existing everywhere and this can be used for persistant XSS. Append
' OR '"><script>alert(1)</script>'='"><script>alert(1)</script> to the page_options value in one of the options files (e.g. options-privacy.php) via WebDeveloper Toolbar and submit. Then, visit /options.php which dumps the whole database without output validation -> Persistant XSS flaws.
Plus the step from XSS (Client/Webpage manipulation) to SQLInjection (Database Manipulation) is taken. This makes attacks like adding users much easier because the take less time the authenticated Admin needs to stay on the attacker's page.
--beni

#4 @markjaquith
17 years ago

Nazgul, we can't use that fix now (although it is planned for 2.4) because it will break a lot of things. For now, we have to do our escaping outside of those functions.

@markjaquith
17 years ago

Patches to update_option/add_option

#5 @markjaquith
17 years ago

Try that patch on for size. The issue here is that while add_option() and update_option() expect the option name to be unescaped, get_option() expects it to be pre-escaped. So we need to create a safe version of the option name to use when add/update_option call get_option().

This is seriously messed up, and will be fixed properly in 2.4 (by making ALL FUNCTIONS expect unescaped data).

#6 @Nazgul
17 years ago

New patch looks good to me.

#7 @markjaquith
17 years ago

  • Keywords commit added; needs-testing removed
  • Milestone changed from 2.3 (trunk) to 2.0.11

Targeting all three branches here.

#8 @markjaquith
17 years ago

  • Resolution set to fixed
  • Status changed from assigned to closed

(In [5829]) add_option()/update_option() should pass the option name to get_option() pre-escaped. fixes #4690 for trunk

#9 @markjaquith
17 years ago

(In [5830]) add_option()/update_option() should pass the option name to get_option() pre-escaped. fixes #4690 for 2.2.x

#10 @markjaquith
17 years ago

(In [5831]) add_option()/update_option() should pass the option name to get_option() pre-escaped. fixes #4690 for 2.0.x

Note: See TracTickets for help on using tickets.