cve-2015-0569 安卓手机提权ROOT漏洞 分析

测试机器: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 pReques t, 
                            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 [COLOR="rgb(139, 0, 0)"]private[/COLOR] 发现这两个函数都是作为参数传入进来的。
( 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 中的[COLOR="rgb(139, 0, 0)"]arg[/COLOR]参数


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函数)

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

#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);
}
强制转换了下我们看下 [COLOR="rgb(139, 0, 0)"]iwreq[/COLOR] 的成员


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 因为返回地址被修改,所以调用完函数后,会导致内核崩溃,但是我们可以使用上级函数的地址来返回。这样就能避免内核崩溃.


 原文地址: http://www.kanxue.com/bbs/showthread.php?t=208066

你可能感兴趣的:(漏洞挖掘与CVE分析)