申请ID:ISP、疯
申请ID:ISP、疯申请邮箱:3096036667@qq.com 吾爱论坛大神在上,为龙飞雪而来
测试机器: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.dataOffset, pRequest->paramsData.dataLength);
+ if ((sizeof(packetFilterSetReq.paramsData.compareData)) <
+ (pRequest->paramsData.dataLength))
+ return -EINVAL;
memcpy(&packetFilterSetReq.paramsData.compareData,
pRequest->paramsData.compareData, pRequest->paramsData.dataLength);
memcpy(&packetFilterSetReq.paramsData.dataMask,
pRequest->paramsData.dataMask, pRequest->paramsData.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.protocolLayer = HDD_FILTER_PROTO_TYPE_MAC;
request.paramsData.cmpFlag = HDD_FILTER_CMP_TYPE_EQUAL;
request.paramsData.dataOffset = WLAN_HDD_80211_FRM_DA_OFFSET;
request.paramsData.dataLength = ETH_ALEN; //长度变成固定,不符合触发漏洞的要求
memcpy(&(request.paramsData.compareData),
&(pAdapter->mc_addr_list.addr), 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.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[] = {
= iw_setint_getnone,//set priv ioctl
= iw_setnone_getint,//get priv ioctl
= iw_setchar_getnone, //get priv ioctl
= iw_set_three_ints_getnone,
= iw_get_char_setnone,
= iw_setnone_getnone, //action priv ioctl
= iw_set_var_ints_getnone,
= iw_add_tspec,
= iw_del_tspec,
= iw_get_tspec,
#ifdef FEATURE_OEM_DATA_SUPPORT
= iw_set_oem_data_req, //oem data req Specifc
= iw_get_oem_data_rsp, //oem data req Specifc
#endif
#ifdef FEATURE_WLAN_WAPI
= iw_qcom_set_wapi_mode,
= iw_qcom_get_wapi_mode,
= iw_qcom_set_wapi_assoc_info,
= iw_qcom_set_wapi_key,
= iw_qcom_set_wapi_bkid,
= iw_qcom_get_wapi_bkid,
#endif /* FEATURE_WLAN_WAPI */
#ifdef WLAN_FEATURE_VOWIFI_11R
= iw_set_fties,
#endif
= iw_set_host_offload,
= iw_get_statistics,
= iw_set_keepalive_params
#ifdef WLAN_FEATURE_PACKET_FILTERING
,
= iw_set_packet_filter_params
#endif
#ifdef FEATURE_WLAN_SCAN_PNO
,
= iw_set_pno_priv
#endif
,
= iw_set_band_config,
= iw_set_dynamic_mcbc_filter,
= iw_clear_dynamic_mcbc_filter,
= iw_set_power_params_priv,
= iw_get_linkspeed,
};
那么在看看哪里使用 we_private
const struct iw_handler_def we_handler_def = {
.num_standard = sizeof(we_handler) / sizeof(we_handler),
.num_private = sizeof(we_private) / sizeof(we_private),
.num_private_args = sizeof(we_private_args) / sizeof(we_private_args),
.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 intindex; /* *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;
#ifdef CONFIG_WEXT_PRIV
/* Try as a private command */
index = cmd - SIOCIWFIRSTPRIV;
if (index < handlers->num_private)
return handlers->private;//获取了漏洞函数地址
#endif
/* Not found */
return NULL;
}
找到了get_handler这个函数,看他下面代码
index = cmd - SIOCIWFIRSTPRIV;
if (index < handlers->num_private)
return handlers->private;//获取了漏洞函数地址
发现他是根据传入的CMD指令来获取函数地址,根据之前的 we_private 存放地址 = 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_handlerhandler;
......
/* 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 SIOCIWFIRSTPRIV0x8BE0
#define WLAN_SET_PACKET_FILTER_PARAMS (SIOCIWFIRSTPRIV + 23)
param3
首先传入的时候
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 的成员
structiwreq
{
union
{
charifrn_name;/* if name, e.g. "eth0" */
} ifr_ifrn;
/* Data part (defined just above) */
unioniwreq_datau;
};
unioniwreq_data
{
struct iw_pointdata; /* Other large parameters */
};
structiw_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 ;
}tPacketFilterCfg, *tpPacketFilterCfg;
struct PacketFilterParamsCfg
{
v_U8_t protocolLayer;
v_U8_t cmpFlag;
v_U8_t dataOffset;
v_U8_t dataLength;
v_U8_t compareData;
v_U8_t dataMask;
};
现在构造触发漏洞的结构体
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.dataLength 定义为unsigned char,覆盖长度无法触发漏洞,所以这里要设定pRequest->numParams的值,已确保能覆盖的地址能到达函数返回地址的长度
{
packetFilterSetReq.paramsData.protocolLayer = pRequest->paramsData.protocolLayer;
packetFilterSetReq.paramsData.cmpFlag = pRequest->paramsData.cmpFlag;
packetFilterSetReq.paramsData.dataOffset = pRequest->paramsData.dataOffset;
packetFilterSetReq.paramsData.dataLength = pRequest->paramsData.dataLength;
//这个成员变量定义为unsigned char,最大数字只能设置255个字节。
packetFilterSetReq.paramsData.reserved = 0;
hddLog(VOS_TRACE_LEVEL_INFO, "Proto %d Comp Flag %d Filter Type %d\n",
pRequest->paramsData.protocolLayer, pRequest->paramsData.cmpFlag,
packetFilterSetReq.filterType);
hddLog(VOS_TRACE_LEVEL_INFO, "Data Offset %d Data Len %d\n",
pRequest->paramsData.dataOffset, pRequest->paramsData.dataLength);
/*因为没有对长度进行判断,而packetFilterSetReq又属于局部变量,导致了我们可以设置适量的长度,覆盖掉wlan_hdd_set_filter的返回地址,让其跳转到自己所写函数的,进行提权操作.*/
memcpy(&packetFilterSetReq.paramsData.compareData,
pRequest->paramsData.compareData, pRequest->paramsData.dataLength);
memcpy(&packetFilterSetReq.paramsData.dataMask,
pRequest->paramsData.dataMask, pRequest->paramsData.dataLength);
hddLog(VOS_TRACE_LEVEL_INFO, "CData %d CData %d CData %d CData %d CData %d CData %d\n",
pRequest->paramsData.compareData, pRequest->paramsData.compareData,
pRequest->paramsData.compareData, pRequest->paramsData.compareData,
pRequest->paramsData.compareData, pRequest->paramsData.compareData);
hddLog(VOS_TRACE_LEVEL_INFO, "MData %d MData %d MData %d MData %d MData %d MData %d\n",
pRequest->paramsData.dataMask, pRequest->paramsData.dataMask,
pRequest->paramsData.dataMask, pRequest->paramsData.dataMask,
pRequest->paramsData.dataMask, pRequest->paramsData.dataMask);
}
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://bbs.pediy.com/showthread.php?p=1417207 看雪这个帖子是你发的,还是拿别人文章来申请的?
页:
[1]