Linux 文件属性及特殊权限详解

文件属性结构如下所示,通过该图我们可以全局把握类 Unix 操作系统的文件属性组成结构,本篇博客不仅会介绍一些常用的属性,还会引申介绍一些容易被忽略的特殊权限。

在这里插入图片描述

0x10 文件类型

Linux 第一个字符代表的是文件类型

  • [ d ] -> directory,目录
  • [ - ] -> 普通文件
  • [ l ] -> link,链接文件
  • [ b ] -> block, 块设备,如硬盘和内存
  • [ c ] -> chapter,字符设备,如键盘和鼠标
  • [ s ] -> socket,套接字,即网络流

0x20 文件权限

紧接着为三个一组,每组都是 [rwx] 的组合,表示文件权限,即 read/write/execute,没有权限就是 [ - ]。文件类型和文件属性用 10 个字符来确定。

Linux/Unix 的文件调用权限分为三级 : 文件所有者(Owner)、用户组(Group)、其它用户(Other Users)。
在这里插入图片描述

只有文件所有者和超级用户可以修改文件或目录的权限。可以使用绝对模式(八进制数字模式),符号模式指定文件的权限。

在这里插入图片描述

0x21 命令行修改文件权限

1 八进制掩码

历史上,曾用 3 位二进制数表示权限 rwx,如下所示,刚好可以表示完所有的权限集合,|r|w|x|,哪一位置 1,表示拥有该权限

# 权限 rwx 二进制
7 读 + 写 + 执行 rwx 111
6 读 + 写 rw- 110
5 读 + 执行 r-x 101
4 只读 r– 100
3 写 + 执行 -wx 011
2 只写 -w- 010
1 只执行 –x 001
0 000

因此 chmod 就可以用简单的八进制掩码来修改文件权限,例如

chmod 777 test.txt		# -rwxrwxrwx,所有人可读可写可执行
chmod 666 test.txt		# -rw-rw-rw-
2 符号模式

符号模式对于初学者可能更加容易记忆,使用符号模式可以设置多个项目:who(用户类型),operator(操作符)和 permission(权限),每个项目的设置可以用逗号隔开。

who 的符号模式表所示:

who 用户类型 说明
u user 文件所有者
g group 文件所有者所在组
o others 所有其他用户
a all 所用用户, 相当于 ugo

operator 的符号模式表:

Operator 说明
+ 为指定的用户类型增加权限
- 去除指定用户类型的权限
= 设置指定用户权限的设置,即将用户类型的所有权限重新设置

permission 的符号模式表:

模式 名字 说明
r 设置为可读权限
w 设置为可写权限
x 执行权限 设置为可执行权限
X 特殊执行权限 只有当文件为目录文件,或者其他类型的用户有可执行权限时,才将文件权限设置可执行
s setuid/gid 当文件被执行时,根据who参数指定的用户类型设置文件的setuid或者setgid权限
t 粘贴位 设置粘贴位,只有超级用户可以设置该位,只有文件所有者u可以使用该位

chmod 命令实例

chmod ugo+r test.txt	# -rwxrwxrwx,所有人可读可写可执行

0x22 代码修改文件权限

Linux 提供了系统调用 chmod,用于修改文件权限,查看 Linux 编程手册(man 2 chmod),提供可供修改权限的函数如下

#include <sys/stat.h>

int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);

#include <fcntl.h>           /* Definition of AT_* constants */
#include <sys/stat.h>

int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags);

chmod 函数只需要提供文件名和修改的权限即可,而 fchmod 则需要先打开文件,获取文件句柄后使用。基于系统安全,如果欲将数据写入一执行文件,而该执行文件具有S_ISUID 或S_ISGID 权限,则这两个位会被清除。如果一目录具有S_ISUID 位权限,表示在此目录下只有该文件的所有者或root 可以删除该文件。

chmod("/testdir/aaa.txt", S_IRUSR|S_IWUSR|S_IRGRP|S_IRGRP|S_IROTH|S_IWOTH);	// 666o

参数 mode 有下列数种组合:

  • S_ISUID 04000 文件的 (set user-id on execution)位
  • S_ISGID 02000 文件的 (set group-id on execution)位
  • S_ISVTX 01000 文件的sticky 位
  • S_IRUSR (S_IREAD) 00400 文件所有者具可读取权限
  • S_IWUSR (S_IWRITE)00200 文件所有者具可写入权限
  • S_IXUSR (S_IEXEC) 00100 文件所有者具可执行权限
  • S_IRGRP 00040 用户组具可读取权限
  • S_IWGRP 00020 用户组具可写入权限
  • S_IXGRP 00010 用户组具可执行权限
  • S_IROTH 00004 其他用户具可读取权限
  • S_IWOTH 00002 其他用户具可写入权限
  • S_IXOTH 00001 其他用户具可执行权限

比如,IDA 逆向时,我们需要将 mode 参数修改为 8 进制表示,就可以很容易看出其权限

在这里插入图片描述

0x30 文件拥有者

Linux/Unix 是多人多工操作系统,所有的文件皆有拥有者。利用 chown 将指定文件的拥有者改为指定的用户或组,用户可以是用户名或者用户 ID(UID),组可以是组名或者组 ID(GID),文件是以空格分开的要改变权限的文件列表,支持通配符

1、chgrp: 更改文件属组

chgrp [OPTION]... GROUP FILE...

常用参数

  • -R,递归更改文件属组,更改某个目录属组,则该目录下的所有文件属组都会更改,这个参数在 Linux 其他命令中也经常用到

2、chown:更改文件属主/属组

chown [OPTION]... [OWNER][:[GROUP]] FILE...

0x40 特殊权限

众所周知,Linux的文件权限如: 777;666等,其实只要在相应的文件上加上UID的权限,就可以用到加权限人的身份去运行这个文件。所以我们只需要将bash复制出来到另一个地方,然后用root加上UID权限,只要用户运行此Shell就可以用用root的身份来执行任何文件了

这种方式可能被用于提权。

0x41 setuid/setgid (rws)

一般来说,谁调用该文件,就只有调用该文件的所有者拥有的权限。这在文件属性中都规定好了。那么问题来了,比如说,如果普通用户想修改密码,发现保存密码配置的文件对于普通用户不可写,那么理论上普通用户连自己的密码都不能修改?

$ ls -lh /etc/passwd
-rw-r--r-- 1 root root 3.2K Jan 14 11:30 /etc/passwd

setuid / setgid 则可以改变这种设置,相当于改变程序运行时的文件所有者。我们再看看用于修改密码的二进制

$ ls -lh /usr/bin/passwd
-rwsr-xr-x 1 root root 63K Feb  7  2020 /usr/bin/passwd

用户属主的权限位 x 是 s,这里的 s 意思是其他用户运行此命令时,会临时具有 root 用户的权限运行此文件

  • setuid:让普通用户可以以 root 用户的角色运行只有 root 帐号才能运行的程序或命令
  • setgid:该权限只对目录有效。目录被设置该位后,任何用户在此目录下创建的文件都具有和该目录所属的组相同的组

0x42 Sticky Bit (rwt)

In Unix-like operating systems, a sticky bit is a permission bit which is set on a file or folder, thereby permitting only the owner or root user of the file or folder to modify, rename or delete the concerned directory or file. No other user would be permitted to have these privileges on a file which has a sticky bit. In Unix-like systems, without the sticky bit on, any user can modify, rename or delete the directory or file regardless of the owner of the file or folder.

类 Unix 操作系统中的文件权限,读写可执行,其中的写和执行权限,就代表了重命名、删除文件的权限。而设置了 Sticky Bit 的文件,只有文件、目录所有者或者 root 才能够有权限重命名或删除该文件。

例如 Linux 文件系统根目录下的 tmp 目录

$ ls -lh / | grep tmp 
drwxrwxrwt  13 root root  12K Feb  8 15:09 tmp

如果本来该位上有 x,则特殊位显示小写字母(s, s, t),否则显示大写字母(S,S,T)。即 rws/rwS

0x43 延伸:具有写权限却不能写入文件

某天在测试某个设备时,突然发现一个问题,明明对该文件具有写的权限,但是却不能写入信息

$ ls -lh /tmp/my.log 
-rw-rw-rw- 1 lys  lys     0 28 15:48 my.log

我们是以 jerry 的身份修改 my.log ,代码如下

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>

int main(int argc, char const *argv[])
{
    
    
	char *name = "/tmp/my.log";
	FILE *fp = fopen(name, "a+");
	if(fp == NULL) {
    
    
		fprintf(stderr, "Couldn't open: %s: %s\n", name, strerror(errno));
	}
	else
		fclose(fp);
	return 0;
}

运行该程序

$ ./test
Couldn't open: /tmp/my.log: Permission denied

这个问题来源于在 Linux 4.19 内核引入的一个内核参数 this commitfs.protected_regular,用于禁止在全局可写 Sticky Bit 目录中打开不属于用户的 FIFO 或常规文件。相关文档位于 Documentation/sysctl/fs.txt

protected_fifos:

The intent of this protection is to avoid unintentional writes to an attacker-controlled FIFO, where a program expected to create a regular file.

protected_regular:

This protection is similar to protected_fifos, but it avoids writes to an attacker-controlled regular file, where a program expected to create one.

When set to “0”, writing to regular files is unrestricted.

When set to “1” don’t allow O_CREAT open on regular files that we don’t own in world writable sticky directories, unless they are owned by the owner of the directory.

When set to “2” it also applies to group writable sticky directories.

即这个保护可以用以下命令关闭

sysctl fs.protected_regular=0

这个特性用于缓解以下漏洞

  • CVE-2000-1134
  • CVE-2007-3852
  • CVE-2008-0525
  • CVE-2009-0416
  • CVE-2011-4834
  • CVE-2015-1838
  • CVE-2015-7442
  • CVE-2016-7489

由于一些开发者没有注意到该特性,会导致一些空指针问题,例如如下代码
在这里插入图片描述

当一个用户运行此程序,会试图创建该文件,并将该文件的权限修改为 -rw-rw-rw- 。当另外一个用户运行此程序,按照正常逻辑,该文件已经存在,因此不需要创建,直接就会打开,但是其实是空指针。fclose 时报错。

0x50 索引节点

0x51 inode

索引节点的英文名 inode(index node),其实输入命令 ls -li 就可以看到文件的索引节点。

ls -lhi ~
total 617M
2360250 -rw-r--r--  1 lys lys    0 Jan  6 17:47 123
2360331 -rwxr-xr-x  1 lys lys  68M Apr 15  2021 code_1.55.2-1618307277_amd64.deb

Linux 文件系统类型有 ext2/3/4,每个存储设备或分区被格式化后,创建为一个新的文件系统,其实这个过程包含两部分:创建 inode,创建 block。inode 用于定位文件位置,block 用于存放实际的文件内容。inode 就是一块存储空间,不同操作系统默认大小不一样。

查看文件系统分区及类型

$ df -Th                                       
Filesystem       Type      Size  Used Avail Use% Mounted on
udev             devtmpfs  3.9G     0  3.9G   0% /dev
tmpfs            tmpfs     797M  996K  796M   1% /run
/dev/sda1        ext4       62G   56G  3.0G  95% /
tmpfs            tmpfs     3.9G     0  3.9G   0% /dev/shm
tmpfs            tmpfs     5.0M     0  5.0M   0% /run/lock
tmpfs            tmpfs     4.0M     0  4.0M   0% /sys/fs/cgroup
tmpfs            tmpfs     797M   56K  797M   1% /run/user/1000

查看指定文件 inode

$ stat 123       
  File: 123
  Size: 5               Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d      Inode: 2360250     Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/     lys)   Gid: ( 1000/     lys)
Access: 2022-01-14 20:06:21.610734953 +0800
Modify: 2022-02-08 20:36:41.541211863 +0800
Change: 2022-02-08 20:36:41.541211863 +0800
 Birth: 2022-01-06 20:47:18.455172720 +0800

查看系统分区 inode 使用情况

$ df -i 
Filesystem        Inodes   IUsed   IFree IUse% Mounted on
udev             1010848     381 1010467    1% /dev
tmpfs            1019341     628 1018713    1% /run
/dev/sda1        4136960 1265184 2871776   31% /
tmpfs            1019341       1 1019340    1% /dev/shm
tmpfs            1019341       2 1019339    1% /run/lock
tmpfs               1024      17    1007    2% /sys/fs/cgroup
tmpfs             203868      65  203803    1% /run/user/1000

查看文件系统 inode 大小

sudo dumpe2fs /dev/sda1 | grep -i "inode size"
dumpe2fs 1.45.6 (20-Mar-2020)
Inode size:               256

查看文件系统 block 大小

$ sudo dumpe2fs /dev/sda1 | grep -i "block size"
dumpe2fs 1.45.6 (20-Mar-2020)
Block size:               4096

0x52 block

硬盘存储的最小单位是扇区(sector),每个扇区存储 512B。Linux 内核使用块(block)作为最小寻址单元,也就是说操作系统一次性会连续读取多个扇区,这些扇区组成了一个块。 block 的大小是 sector 的 2^n 次方倍(n 可以为 0),但是不大于 page size。常见的 block 大小为 512Bytes,1KB,4KB

查看每个分区 block 数量

$ df -l /dev/sda1   
Filesystem     1K-blocks     Used Available Use% Mounted on
/dev/sda1       64806500 58404920   3079828  95% /

查看指定分区的扇区、磁道、柱面、磁头等信息,磁盘的容量=磁头*磁道*扇区(512B)*柱面

$ sudo fdisk -l /dev/sda1                                                                     1 ⨯
Disk /dev/sda1: 63.04 GiB, 67693969408 bytes, 132214784 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

总结

Linux 系统是一种典型的多用户操作系统,不同用户处于不同地位,拥有不同权限。为保护系统安全,不同用户对同一文件的访问权限做了不同规定。我们在这里讨论了常见的文件属性,也讲述其中的一些特殊权限。并结合一些实际案例(拥有写权限却不能写入文件)介绍 Linux 内核新特性。这都是由实际项目中发现的安全问题引发的一些思考。

参考文献

猜你喜欢

转载自blog.csdn.net/song_lee/article/details/122827879