在复现提权的时候发现对于shell和权限体系的了解很重要,基础打好便事倍功半,那么让我们了解一下
SHELL
参考
http://bbs.chinaunix.net/thread-218853-1-1.html
https://doc.yonyoucloud.com/doc/wiki/project/shell-tutorial/shell-echo-command.html
关于shell
Shell本身是一个用C语言编写的程序,它是用户使用Unix/Linux的桥梁,用户的大部分工作都是通过Shell完成的。Shell既是一种命令语言,又是一种程序设计语言。
什么是shell
Shell本身是一个用 C 语言编写的程序,它是用户使用 Unix/Linux 的桥梁,用户的大部分工作都是通过 Shell 完成的。Shell既是一种命令语言,又是一种程序设计语言
。作为命令语言,它交互式地解释和执行用户输入的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支。
它虽然不是Unix/Linux系统内核的一部分,但它调用了系统核心的大部分功能来执行程序、建立文件并以并行的方式协调各个程序的运行。因此,对于用户来说,shell是最重要的实用程序,深入了解和熟练掌握shell的特性极其使用方法,是用好Unix/Linux系统的关键。
Shell有两种执行命令的方式:
- 交互式(Interactive):解释执行用户的命令,用户输入一条命令,Shell就解释执行一条。
- 批处理(Batch):用户事先写一个Shell脚本(Script),其中有很多条命令,让Shell一次把这些命令执行完,而不必一条一条地敲命令。
Shell脚本和编程语言很相似,也有变量和流程控制语句,但Shell脚本是解释执行的,不需要编译,Shell程序从脚本中一行一行读取并执行这些命令,相当于一个用户把脚本中的命令一行一行敲到Shell提示符下执行。
常见的shell类型
Shell 是一种脚本语言,那么,就必须有解释器来执行这些脚本。
Unix/Linux 上常见的 Shell 脚本解释器有 bash、sh、csh、ksh 等,习惯上把它们称作一种Shell。我们常说有多少种 Shell,其实说的是 Shell 脚本解释器
bash
bash 是 Linux 标准默认的 shell
。bash 由 Brian Fox 和 Chet Ramey 共同完成,是 BourneAgain Shell 的缩写,内部命令一共有40个。
Linux 使用它作为默认的 shell 是因为它有诸如以下的特色:
- 可以使用类似 DOS 下面的 doskey 的功能,用方向键查阅和快速输入并修改命令。
- 自动通过查找匹配的方式给出以某字符串开头的命令。
- 包含了自身的帮助功能,你只要在提示符下面键入 help 就可以得到相关的帮助。
sh
sh 由 Steve Bourne 开发,是 Bourne Shell 的缩写,sh 是 Unix 标准默认的 shell。
ash
ash shell 是由 Kenneth Almquist 编写的,Linux 中占用系统资源最少的一个小 shell,它只包含24个内部命令,因而使用起来很不方便。
csh
csh 是 Linux 比较大的内核,它由以 William Joy 为代表的共计47位作者编成,共有52个内部命令。该 shell 其实是指向 /bin/tcsh 这样的一个 shell,也就是说,csh 其实就是 tcsh。
ksh
ksh 是 Korn shell 的缩写,由 Eric Gisin 编写,共有42条内部命令。该 shell 最大的优点是几乎和商业发行版的 ksh 完全兼容,这样就可以在不用花钱购买商业版本的情况下尝试商业版本的性能了。
1 | bash 是 Bourne Again Shell 的缩写,是 linux 标准的默认 shell ,它基于 Bourne shell,吸收了 C shell 和 Korn shell 的一些特性。bash 完全兼容 sh,也就是说,用 sh 写的脚本可以不加修改的在 bash 中执行。 |
在何时使用shell
Shell 似乎是各 UNIX 系统之间通用的功能,并且经过了 POSIX 的标准化。因此,Shell 脚本只要“用心写”一次,即可应用到很多系统上。因此,之所以要使用 Shell 脚本是基于:
- 简单性:Shell 是一个高级语言;通过它,你可以简洁地表达复杂的操作。
- 可移植性:使用 POSIX 所定义的功能,可以做到脚本无须修改就可在不同的系统上执行。
- 开发容易:可以在短时间内完成一个功能强大又好用的脚本。
但是,考虑到 Shell 脚本的命令限制和效率问题,下列情况一般不使用 Shell:
- 资源密集型的任务,尤其在需要考虑效率时(比如,排序,hash 等等)。
- 需要处理大任务的数学操作,尤其是浮点运算,精确运算,或者复杂的算术运算(这种情况一般使用 C++ 或 FORTRAN 来处理)。
- 有跨平台(操作系统)移植需求(一般使用 C 或 Java)。
- 复杂的应用,在必须使用结构化编程的时候(需要变量的类型检查,函数原型,等等)。
- 对于影响系统全局性的关键任务应用。
- 对于安全有很高要求的任务,比如你需要一个健壮的系统来防止入侵、破解、恶意破坏等等。
等等。。。。。。
如何写一个脚本
打开文本编辑器,新建一个文件,扩展名为 sh(sh 代表 shell),扩展名并不影响脚本执行,见名知意就好,如果你用 php 写 shell 脚本,扩展名就用 php 好了。
1 |
|
“#!” 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。echo命令用于向窗口输出文本。
运行 Shell 脚本有两种方法。
将上面的代码保存为 test.sh,并 cd 到相应目录:
1 | chmod +x ./test.sh #使脚本具有执行权限 |
注意,一定要写成 ./test.sh,而不是 test.sh。运行其它二进制的程序也一样,直接写test.sh,linux 系统会去 PATH 里寻找有没有叫 test.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 test.sh 是会找不到命令的,要用 ./test.sh 告诉系统说,就在当前目录找。
通过这种方式运行 bash 脚本,第一行一定要写对,好让系统查找到正确的解释器。
这里的”系统”,其实就是 shell 这个应用程序(想象一下 Windows Explorer),但我故意写成系统,是方便理解,既然这个系统就是指 shell,那么一个使用 /bin/sh 作为解释器的脚本是不是可以省去第一行呢?是的。
如下 当我们直接使用解释器
1 | /bin/sh test.sh |
写一个检测tmux是否掉线脚本
注 nano编辑后需要添加可执行权限 chmod +x mirai.sh
1 | #!/bin/bash |
SHELL基础
1 | 变量类型 |
运行 shell 时,会同时存在三种变量:
1) 局部变量
局部变量在脚本或命令中定义,仅在当前 shell 实例中有效,其他 shell 启动的程序不能访问局部变量。
2) 环境变量
所有的程序,包括 shell 启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候 shell 脚本也可以定义环境变量。
3) shell 变量
shell 变量是由 shell 程序设置的特殊变量。shell 变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了 shell 的正常运行
定义变量
不加 $
1 | name="hello" |
变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:
- 首个字符必须为字母(a-z,A-Z)。
- 中间不能有空格,可以使用下划线(_)。
- 不能使用标点符号。
- 不能使用 bash 里的关键字(可用 help 命令查看保留关键字)。
1 | 使用变量 |
只读变量
尝试更改只读变量,结果报错:
1 | name="hello" |
删除变量
使用 unset 命令可以删除变量并且不能删除只读变量
1 | 特殊变量 |
Unix 命令默认从标准输入设备(stdin)获取输入,将结果输出到标准输出设备(stdout)显示。一般情况下,标准输入设备就是键盘,标准输出设备就是终端,即显示器。
命令的输出不仅可以是显示器,还可以很容易的转移向到文件,这被称为输出重定向。
命令输出重定向的语法为: command > file
输出到显示器的内容就可以被重定向到文件。
例如,下面的命令在显示器上不会看到任何输出:
1 | $ who > users |
输出重定向会覆盖文件内容 不希望文件内容被覆盖,可以使用 >> 追加到文件末尾
1 | $ echo line 1 > users |
和输出重定向一样,Unix 命令也可以从文件获取输入,语法为
1 | command < file |
从键盘获取输入的命令会转移到文件读取内容。
注意:输出重定向是大于号(>),输入重定向是小于号(<)。
例如,计算 users 文件中的行数,可以使用下面的命令:
1 | wc -l users |
第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容
一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
- 标准输入文件(stdin):stdin 的文件描述符为0,Unix 程序默认从 stdin 读取数据。
- 标准输出文件(stdout):stdout 的文件描述符为1,Unix 程序默认向 stdout 输出数据。
- 标准错误文件(stderr):stderr 的文件描述符为2,Unix 程序会向 stderr 流中写入错误信息。
如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写
1 | $command > file 2>&1 |
Here Document
ere Document 目前没有统一的翻译,这里暂译为”嵌入文档“。Here Document 是 Shell 中的一种特殊的重定向方式,它的基本的形式如下
1 | command << delimiter |
- 结尾的 delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
- 开始的 delimiter 前后的空格会被忽略掉。
1 | 通过 wc -l 命令计算 document 的行数 |
/dev/null 文件
执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null
1 | $ command > /dev/null |
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到”禁止输出“的效果。
如果希望屏蔽 stdout 和 stderr,可以这样写 command > /dev/null 2>&1文件
SHELL 进阶
{} 与 ()
如上 {} 为了帮助解释器识别变量的边界不再赘述
()命令替代用来替代一个命令和命令行输出。命令替代的标准语法,也是POSIX鼓励的一种语法是:$(command). 命令替代让你捕获一个命令的输出,用它作为另一个命令的参数,或是赋值给一个变量。象在变量替代中一样,命令替代的执行是在命令行开始之前完成的。当命令行输出包含回车换行,它们会被空格代替。 同变量替代相似,命令替代使用一个美元符号之后的用括号包围的一个命令。
1 | $pwd |
试问echo知多少
echo 是一个非常简单、直接的 Linux 命令:
将 argument 送出至标準输出(STDOUT),通常就是在监视器(monitor)上输出。
1 | $ echo |
你会发现只有一个空白行,然后又回到 shell prompt 上了。
这是因为 echo 在预设上,在显示完 argument 之后,还会送出一个换行符号(new-line charactor)。
但是上面的 command 并没任何的 argument ,那结果就只剩一个换行符号了…
若你要取消这个换行符号,可利用 echo 的 -n option :
1 | $ echo -n |
于上两个 echo 命令中,你会发现 argument 的部份显示,而换行符号则视 -n option 的有无而别。
很明显的,第二个 echo 由于换行符号被取消了,接下来的 shell prompt 就接在输出结果同一行了… ^_^
事实上,echo 除了 -n options 之外,常用选项还有:
-e :启用反斜线控制字符的转换(参考下表)
-E:关闭反斜线控制字符的转换(预设如此)
-n :取消行末之换行符号(与 -e 选项下的 \c 字符同意)
1 | $ echo -e "a\tb\tc\nd\te\tf" |
‘ ‘ 与 “ “
单引号字符串的限制: 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的; 单引号字串中不能出现单引号(对单引号使用转义符后也不行)。
1 | str='this is a string' |
双引号的优点: 双引号里可以有变量 双引号里可以出现转义字符
1 | name="hello" |
权限体系
Linux操作系统基于UGO的基础权限体系
用户身份划分:(文件和目录可以属于文件(u),组(g)或其他(o)的所有者)
- U(user/owner-所属者):文件或文件夹的所属用户的权限。
- G(group-所属组):文件或文件夹的所属组队权限。
- O(others-其他人):其他用户对文件夹或文件的权限。
用户权限划分:(Linux操作系统中存在3种类型的文件权限:读(r),写(w)和执行(x)权限。这些权限确定哪些用户可以读取,写入或执行文件)
- r:对文件和文件夹读权限,用数字表示是4(2^2)
- w:对文件和文件夹写权限,用数字表示是2(2^1)
- x:对文件的执行权限,和对文件的浏览权限,用数字表示是1(2^0)
1 | ls -l 命令查看的文件权限 |
从左往右解释:
- -:文件类型;-表示文件,d表示文件夹,符号l表示符号链接,b:块设备文件,c:字符设备文件。
- rwx:即U(user)类型的权限,表示文件的所属用户对文件具有 读 写 执行 权限,可以用7表示(4+2+1)。
- rx: 即G(group)类型的权限,表示文件的所属组对文件具有 读 执行 权限,可以用5表示(4+1)。
- x: 即O(other)类型的权限,表示其他用户对文件具有 执行 权限,可以用1表示(1)。
- root:即文件的所属用户是root。
- root:即文件的所属组是root组。
默认权限与修改
1 | 文件默认权限是 644 = -rw-r--r-- ;目录默认权限是 755 = drwxr-xr-xr |
chgrp:修改文件或文件夹的所属组。
chown:修改文件或文件夹的所属用户。
chown
命令可以修改文件或目录的所有者和所属组的,格式为chown[参数]所有者:所属组文件或目录名称
。注意,针对目录进行操作时需要加上大写参数-R
来表示递归操作,即对目录内所有的文件进行整体操作。
1 | chown root:test test |
chmod:修UGO权限。
1 | chmod u+w 表示为所属用户添加写权限 |
特殊权限
Linux系统中还有一种更灵活的特殊权限,如SUID、SGID与SBIT等特殊权限位。
SUID
- 是一种对二进制程序进行设置的特殊权限,可以让二进制程序的执行者临时拥有所属用户的权限(仅对拥有执行权限的二进制程序有效)。例如,本来只有root用户可以操作etc/shadow文件,该文件中存储了用户的密码,而
SUID
则可以让普通用户也可以临时操作这个文件。 - 其实意思就是说普通用户对这个文件还是没有任何读,写和执行的权限,但是运行普通用户使用某个可执行程序来对这个文件进行操作,比如说
cat
命令就是一个二进制程序,给文件赋予cat
的SUID权限后,普通用户就可以使用这个命令来操作文件了。 - 比如下面对
cat
命令添加了SUID
权限,所有者的权限由rwx
变成了rws
,这个时候普通用户就可以对没有权限的文件使用这个命令了,注意,如果原来没有x
权限,则rw-
会变成rwS
。
1 | [test@VM_0_11_centos /]$ cat /etc/shadow |
SGID
- 类似于
SUID
命令,只是它是临时获取到文件所属组的权限。如果对目录使用该命令,则只要拥有写和执行的权限就可以在目录下创建文件,并且该文件都属于该目录所属的组。
1 | [root@VM_0_11_centos bin]# chmod 4755 /bin/cat |
SBIT(StickyBit)
- 可以确保只能删除自己的文件,不能删除其他用户的文件。当目录被设置
SBIT
特殊权限位后,文件的其他人权限部分的x
执行权限就会被替换成t
或者T
,原本有x
执行权限则会写成t
,原本没有x
执行权限则会被写成T
。
1 | [root@VM_0_11_centos bin]# chmod 1775 cat |
sticky:作用于文件夹,表示在该文件夹下的文件只能由文件的owner删除,其他人可以在文件夹下创建、浏览、但也只能删除自己为owner的文件。O权限中的x会被替换成t。
例如:chmod o+t:将文件夹设置sticky
chattr、lsattr命令
Linux系统中的文件除了具备一般权限和特殊权限之外,还有一种隐藏权限,默认情况下不能直接被用户发觉。例如仅能在日志文件中追加内容而不能修改或删除内容,这在一定程度上保障了Linux系统的安全性。
chattr
- 用于设置文件的隐藏权限,格式为
chattr[参数] 文件
1 | chattr +a test |
常用参数
- i,无法对文件进行修改,若对目录设置了该参数,则仅能修改其中的子文件内容而不能新建或删除文件
- a,仅允许补充(追加)内容,无法覆盖/删除内容(AppendOnly)
- S,文件内容在变更后立即同步到硬盘(sync)
- s,彻底从硬盘中删除,不可恢复(用0填充原文件所在硬盘区域)
- A,不再修改这个文件或目录的最后访问时间(atime)
- b,不再修改文件或目录的存取时间
- D,检查压缩文件中的错误
- d,使用dump命令备份时忽略本文件/目录
- c,默认将文件或目录进行压缩
- u,当删除该文件后依然保留其在硬盘中的数据,方便日后恢复
- t,让文件系统支持尾部合并(tail-merging)
- X,可以直接访问压缩文件中的内容
lsattr
- 用于显示文件的隐藏权限,格式为
lsattr [参数] 文件
,隐藏权限必须使用lsattr命令来查看。
文件访问控制列表
上面提到的的权限命令都是针对一类用户的权限配置,如果希望对某个指定的用户进行单独的权限控制,就需要用到文件的访问控制列表(ACL),其实本质上来讲也就是设置文件或目录的ACL来修改权限。
如果针对某个目录设置了ACL,则目录中的文件会继承其ACL,若针对文件设置了ACL,则文件不再继承其所在目录的ACL。
- setfacl
- 用于管理文件的
ACL
规则,格式为setfacl [参数] 文件名称
。使用ls
命令查看的时候可以看到文件的权限最后一个点(.)
变成了加号(+)
,就该文件已经设置了ACL
了。
1 | setfacl -m u:USERNAME:MODE FILE |
- 常用参数
- 针对目录文件需要使用-R递归参数
- 针对普通文件则使用-m参数
- 如果想要删除某个文件的ACL,则可以使用-x参数 -b删除所有
1 | 为某个目录设定默认的ACL使其目录内新创建的任何文件或目录继承此ACL |
- getfacl
- 用于显示文件上设置的
ACL
信息,格式为getfacl 文件名称
- 添加普通用户进入/root的权限
1 | [root@VM_0_11_centos dev]# setfacl -Rm u:test:rwx /root |
su命令与sudo服务
1 | su |
- 用于切换用户, 例如su - test,减号(-)表示完全切换到新的用户,即把环境变量信息也变更为新用户的相应信息,而不是保留原始的信息。
su 切换不同的用户的身份:
默认只是切换身份,并没有切换环境变量,环境变量依然是普通用户的。
切换用户身份时,用户的环境变量也切换成新用户的环境变量,所以”-“不能省略,否则有些操作无法执行。
su root 输入root密码后切换到root用户但是pwd目录不变
su - root 输入root密码后切换到root用户但是pwd目录/root
1 | sudo |
- 把特定命令的执行权限赋予给指定用户,这样既可保证普通用户能够完成特定的工作,也可以避免泄露root管理员密码,格式为
sudo [参数] 命令名称
。 - 常用参数
- -h,列出帮助信息
- -l,列出当前用户可执行的命令
- -u,用户名或UID值以指定的用户身份执行命令
- -k,清空密码的有效时间,下次执行sudo时需要再次进行密码验证
- -b,在后台执行指定的命令
- -p,更改询问密码的提示语
- 使用visudo开启用户的
sudo
服务,类似于vim的编辑方式
1 | [root@VM_0_11_centos /]# visudo |
- 如果只想开启部分服务,需要查到服务的路径,然后配置即可,例如只开启
cat
服务,查到它的路径,再配置sudo
服务即可,此外可以配置免验证密码执行sudo
命令。
1 | [test@VM_0_11_centos ~]$ whereis cat |
sudo -i root 与sudo - root、sudo -i ,sudo - ,sudo root 效果相同 提示输入密码时该密码
为当前账户的密码,要求执行该命令的用户必须在sudoers 中才可以 su 需要的是切换后账户的密码,用法为“su 账户名称”
sudo : 暂时切换到超级用户模式以执行超级用户权限,一般指的是root 用户,提示输入密码时该密码为当前用户的密码,而不是超级账户的密码。不过有时间限制,Ubuntu 默认为一次时长15 分钟。
su : 切换到某某用户模式,提示输入密码时该密码为切换后账户的密码,用法为“su 账户名称”。如果后面不加账户时系统默认为root 账户,密码也为超级账户的密码,没有时间限制。
sudo -i: 为了频繁地执行某些只有超级用户才能执行的权限,而不用每次输入密码,可以使用该命令。提示输入密码时该密码为当前账户的密码。没有时间限制。执行该命令后提示符变为“#”而不是“$”:想退回普通账户时可以执行“exit”或“logout” 。要求执行该命令的用户必须在sudoers 中才可以。
sudo: command not found
当我们用sudo来执行cd、ls等命令时,会出现command not found的提示
在执行Linux命令时,如果在其前面加上sudo,就表示以root权限执行。但是这其实是有一个前提的,就是只有那些Linux内置系统命令才可以用如此的形式来执行,而对于Shell内置命令或其他用户自定义命令、别名等,是不能用sudo来使用root权限的。
因为当在Linux下用sudo执行某一命令时,是在原进程(parent process)的基础上fork出来一个子进程(child process),这个子进程是以root权限执行的。然后在子进程中,执行你在sudo后面跟的命令。
在子进程中是无法调用涉及到父进程的状态的一些命令的,所以非系统内置命令会被拒绝。这就是为什么会出现command not found的提示。具体来说,当我们执行:
1 | sudo cd /home/michael |
所在这个shell进程中(称其为PP,表示parent process)fork出一个子进程(称其为CP,表示child process),那么在CP中是无法改变PP的所在目录的。
sudo ls /home/michael
sudo后由PP产生了CP,CP是无法获取PP所在的目录的内容的(具体来说,是读取该目录的block data)
1 | shell内置命令 |
sudoers 与 passwd
1 | /etc/sudoers是Unix和Linux系统上的一个文件,它包含了授权用户或组以root或其他特权用户身份运行命令的规则。sudoers文件通常只能由系统管理员或具有特权的用户进行编辑。 |