ROOT基本步骤
手机实现root只需要执行以下三步:
cp /data/local/tmp/su /system/bin/ #copy su 到/system/分区
chown root:root su #su的所有者置成root
chmod 4775 /system/bin/su #把su置成-rwsrwxr-x
第一条命令cp /data/local/tmp/su /system/bin/作用是把su文件放到环境变量中,这样shell在任意路径下执行命令su都能成功。
那么,如果我不想copy,我直接在/data下放置su文件,并完成第2、3条命令,需要su时直接在/data下执行,可以吗?不可以
因为Android 4.3之后 /system 和 /data 分区以 nosuid option被挂载,让文件的SUID标识失效,所以/data分区在被mount时,其中的可执行文件不能有SUID权限
如果非要这么搞,需要重新挂载一次data分区:
mount -o rw,seclabel,suid,nodev,noatime,background_gc=on,discard,user_xattr,inline_xattr,acl,inline_data,inline_dentry,extent_cache,inline_encrypt,active_logs=6,remount -t f2fs /data
文件特殊权限-SetUID
SetUID的功能
实现SetUID功能的前提条件
(1) 只有可执行的二进制程序才能设定SUID
(2) 命令执行者要对该程序拥有x(执行)权限
SetUID的功能
(1) 命令执行者在执行改程序时获得该程序文件属主的身份(在执行程序的过程中灵魂附体为文件的属组)
(2) SetUID权限只在该程序中有效,也就是说身份改变只在程序执行过程中有效。
举例:
- passwd命令拥有SetUID权限,所以普通用户可以修改自己的密码
这个passwd为什么要有setUID权限那??? 普通用户可以改自己的密码的,真正的密码其实写入了/etc/shadow文件中。但是通过查看shadow文件的权限可知:
shadow文件的权限是000,普通用户别说写了,连看都看不到这个文件,但是普通用户可以更改自己的密码,实际上是写入了/etc/shadow这个文件, 普通用户明明对他没有权限,怎么可能写入这个文件的那?因为这个passwd这条命令作用的,当普通用户执行passwd这条命令的时候,因为passwd有SUID权限, 任何普通用户在执行这条命令的时候,都暂时性地获得这个文件的所有者权限,普通用户在执行passwd命令的时候,系统没有把他当作普通用户来对待,而是当作超级用户来对待,超级用户对shadow文件拥有任何权限,虽然shadow文件的权限是000,到那时这个权限对超级用户是没有意义的超级用户对这个文件依然有rwx权限。
注意:普通用户对passwd作为其他人的身份进行文件的操作,其他人必须要有执行权限x的,这样才能调用passwd命令,作为一个前提条件。
设定SetUID的方法
4代表SetUID
chmod 4755 文件名
chmod u+s 文件名
系统有一些默认的SetUID权限,如果没有特殊情况,不允许用户设定SetUID的其他程序,系统有了这些SetUID命令,像passwd命令,是迫不得已,因为这样已经非常危险了,如果还要手工设定一些SetUID的一些命令,万一要是设错了,就会带来一些灾难性的后果。
4755 4代表SetUID,跟所有者有关
2755 2代表SetGID,跟所属组有关
1755 1代表SetBIT权限,跟其他人有关
7755 三种权限都有,没有意义,因为操作的对象是不一样的
su程序基本功能
为什么通过执行SetUID后的su程序就能获得root权限?
答案是su中代码赋予shell进程持久性的root用户权限
su源码 链接: su源码
基本逻辑原理:
su中新fork出来的bash进程具有root权限。
Android系统对app进程root权限的进一步限制
(1)在 Android 4.3 之前,app进程 可以直接借助 有SUID标志位的su二进制文件 来获取root用户权限。
(2)Android 4.3之后,app进程基于SUID获取root权限的方案被禁用。主要措施是:
/system 和 /data 分区以 nosuid option被挂载,让文件的SUID标识失效。
app进程是由zygote 进程 fork产生的。zygote进程设置了NO_NEW_PRIVS标志,父进程的NO_NEW_PRIVS位会在父进程fork、clone和execve时,被子进程继承 ,并且不能被撤销。NO_NEW_PRIVS标志会让当前进程在执行可执行文件时,进程的EUID和EGID不受可执行文件的SUID和SGID位影响。
(3)系统进程改用 Capability来获取root权限,但app进程不行。
系统daemon可通过可执行文件的capability来获取进程的cap_effective,
但app进程不能这样做,因为app进程是由zygote 进程fork出来的;而zygote进程设置了NO_NEW_PRIVS标志,使得app进程无法通过可执行文件的capability来获取cap_effective。
NO_NEW_PRIVS标志会使SUID和SGID位无法改变进程的 uid 或 gid,file capability也不会被添加到进程的capability中。也就是NO_NEW_PRIVS标志会使获取root权限的 SUID方案和file capability方案 失效。
参考:https://www.kernel.org/doc/html/latest/translations/zh_CN/userspace-api/no_new_privs.html
为了对 root 权限进行更细粒度的控制,实现按需授权,解决SetUID带来的风险,Linux 引入了另一种机制叫 capabilities。
Linux capabilities 是什么?
Capabilities 机制是在 Linux 内核 2.2 之后引入的,原理很简单,就是将之前与超级用户 root(UID=0)关联的特权细分为不同的功能组,Capabilites 作为线程(Linux 并不真正区分进程和线程)的属性存在,每个功能组都可以独立启用和禁用。其本质上就是将内核调用分门别类,具有相似功能的内核调用被分到同一组中。
这样一来,权限检查的过程就变成了:在执行特权操作时,如果线程的有效身份不是 root,就去检查其是否具有该特权操作所对应的 capabilities,并以此为依据,决定是否可以执行特权操作。
Capabilities 可以在进程执行时赋予,也可以直接从父进程继承。所以理论上如果给 nginx 可执行文件赋予了 CAP_NET_BIND_SERVICE capabilities,那么它就能以普通用户运行并监听在 80 端口上。
capabilities 的赋予和继承
Linux capabilities 分为进程 capabilities 和文件 capabilities。对于进程来说,capabilities 是细分到线程的,即每个线程可以有自己的capabilities。对于文件来说,capabilities 保存在文件的扩展属性中。如果想修改这些属性,需要具有 CAP_SETFCAP 的 capability。文件与线程的 capabilities 共同决定了通过 execve() 运行该文件后的线程的 capabilities。
文件的 capabilities 功能,需要文件系统的支持。如果文件系统使用了 nouuid 选项进行挂载,那么文件的 capabilities 将会被忽略。
如何执行本文最开始列出的三个命令
前文已述,一个shell进程,要想获得root权限,需要执行下列代码:
cp /data/local/tmp/su /system/bin/ #copy su 到/system/分区
chown root:root su #su的所有者置成root
chmod 4775 /system/bin/su #把su置成-rwsrwxr-x
但问题是,上面的每一行代码,都需要root用户权限才能执行。
而上述代码本身就是用于获取root用户权限的。所以再执行上述代码之前,普通app发起的进程,是无法获取root用户权限的。
可以使用如下方法:
- 利用了一个拥有root权限的进程的漏洞来提权,例如栈溢出漏洞
- 修改ROM并刷机
suid机制失效了怎么办?
从Android 4.3开始,很多分区(例如/system 和 /data)在一开始挂载时就被设置为nosuid
所以一个可行的方案是,在设备启动时由init进程开启一个su daemon 守护进程,当有程序调用su时,就作为client与这个server通信,由远程的server完成所有操作。由于init进程具有天然的root权限,由它 fork 出的su daemon自然也是root身份,这就实现了提权。具体实现参考:https://www.jianshu.com/p/6bc251ee9026
参考:
链接:https://blog.youkuaiyun.com/weixin_46818279/article/details/106907658
链接:https://blog.youkuaiyun.com/qq_39441603/article/details/124996277