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