2

[原创][首发]cve-2015-0569 安卓手机提权ROOT漏洞 分析

小白君 2016-2-26 18:56 15403
测试机器:nexus4       android版本:4.4   内核版本3.4.0
漏洞介绍:函数进行拷贝时没有对长度进行判断,导致用户可以修改内核栈中值。
漏洞利用:通过修改函数返回地址,来进行提权操作


1,首先找到官方的修补代码
修补代码地址:https://www.codeaurora.org/cgit/quic/la/platform/vendor/qcom-opensource/wlan/qcacld-2.0/diff/CORE/HDD/src/wlan_hdd_wext.c?id=a079d716b5481223f0166c644e9ec7c75a31b02c

@@ -9741,6 +9741,9 @@ int wlan_hdd_set_filter(hdd_context_t *pHddCtx, tpPacketFilterCfg pRequest,
 
                 hddLog(VOS_TRACE_LEVEL_INFO, "Data Offset %d Data Len %d",
                         pRequest->paramsData[i].dataOffset, pRequest->paramsData[i].dataLength);
+                if ((sizeof(packetFilterSetReq.paramsData[i].compareData)) <
+                           (pRequest->paramsData[i].dataLength))
+                    return -EINVAL;
 
                memcpy(&packetFilterSetReq.paramsData[i].compareData,
                        pRequest->paramsData[i].compareData, pRequest->paramsData[i].dataLength);
                        
                memcpy(&packetFilterSetReq.paramsData[i].dataMask,
                        pRequest->paramsData[i].dataMask, pRequest->paramsData[i].dataLength);

2,推测漏洞的产生原理
从官方的修补来看,这里只是添加了一个拷贝长度的限制,那么漏洞产生的原因,应该是memcpy拷贝时数据越界有关。
为了证实我的推测,我们看看packetFilterSetReq , pRequest 两个结构体是从何而来。
int wlan_hdd_set_filter(hdd_context_t *pHddCtx, tpPacketFilterCfg pRequest,
                            tANI_U8 sessionId)
{
    tSirRcvPktFilterCfgType    packetFilterSetReq = {0};
    tSirRcvFltPktClearParam    packetFilterClrReq = {0};
    int i=0;

从内核代码上看,packetFilterSetReq 是局部变量,而pRequest则是作为参数传入。
结合起来看,我推测,漏洞产生的原因应该是,memcpy拷贝时越界修改掉了栈的值,覆盖掉了函数的返回地址。

3,查找调用漏洞函数的过程
为了利用,以及验证我的推测,现在要找出从漏洞函数到入口函数的整条调用的线
首先从产生漏洞的函数wlan_hdd_set_filter;开始找
(wlan_hdd_set_filter在内核源码drivers\staging\prima\core\hdd\src\Wlan_hdd_wext.c文件中)
查看wlan_hdd_set_filter的调用,发现了两处,分别是同文件下的wlan_hdd_set_mc_addr_list 和 iw_set_packet_filter_params
首先看 wlan_hdd_set_mc_addr_list 函数

void wlan_hdd_set_mc_addr_list(hdd_adapter_t *pAdapter, v_U8_t set)
{
    v_U8_t filterAction;
    tPacketFilterCfg request;
    v_U8_t i;
    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);

    filterAction = set ? HDD_RCV_FILTER_SET : HDD_RCV_FILTER_CLEAR;

    /*set mulitcast addr list*/
    for (i = 0; i < pAdapter->mc_addr_list.mc_cnt; i++)
    {
        memset(&request, 0, sizeof (tPacketFilterCfg));
        request.filterAction = filterAction;
        request.filterId = i;
        if (set)
        {
            request.numParams = 1;
            request.paramsData[0].protocolLayer = HDD_FILTER_PROTO_TYPE_MAC;
            request.paramsData[0].cmpFlag = HDD_FILTER_CMP_TYPE_EQUAL;   
            request.paramsData[0].dataOffset = WLAN_HDD_80211_FRM_DA_OFFSET;
            request.paramsData[0].dataLength = ETH_ALEN; //长度变成固定,不符合触发漏洞的要求
            memcpy(&(request.paramsData[0].compareData[0]),
                    &(pAdapter->mc_addr_list.addr[i][0]), ETH_ALEN);
            /*set mulitcast filters*/
            hddLog(VOS_TRACE_LEVEL_INFO,
                    "%s: %s multicast filter: addr ="
                    MAC_ADDRESS_STR,
                    __func__, set ? "setting" : "clearing",
                    MAC_ADDR_ARRAY(request.paramsData[0].compareData));
        }
        wlan_hdd_set_filter(pHddCtx, &request, pAdapter->sessionId);
    }
    pAdapter->mc_addr_list.isFilterApplied = set ? TRUE : FALSE;
}
从代码上来看,再看看这个宏的值
#define ETH_ALEN                6
发现这个长度,根本无法达到产生漏洞的条件,因此可以判断出,这个函数不是我们所要找的目标函数。
回过头看另外一个函数iw_set_packet_filter_params

static int iw_set_packet_filter_params(struct net_device *dev, struct iw_request_info *info,
        union iwreq_data *wrqu, char *extra)
{   
    hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
    tpPacketFilterCfg pRequest = (tpPacketFilterCfg)wrqu->data.pointer;

return wlan_hdd_set_filter(WLAN_HDD_GET_CTX(pAdapter), pRequest, pAdapter->sessionId)
}
发现这个pRequest的值是作为参数传入,那么继续找他的调用点,但是发现,却找不到调用点,那么这个函数应该是函数指针调用的,在文件中搜索函数名发现了一个结构体中保存了函数地址
static const iw_handler we_private[] = {
其中有这一项

static const iw_handler we_private[] = {

   [WLAN_PRIV_SET_INT_GET_NONE      - SIOCIWFIRSTPRIV]   = iw_setint_getnone,  //set priv ioctl
   [WLAN_PRIV_SET_NONE_GET_INT      - SIOCIWFIRSTPRIV]   = iw_setnone_getint,  //get priv ioctl
   [WLAN_PRIV_SET_CHAR_GET_NONE     - SIOCIWFIRSTPRIV]   = iw_setchar_getnone, //get priv ioctl
   [WLAN_PRIV_SET_THREE_INT_GET_NONE - SIOCIWFIRSTPRIV]  = iw_set_three_ints_getnone,
   [WLAN_PRIV_GET_CHAR_SET_NONE      - SIOCIWFIRSTPRIV]  = iw_get_char_setnone,
   [WLAN_PRIV_SET_NONE_GET_NONE     - SIOCIWFIRSTPRIV]   = iw_setnone_getnone, //action priv ioctl
   [WLAN_PRIV_SET_VAR_INT_GET_NONE  - SIOCIWFIRSTPRIV]   = iw_set_var_ints_getnone,
   [WLAN_PRIV_ADD_TSPEC             - SIOCIWFIRSTPRIV]   = iw_add_tspec,
   [WLAN_PRIV_DEL_TSPEC             - SIOCIWFIRSTPRIV]   = iw_del_tspec,
   [WLAN_PRIV_GET_TSPEC             - SIOCIWFIRSTPRIV]   = iw_get_tspec,
#ifdef FEATURE_OEM_DATA_SUPPORT
   [WLAN_PRIV_SET_OEM_DATA_REQ - SIOCIWFIRSTPRIV] = iw_set_oem_data_req, //oem data req Specifc
   [WLAN_PRIV_GET_OEM_DATA_RSP - SIOCIWFIRSTPRIV] = iw_get_oem_data_rsp, //oem data req Specifc
#endif

#ifdef FEATURE_WLAN_WAPI
   [WLAN_PRIV_SET_WAPI_MODE             - SIOCIWFIRSTPRIV]  = iw_qcom_set_wapi_mode,
   [WLAN_PRIV_GET_WAPI_MODE             - SIOCIWFIRSTPRIV]  = iw_qcom_get_wapi_mode,
   [WLAN_PRIV_SET_WAPI_ASSOC_INFO       - SIOCIWFIRSTPRIV]  = iw_qcom_set_wapi_assoc_info,
   [WLAN_PRIV_SET_WAPI_KEY              - SIOCIWFIRSTPRIV]  = iw_qcom_set_wapi_key,
   [WLAN_PRIV_SET_WAPI_BKID             - SIOCIWFIRSTPRIV]  = iw_qcom_set_wapi_bkid,
   [WLAN_PRIV_GET_WAPI_BKID             - SIOCIWFIRSTPRIV]  = iw_qcom_get_wapi_bkid,
#endif /* FEATURE_WLAN_WAPI */
#ifdef WLAN_FEATURE_VOWIFI_11R
   [WLAN_PRIV_SET_FTIES                 - SIOCIWFIRSTPRIV]   = iw_set_fties,
#endif
   [WLAN_PRIV_SET_HOST_OFFLOAD          - SIOCIWFIRSTPRIV]   = iw_set_host_offload,
   [WLAN_GET_WLAN_STATISTICS            - SIOCIWFIRSTPRIV]   = iw_get_statistics,
   [WLAN_SET_KEEPALIVE_PARAMS           - SIOCIWFIRSTPRIV]   = iw_set_keepalive_params
#ifdef WLAN_FEATURE_PACKET_FILTERING
   ,
   [WLAN_SET_PACKET_FILTER_PARAMS       - SIOCIWFIRSTPRIV]   = iw_set_packet_filter_params
#endif
#ifdef FEATURE_WLAN_SCAN_PNO
   ,
   [WLAN_SET_PNO                        - SIOCIWFIRSTPRIV]   = iw_set_pno_priv
#endif
   ,
   [WLAN_SET_BAND_CONFIG                - SIOCIWFIRSTPRIV]   = iw_set_band_config,
   [WLAN_PRIV_SET_MCBC_FILTER           - SIOCIWFIRSTPRIV]   = iw_set_dynamic_mcbc_filter,
   [WLAN_PRIV_CLEAR_MCBC_FILTER         - SIOCIWFIRSTPRIV]   = iw_clear_dynamic_mcbc_filter,
   [WLAN_SET_POWER_PARAMS               - SIOCIWFIRSTPRIV]   = iw_set_power_params_priv,
   [WLAN_GET_LINK_SPEED                 - SIOCIWFIRSTPRIV]   = iw_get_linkspeed,
};
那么在看看哪里使用 we_private  

const struct iw_handler_def we_handler_def = {
   .num_standard     = sizeof(we_handler) / sizeof(we_handler[0]),
   .num_private      = sizeof(we_private) / sizeof(we_private[0]),
   .num_private_args = sizeof(we_private_args) / sizeof(we_private_args[0]),

   .standard         = (iw_handler *)we_handler,
   .private          = (iw_handler *)we_private,
   .private_args     = we_private_args,
   .get_wireless_stats = get_wireless_stats,
};
发现最后赋值给了const struct iw_handler_def we_handler_def 中的 .private  

int hdd_register_wext(struct net_device *dev)
    {
     ......
    if (!VOS_IS_STATUS_SUCCESS(vos_event_init(&pwextBuf->scanevent)))
    {
        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, ("ERROR: HDD scan event init failed!!\n"));
        return eHAL_STATUS_FAILURE;
    }

    // Register as a wireless device
    dev->wireless_handlers = (struct iw_handler_def *)&we_handler_def;//搜索文件看哪里使用了dev->wireless_handlers

    EXIT();
    return 0;
}
到这步之后,再继续看调we_handler_def的调用点。
发现最后给了dev->wireless_handlers 这个成员变量。现在我们在使用lookup References功能 看看 哪些地方使用了 dev->wireless_handlers

发现,很多地方都使用了dev->wireless_handlers这个成员变量,那么现在来根据条件(看哪些地方是获取这个成员变量)来排除
最后发现,只有这一处是获取到了dev->wireless_handlers这个成员变量。

然后打开这个目录文件搜索(目录:net\wireless\Wext-core.c)

static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
{
        /* Don't "optimise" the following variable, it will crash */
        unsigned int        index;                /* *MUST* be unsigned */
        const struct iw_handler_def *handlers = NULL;

#ifdef CONFIG_CFG80211_WEXT
        if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy)
                handlers = dev->ieee80211_ptr->wiphy->wext;
#endif
#ifdef CONFIG_WIRELESS_EXT
if (dev->wireless_handlers)
handlers = dev->wireless_handlers;

#endif

        if (!handlers)
                return NULL;

        /* Try as a standard command */
        index = IW_IOCTL_IDX(cmd);
        if (index < handlers->num_standard)
                return handlers->standard[index];

#ifdef CONFIG_WEXT_PRIV
        /* Try as a private command */
        index = cmd - SIOCIWFIRSTPRIV;
        if (index < handlers->num_private)
                return handlers->private[index];  //获取了漏洞函数地址
#endif

        /* Not found */
        return NULL;
}
找到了get_handler这个函数,看他下面代码
        index = cmd - SIOCIWFIRSTPRIV;
        if (index < handlers->num_private)
                return handlers->private[index];  //获取了漏洞函数地址
发现他是根据传入的CMD指令来获取函数地址,根据之前的 we_private 存放地址   [WLAN_SET_PACKET_FILTER_PARAMS       - SIOCIWFIRSTPRIV]   = iw_set_packet_filter_params 得出了CMD值

static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
                                  unsigned int cmd,
                                  struct iw_request_info *info,
                                  wext_ioctl_func standard,
                                  wext_ioctl_func private)
{
        struct iwreq *iwr = (struct iwreq *) ifr;
        struct net_device *dev;
        iw_handler        handler;

        ......

        /* Basic check */
        if (!netif_device_present(dev))
                return -ENODEV;

        /* New driver API : try to find the handler */
        handler = get_handler(dev, cmd);     ////获取了漏洞函数地址
        if (handler) {
                /* Standard and private are not the same */
                if (cmd < SIOCIWFIRSTPRIV)
                        return standard(dev, iwr, cmd, info, handler);
                else if (private)
                        return private(dev, iwr, cmd, info, handler);
        }
        /* Old driver API : call driver ioctl handler */
       .....
}
现在继续看get_handler 的调用点,
根据图片代码看到,将调用漏洞的函数地址作为参数,然后调用了standard or private 发现这两个函数都是作为参数传入进来的。
( iwr 就是漏洞产生的条件,struct iwreq *iwr = (struct iwreq *) ifr;    ifr则作为参数传入 )
那么再继续看上级调用。

static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr,
                               unsigned int cmd, struct iw_request_info *info,
                               wext_ioctl_func standard,
                               wext_ioctl_func private)
{
        int ret = wext_permission_check(cmd);

        if (ret)
                return ret;

        dev_load(net, ifr->ifr_name);
        rtnl_lock();
        ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private);
        rtnl_unlock();

        return ret;
}
发现我们所感兴趣的值还是作为参数传入的,那么继续往上,

int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
                      void __user *arg)
{
        struct iw_request_info info = { .cmd = cmd, .flags = 0 };
        int ret;

        ret = wext_ioctl_dispatch(net, ifr, cmd, &info,
                                  ioctl_standard_call,
                                  ioctl_private_call);
  //ioctl_private_call 中调用了漏洞函数
        if (ret >= 0 &&
            IW_IS_GET(cmd) &&
            copy_to_user(arg, ifr, sizeof(struct iwreq)))
                return -EFAULT;

        return ret;
}
现在得到了这两个函数的地址。
查看ioctl_private_call的代码

int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
                       unsigned int cmd, struct iw_request_info *info,
                       iw_handler handler)
{
        int extra_size = 0, ret = -EINVAL;
        const struct iw_priv_args *descr;

        extra_size = get_priv_descr_and_size(dev, cmd, &descr);

        /* Check if we have a pointer to user space data or not. */
        if (extra_size == 0) {
                /* No extra arguments. Trivial to handle */
                ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));//handler:漏洞函数,iwr->u:触发条件
        } else {
                ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
                                             handler, dev, info, extra_size);
        }

        /* Call commit handler if needed and defined */
        if (ret == -EIWCOMMIT)
                ret = call_commit_handler(dev);

        return ret;
}
可以看到这里调用了漏洞函数。 &(iwr->u) ,则是漏洞产生的条件

int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
{
        struct ifreq ifr;
        int ret;
        char *colon;

        /* One special case: SIOCGIFCONF takes ifconf argument
           and requires shared lock, because it sleeps writing
           to user space.
         */

        if (cmd == SIOCGIFCONF) {
                rtnl_lock();
                ret = dev_ifconf(net, (char __user *) arg);
                rtnl_unlock();
                return ret;
        }
        if (cmd == SIOCGIFNAME)
                return dev_ifname(net, (struct ifreq __user *)arg);

        if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))  //给ifr结构体赋值
                return -EFAULT;

       ........

                                ret = -EFAULT;
                        return ret;
                }
                /* Take care of Wireless Extensions */
                if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)
                        return wext_handle_ioctl(net, &ifr, cmd, arg);
                return -ENOTTY;
        }
现在继续看wext_handle_ioctl函数(注意:ifr参数就是漏洞的产生条件)继续看上级调用

int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
{
        struct ifreq ifr;
        int ret;
        char *colon;

        /* One special case: SIOCGIFCONF takes ifconf argument
           and requires shared lock, because it sleeps writing
           to user space.
         */

        if (cmd == SIOCGIFCONF) {
                rtnl_lock();
                ret = dev_ifconf(net, (char __user *) arg);
                rtnl_unlock();
                return ret;
        }
        if (cmd == SIOCGIFNAME)
                return dev_ifname(net, (struct ifreq __user *)arg);

        if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
                return -EFAULT;
看函数头
发现ifr的内容是拷贝的dev_ioctl中的arg参数

static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
        struct socket *sock;
        struct sock *sk;
        void __user *argp = (void __user *)arg;
        int pid, err;
        struct net *net;

        sock = file->private_data;
        sk = sock->sk;
        net = sock_net(sk);
        if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
                err = dev_ioctl(net, cmd, argp);
        } else
到这里sock_ioctl没有发现上级调用,那么看哪些地方出现过这个函数

搜索sock_ioctl发现

static const struct file_operations socket_file_ops = {
        .owner =        THIS_MODULE,
        .llseek =        no_llseek,
        .aio_read =        sock_aio_read,
        .aio_write =        sock_aio_write,
        .poll =                sock_poll,
        .unlocked_ioctl = sock_ioctl,//保存在了 unlocked_ioctl 中
#ifdef CONFIG_COMPAT
        .compat_ioctl = compat_sock_ioctl,
#endif
        .mmap =                sock_mmap,
        .open =                sock_no_open,        /* special open code to disallow open via /proc */
        .release =        sock_close,
        .fasync =        sock_fasync,
        .sendpage =        sock_sendpage,
        .splice_write = generic_splice_sendpage,
        .splice_read =        sock_splice_read,
};
file_operations结构体中获取
linux大多都是使用ioctl系统调用来控制驱动设备的。所以我们跟踪ioctl函数

SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{
        struct file *filp;
       
int error = -EBADF;
        int fput_needed;

        filp = fget_light(fd, &fput_needed);
        if (!filp)
                goto out;

        error = security_file_ioctl(filp, cmd, arg);
        if (error)
                goto out_fput;

        error = do_vfs_ioctl(filp, fd, cmd, arg);
out_fput:
        fput_light(filp, fput_needed);
out:
        return error;
}

int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
             unsigned long arg)
{
        int error = 0;
        int __user *argp = (int __user *)arg;
        struct inode *inode = filp->f_path.dentry->d_inode;

        .....

        default:
                if (S_ISREG(inode->i_mode))
                        error = file_ioctl(filp, cmd, arg);
                else
                        error = vfs_ioctl(filp, cmd, arg);
                break;
        }
do_vfs_ioctl 函数下找到 vfs_ioctl

static long vfs_ioctl(struct file *filp, unsigned int cmd,
                      unsigned long arg)
{
        int error = -ENOTTY;

        if (!filp->f_op || !filp->f_op->unlocked_ioctl)
                goto out;

        error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
        if (error == -ENOIOCTLCMD)
                error = -ENOTTY;
out:
        return error;
}
这样调用漏洞函数的过程就全部串联起来。

4,构建触发漏洞条件
首先看下 ioctl  有三个参数
param1首先打开网络驱动设备(linux大多数驱动都是以文件形式存在的,只有网络驱动例外。想了解详细的可以看下socket函数)

param2cmd,之前就已经找函数调用路径的时候我们就发现

#define SIOCIWFIRSTPRIV        0x8BE0
#define WLAN_SET_PACKET_FILTER_PARAMS (SIOCIWFIRSTPRIV + 23)


param3[/IMG]
首先传入的时候

if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
然后

static int iw_set_packet_filter_params(struct net_device *dev, struct iw_request_info *info,
        union iwreq_data *wrqu, char *extra)
{   
    hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
    tpPacketFilterCfg pRequest = (tpPacketFilterCfg)wrqu->data.pointer;

    return wlan_hdd_set_filter(WLAN_HDD_GET_CTX(pAdapter), pRequest, pAdapter->sessionId);
}
强制转换了下我们看下 iwreq 的成员

struct  iwreq 
{
  union
  {
    char  ifrn_name[IFNAMSIZ];  /* if name, e.g. "eth0" */
  } ifr_ifrn;

  /* Data part (defined just above) */
  union  iwreq_data  u;
};

union  iwreq_data
{
  struct iw_point  data;    /* Other large parameters */
};

struct  iw_point
{
  void __user  *pointer;  /* Pointer to the data  (in user space) */    
  __u16    length;    /* number of fields or size in bytes */      
  __u16    flags;    /* Optional params */                
};

看下tpPacketFilterCfg的结构体
typedef struct
{
    v_U8_t            filterAction;
    v_U8_t            filterId;
    v_U8_t            numParams;
    struct PacketFilterParamsCfg paramsData [HDD_MAX_CMP_PER_PACKET_FILTER];
}tPacketFilterCfg, *tpPacketFilterCfg;

struct PacketFilterParamsCfg
{
    v_U8_t              protocolLayer;
    v_U8_t              cmpFlag;
    v_U8_t              dataOffset;
    v_U8_t              dataLength;
    v_U8_t              compareData[8];
    v_U8_t              dataMask[8];
};

现在构造触发漏洞的结构体
int wlan_hdd_set_filter(hdd_context_t *pHddCtx, tpPacketFilterCfg pRequest,
                            tANI_U8 sessionId)
{
    tSirRcvPktFilterCfgType    packetFilterSetReq = {0};
    tSirRcvFltPktClearParam    packetFilterClrReq = {0};
    int i=0;

    if (pHddCtx->cfg_ini->disablePacketFilter)
    {
        hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Packet Filtering Disabled. Returning ",
                __func__ );
        return 0;
    }
    if (pHddCtx->isLogpInProgress)
    {
       VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
                                  "%s:LOGP in Progress. Ignore!!!", __func__);
       return -EBUSY;
    }
    /* Debug display of request components. */
    hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Packet Filter Request : FA %d params %d",
            __func__, pRequest->filterAction, pRequest->numParams);

    switch (pRequest->filterAction) 进入条件
    {
        case HDD_RCV_FILTER_SET:
            hddLog(VOS_TRACE_LEVEL_INFO, "%s: Set Packet Filter Request for Id: %d",
                    __func__, pRequest->filterId);

            packetFilterSetReq.filterId = pRequest->filterId;
            if ( pRequest->numParams >= HDD_MAX_CMP_PER_PACKET_FILTER)
            {
                hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Number of Params exceed Max limit %d\n",
                        __func__, pRequest->numParams);
                return -EINVAL;
            }
            packetFilterSetReq.numFieldParams = pRequest->numParams;
            packetFilterSetReq.coalesceTime = 0;
            packetFilterSetReq.filterType = 1;
            for (i=0; i < pRequest->numParams; i++)//因为= pRequest->paramsData[i].dataLength 定义为unsigned char,覆盖长度无法触发漏洞,所以这里要设定pRequest->numParams的值,已确保能覆盖的地址能到达函数返回地址的长度
            {
                packetFilterSetReq.paramsData[i].protocolLayer = pRequest->paramsData[i].protocolLayer;
                packetFilterSetReq.paramsData[i].cmpFlag = pRequest->paramsData[i].cmpFlag;
                packetFilterSetReq.paramsData[i].dataOffset = pRequest->paramsData[i].dataOffset;
                  packetFilterSetReq.paramsData[i].dataLength = pRequest->paramsData[i].dataLength;
                 //这个成员变量定义为unsigned char,最大数字只能设置255个字节。
                packetFilterSetReq.paramsData[i].reserved = 0;

                hddLog(VOS_TRACE_LEVEL_INFO, "Proto %d Comp Flag %d Filter Type %d\n",
                        pRequest->paramsData[i].protocolLayer, pRequest->paramsData[i].cmpFlag,
                        packetFilterSetReq.filterType);

                hddLog(VOS_TRACE_LEVEL_INFO, "Data Offset %d Data Len %d\n",
                        pRequest->paramsData[i].dataOffset, pRequest->paramsData[i].dataLength);

                /*因为没有对长度进行判断,而packetFilterSetReq又属于局部变量,导致了我们可以设置适量的长度,覆盖掉wlan_hdd_set_filter的返回地址,让其跳转到自己所写函数的,进行提权操作.*/
                memcpy(&packetFilterSetReq.paramsData[i].compareData,
                        pRequest->paramsData[i].compareData, pRequest->paramsData[i].dataLength);
                memcpy(&packetFilterSetReq.paramsData[i].dataMask,
                        pRequest->paramsData[i].dataMask, pRequest->paramsData[i].dataLength);

                hddLog(VOS_TRACE_LEVEL_INFO, "CData %d CData %d CData %d CData %d CData %d CData %d\n",
                        pRequest->paramsData[i].compareData[0], pRequest->paramsData[i].compareData[1],
                        pRequest->paramsData[i].compareData[2], pRequest->paramsData[i].compareData[3],
                        pRequest->paramsData[i].compareData[4], pRequest->paramsData[i].compareData[5]);

                hddLog(VOS_TRACE_LEVEL_INFO, "MData %d MData %d MData %d MData %d MData %d MData %d\n",
                        pRequest->paramsData[i].dataMask[0], pRequest->paramsData[i].dataMask[1],
                        pRequest->paramsData[i].dataMask[2], pRequest->paramsData[i].dataMask[3],
                        pRequest->paramsData[i].dataMask[4], pRequest->paramsData[i].dataMask[5]);
            }

            if (eHAL_STATUS_SUCCESS != sme_ReceiveFilterSetFilter(pHddCtx->hHal, &packetFilterSetReq, sessionId))
            {
                hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Failure to execute Set Filter\n",
                        __func__);
                return -EINVAL;
            }

            break;

        case HDD_RCV_FILTER_CLEAR:

            hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: Clear Packet Filter Request for Id: %d\n",
                    __func__, pRequest->filterId);
            packetFilterClrReq.filterId = pRequest->filterId;
            if (eHAL_STATUS_SUCCESS != sme_ReceiveFilterClearFilter(pHddCtx->hHal, &packetFilterClrReq, sessionId))
            {
                hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Failure to execute Clear Filter\n",
                        __func__);
                return -EINVAL;
            }
            break;

        default :
            hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: Packet Filter Request: Invalid %d\n",
                    __func__, pRequest->filterAction);
            return -EINVAL;
    }
    return 0;
}

iwreq.ifrn_name = 网络驱动名字
tpPacketFilterCfg.numParams = 4  

PacketFilterParamsCfg.dataLength = 255
PacketFilterParamsCfg.compareData = my_fun_address

这样设置参数就能触发漏洞

5,函数返回崩溃问题
wlan_hdd_set_filter 因为返回地址被修改,所以调用完函数后,会导致内核崩溃,但是我们可以使用上级函数的地址来返回。这样就能避免内核崩溃.

QQ : 3455456608 有空一起交流~
附带利用代码:
上传的附件:
最新回复 (24)
影子不寂寞 2016-2-26 19:19
2
谢谢分享
huangyalei 2016-2-27 10:34
3
谢谢,图片看不到
sandikast 2016-2-27 11:02
4
谢谢分享
了log 2016-2-29 13:12
5
谢谢分享
2
crazybug 2016-2-29 17:59
6
学习!!!!!!
huluxia 2016-2-29 18:55
7
感谢分享。
云淡风轻tzm 2016-3-1 14:05
8
此楼层已删除
2
地狱怪客 2016-3-1 14:17
9
收藏了~
deadxing 2016-3-2 10:17
10
前排学习~
3
anbc 2016-3-2 12:38
11
分析详细透彻,很值得看,点赞
了log 2016-3-2 18:51
12
感谢分享。
kwanhua 2016-3-3 15:22
13
收藏学习,感谢分享
11
Netfairy 2016-3-7 18:53
14
分析详细透彻,很值得看,点赞 !!!
3
ggggwwww 2016-3-18 11:04
15
感谢小白君这么有营养的分析,感谢技术分享。
pregnant 2016-3-20 12:34
16
赶紧下载学习
cgwyywgc 2016-3-22 17:49
17
学习了,谢谢分享
写轮没有眼 2016-3-26 21:04
18
我靠,楼主这么牛逼啊
逆向初阶 2016-4-2 17:28
19
感谢牛牛分享!想着运行试试,可是不知道这个exp中的c文件怎么在模拟器中执行。。。
Mattermon 2016-4-5 14:35
20
大赞楼主,Orz
小鱼钓猫 2016-4-22 09:58
21
感谢分享
jekinlee 2016-5-31 19:40
22
大神,ndk-build编译的时候报错:
exploit.c:68:35: error: local frame unavailable (naked function?)

怎么解决
嘿l嘿l嘿 2016-10-10 14:29
23
赶紧下载学习
看雪vip用户 2016-10-18 17:13
24
学习!!!
Ziola 2017-7-20 14:50
25
不错,下载学习下
violet陈 2017-8-9 17:12
26
jekinlee 大神,ndk-build编译的时候报错: exploit.c:68:35: error: local frame unavailable (naked function?) 怎么解决
同问,遇到这个问题,你最后咋解决的?
返回