10
29
2013
11

不需要 root 权限的 ICMP ping

本文来自依云's Blog,转载请注明。

ICMP 套接字是两年前 Linux 内核新加入的功能,目的是允许不需要 set-user-id 和CAP_NET_RAW权限的 ping 程序的实现。大家都知道,set-user-id 程序经常成为本地提权的途径。在 Linux 内核加入此功能之前,以安全为目标的 Openwall GNU/*/Linux 实现了除 ping 程序之外的所有程序去 suid 化……这个功能也是由他们提出并加入的。

我并没有在 man 手册中看到关于 ICMP 套接字的信息。关于 ICMP 套接字使用的细节来自于内核邮件列表

使用 ICMP 套接字的好处

  1. 程序不需要特殊的权限;
  2. 内核会帮助搞定一些工作。

坏处是:

  1. 基本没有兼容性可讲;
  2. 需要调整一个内核参数。

这个内核参数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 不支持这个特性。

Category: Linux | Tags: linux python 网络 ICMP | Read Count: 26761
None 说:
Oct 31, 2013 01:26:13 AM

原来如此,ping 是带 set-user-id 的..
查了下 Windows 下的解决方法似乎是调用系统API IcmpSendEcho() ...

Avatar_small
依云 说:
Oct 31, 2013 12:21:55 PM

不是所有 ping 是带 suid 的,比如我系统上的是带 CAP_NET_RAW 权限的。都是超过需要的特权就是了。

jack 说:
Nov 12, 2013 04:54:47 PM

看到这个我又想起来之前装android-sdk包把文件系统权限搞乱的事情了,一堆文件系统775,软件包755

Avatar_small
Craynic 说:
Feb 03, 2018 07:20:28 PM

尝试后发现,Identifier 和 Sequence number 都和发的不一样,似乎是发的时候被内核改掉了。如果要区分不同目标的包的话是不是就只能自己在payload里面加东西了?

Avatar_small
依云 说:
Feb 03, 2018 08:09:32 PM

不同目标的话,地址是不一样的呀。跟 UDP 差不多用就可以了。

linuxdog 说:
Jul 28, 2018 03:22:38 AM

我的Arch Linux系统,
ping是没有setuid的, 但是看/proc/sys/net/ipv4/ping_group_range 还是关闭的
可以 ping -n www.163.com, 如何解释!

Avatar_small
依云 说:
Jul 28, 2018 11:31:10 AM

有网络特权的:

>>> getcap =ping
/usr/bin/ping = cap_net_raw+ep

ls 也会显示成特殊的颜色。

KStar 说:
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的问题吗

Avatar_small
依云 说:
Mar 21, 2019 02:30:05 PM

你要去改 ping_group_range 呀。估计内核是为了保持稳定,所以默认没有授予权限。但是这个管理员是可以改的嘛。

leslie 说:
Sep 19, 2020 04:31:32 PM

Linux内核version 5.4.0-1018-raspi,使用python写一个多线程的ICMP ping部分线程创建socket总是提示「Protocol not supported」看了这个,修改了。原来是这样。

Gap 说:
Nov 28, 2020 04:40:13 PM

现在相关内容在 man手册 icmp(7)
ChromeOS 上为linux容器编译的“特制内核”默认值果然是“1 0”……


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

| Theme: Aeros 2.0 by TheBuckmaker.com