本文来自依云's Blog,转载请注明。
ICMP 套接字是两年前 Linux 内核新加入的功能,目的是允许不需要 set-user-id 和CAP_NET_RAW
权限的 ping 程序的实现。大家都知道,set-user-id 程序经常成为本地提权的途径。在 Linux 内核加入此功能之前,以安全为目标的 Openwall GNU/*/Linux 实现了除 ping 程序之外的所有程序去 suid 化……这个功能也是由他们提出并加入的。
我并没有在 man 手册中看到关于 ICMP 套接字的信息。关于 ICMP 套接字使用的细节来自于内核邮件列表。
使用 ICMP 套接字的好处:
- 程序不需要特殊的权限;
- 内核会帮助搞定一些工作。
坏处是:
- 基本没有兼容性可讲;
- 需要调整一个内核参数。
这个内核参数是net.ipv4.ping_group_range
,是一对整数,指定了允许使用 ICMP 套接字的组 ID的范围。默认值为1 0
,意味着没有人能够使用这个特性。手动修改下:
sudo sysctl -w net.ipv4.ping_group_range='0 10'
当然你可以直接去写/proc/sys/net/ipv4/ping_group_range
文件。
如果系统不支持这个特性,在创建套接字的时候会得到「Protocol not supported」错误,而如果没有权限,则会得到「Permission denied」错误。
创建 ICMP 套接字的方法如下:
import socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_ICMP)
它的类型和 UDP 套接字一样,是SOCK_DGRAM
,不是SOCK_RAW
哦。这意味着你不会收到 20 字节的 IP 头。不仅仅如此,使用 ICMP 套接字不需要手工计算校验和,因为内核会重新计算的。ICMP id 也是由内核填的。在接收的时候,内核会只把相应 id 的 ICMP 回应返回给程序,不需要自己或者要求内核过滤了。
所以,要组装一个 ICMP ECHO 请求包头很容易了:
header = struct.pack('bbHHh', 8, 0, 0, 0, seq)
这五项依次是:类型(ECHO_REQUEST
)、code(只能为零)、校验和(不需要管)、id(不需要管)、序列号。
接收起来也简单,只要看一下序列号知道是回应自己发的哪个包的就行了。
这里是我的一个很简单的实例。
附注:Mac OS X 在 Linux 之前实现了类似的功能。但是行为可能不太一样。有报告校验和需要自己计算的,也有报告发送正确但是返回报文是乱码的。另,FreeBSD 和 OpenBSD 不支持这个特性。
Oct 31, 2013 01:26:13 AM
原来如此,ping 是带 set-user-id 的..
查了下 Windows 下的解决方法似乎是调用系统API IcmpSendEcho() ...
Oct 31, 2013 12:21:55 PM
不是所有 ping 是带 suid 的,比如我系统上的是带 CAP_NET_RAW 权限的。都是超过需要的特权就是了。
Nov 12, 2013 04:54:47 PM
看到这个我又想起来之前装android-sdk包把文件系统权限搞乱的事情了,一堆文件系统775,软件包755
Feb 03, 2018 07:20:28 PM
尝试后发现,Identifier 和 Sequence number 都和发的不一样,似乎是发的时候被内核改掉了。如果要区分不同目标的包的话是不是就只能自己在payload里面加东西了?
Feb 03, 2018 08:09:32 PM
不同目标的话,地址是不一样的呀。跟 UDP 差不多用就可以了。
Jul 28, 2018 03:22:38 AM
我的Arch Linux系统,
ping是没有setuid的, 但是看/proc/sys/net/ipv4/ping_group_range 还是关闭的
可以 ping -n www.163.com, 如何解释!
Jul 28, 2018 11:31:10 AM
有网络特权的:
>>> getcap =ping
/usr/bin/ping = cap_net_raw+ep
ls 也会显示成特殊的颜色。
Mar 21, 2019 12:37:48 AM
没理解到,而如果没有权限,则会得到「Permission denied」错误 这一点,经过我观察,大部分内核版本高达4.4的ubuntu server基本都是range 1 0 -I权限拒绝错误,traceroute也是带DGRAM ICMP的版本,在原生range 1-2^32 的android 4.4(3.10)上uid 10081反而一切正常,如果说这个实现是为了摆脱SOCK_RAW的root权限问题,那还permission denied有何意义?是selinux的问题吗
Mar 21, 2019 02:30:05 PM
你要去改 ping_group_range 呀。估计内核是为了保持稳定,所以默认没有授予权限。但是这个管理员是可以改的嘛。
Sep 19, 2020 04:31:32 PM
Linux内核version 5.4.0-1018-raspi,使用python写一个多线程的ICMP ping部分线程创建socket总是提示「Protocol not supported」看了这个,修改了。原来是这样。
Nov 28, 2020 04:40:13 PM
现在相关内容在 man手册 icmp(7)
ChromeOS 上为linux容器编译的“特制内核”默认值果然是“1 0”……