*本文作者:nancce,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。
前言
lynis是一款采用shell脚本编写,适用于Linux,macOS和基于UNIX的系统的安全审核工具,相信小伙伴们在网上搜索lynis会有很多介绍lynis如何使用的文章,但是关于lynis插件编写的文章却很少,本文就来讨论下lynis的插件编写。
lynis的使用也很是简单方便,一般来说我们使用以下的命令就可以了:
cd lynis; ./lynis audit system
除了使用lynis默认的规则来加固linux之外,lynis还提供了插件的功能,通过自己编写插件,我们可以把lynis改装成一款linux提权信息的收集器,或者将其改装成恶意程序的检测工具,本文我们将先介绍编写lynis插件的语法,然后基于lynis编写一个linux提权信息收集插件,希望通过这个案例可以启发小伙伴们开发出更精彩的插件。
插件介绍
脚本语言
Lynis采用shell脚本编写的,这是一种多功能脚本语言,可在在Linux或基于UNIX的操作系统的所有系统上运行。
Lynis执行步骤
我们先看看lynis完整的运行周期如下:
[Initialization] → [OS detection] → [Detection of binaries] → [Plugins phase 1] → [Built-in tests] → [Custom tests] → [Plugins phase 2] → [Report]
可以发现,在lynis中有两个阶段可以去运行插件,它们分别是Plugins phase 1,Plugins phase 2。
lynis SDK
lynis同时也提供lynis SDK的开发方式及下载。
开发步骤
第一步:找到插件目录
第一步是就了解Lynis的安装位置,特别是您的插件存储的目录。当运行Lynis时,插件目录就会显示在屏幕上,并存储在日志文件中(通常为/var/log/lynis.log),所以我们可以通过下面的命令来查看插件的目录文章:
grep -i plugin /var/log/lynis.log
第二步:决定加载插件的阶段
第一阶段
在第一阶段运行的插件将在内置测试之前执行。此阶段非常适合仅收集数据的测试。这有可能是获取已安装的软件包列表或系统上运行的进程。
第二阶段
插件的第二阶段发生在正常测试结束时。这是你希望分析先前发现的信息,处理它并可选择在屏幕上显示的时候。
提示:如果您是第一次创建插件,请使用第二阶段。这样你就可以使所有测试数据可用的同时向屏幕输出,因为它在审计周期最后阶段执行。
第三步:创建插件文件
在plugins目录,将customplugin.template文件复制到plugin [name] _phase [number]文件中。
Plugin Name and Phase
[name]的值应替换为插件的唯一名称。仅使用小写字符,数字和(可选)短划线( - )来链接两个单词。 [number]用以定义插件阶段,即1或2。
例如name的取值可为:companyname, custom-tests, iso27001, personal
命令:
cp custom_plugin.template plugin_custom-tests_phase2
第四步:配置插件
修改文件标题
对此文件的第一次调整就是修改标题。这些细节用于插件的部分说明,用于验证插件的正确格式。
#########################################################################
#
# * DO NOT REMOVE *
#-----------------------------------------------------
# PLUGIN_AUTHOR=Mr Auditor <auditor@company.example.org>
# PLUGIN_CATEGORY=Custom
# PLUGIN_DESC=This are my custom tests
# PLUGIN_NAME=custom-tests
# PLUGIN_REQUIRED_TESTS=
#-----------------------------------------------------
#########################################################################
PLUGIN_AUTHOR: 作者字段定义谁负责创建和更新此文件。
PLUGIN_CATEGORY: 这个字段描述了此插件所属的测试类型(例如,network)。可以使用“custom”。
PLUGIN_DESC: 描述字段,带有可选的解释,说明为什么创建此插件及其目标。
PLUGIN_NAME: 应该和你用作文件名的名称相同。
PLUGIN_REQUIRED_TESTS: 描述在执行该插件之前应该已经执行的依赖检测。通常可以跳过。
Tips:
检查是否正确定义了PLUGIN_NAME字段。
保留#符号,只更改每行的值。
第五步:创建插件
每个插件都包含一个或多个单独的检测单元。它们标有唯一的ID,以便正确的记录和存储测试结果。出于同样的原因,您创建的所有测试都应以“CUST-”开头,然后是四个数字(例如CUST-0010)。
检测单元使用通用的shell脚本语言创建。为了简化操作,lynis预先实现了几个函数(include / functions)。示例包括向屏幕,日志文件或报告文件添加文本。它还包括用于检查权限的检测等。
基础的函数:
Display: 向屏幕输出、打印
LogText: 向log文件中输出
Register: 测试单元队列执行
Report: 向report中输出
检测流程:
设置可选的先决条件;
注册检测(注册功能)。然后Lynis将检查是否需要执行或跳过;
检查注册功能的状态(if [ ${SKIPTEST} -eq 0 ]; then);
运行检测代码5、关闭检测(fi)。
可以在include/tests_custom.template文件中看到测试和函数的一些示例。另一个很好的资源是Lynis中的常规检测,也可以在include目录中找到。
第六步:运行lynis
在运行lynis之后,插件会被载入。
常见问题
如果,插件没有被激活,请按检查以下的项目:
在profile(.prf)中是否被开启;
插件名和插件代码中是否一致;
插件文件是否有权限。
日志文件(/var/log/lynis.log)中也将记录忽略该插件的原因。
linux提权信息收集插件
如何运行插件
虽然在上面的章节中我们介绍了在运行lynis后,插件会被载入,但是这样的话会在运行我们的插件的同时运行lynis自带的插件和很多其他的信息收集脚本,所以lynis提供了一种单独运行插件的选项--tests。
./lynis audit system --tests CUST-id1,CUST-id2
这里的id就是编写插件时自定义的独一无二的值
确认收集什么信息
目前该插件已经完成收集的信息有:
判断/etc/passwd中是否有hash的password,如果有提取出来后续可以使用hashcat等破解(插件id:PASS-0001,plugin_check_passwd_shadow_phase2);
判断/etc/shadow文件root之外的用户是否有权限读取,如果有提取出来后续可以使用hashcat等破解(插件id:PASS-0002,plugin_check_passwd_shadow_phase2);
判断/etc/passwd文件root之外的用户是否有写入的权限,可以直接修改密码(插件id:PASS-0003,plugin_check_passwd_shadow_phase2);
检测当前用户可以执行并且可被用以提权的程序(插件id:PWN-0001,plugin_check_pwnable_binary_phase2);
发现隐藏文件(插件id:HIDDEN-0001,plugin_check_hidden_file_phase2);
发现suid文件,经典提权(插件id:SUID-0001,plugin_check_suid_file_phase2);
检测是否有docker用户组并列出其中的用户(插件id:GROUP-0001,plugin_check_docker_group_phase2)。
目前收集的信息还是比较少的,后续会逐步的添加。
实战Lin.security
首先,简单介绍下插件的内容,定义检查/etc/passwd文件中是否有密文的函数:
check_password_hashes() {
# define some constant
IFS_OLD=$IFS
IFS=$'\n'
ENCRYPTEDPASS='x'
#start
LogText "Test:obtain /etc/passwd file content"
passwd_content=$(${AWKBINARY} '{print $0}' /etc/passwd)
for each_passwd in ${passwd_content};
do
IFS=':'
passwd_split_colon=($each_passwd)
if [ "${passwd_split_colon[1]}" != "$ENCRYPTEDPASS" ];then
IFS=$'\n'
Report "sensetive_passwd_hashes[]=$each_passwd"
fi
done
IFS=$IFS_OLD
}
然后在注册代码块里调用该函数在插件加载时运行:
#################################################################################
#
# Test : PASS-0001
# Description : We show some lines on the screen
# Register our first custom test
# We consider it to be a lightweight test (no heavy IO, or long searches), no network connection needed
Register --test-no PASS-0001 --weight L --network NO --description "A test for check if there is password hashess stored in /etc/passwd"
if [ ${SKIPTEST} -eq 0 ]; then
# The Display function makes it easy to show something on screen, with colors.
# --indent defines amount of spaces
# --text text to be displayed on screen
# --result text at end of line
# --color color of result text
Display --indent 2 --text "- Checking if there is a password hashes on /etc/passwd file" --result OK --color GREEN
check_password_hashes;
fi
#
运行命令:
./lynis audit system --tests PASS-0001
查看收集结果的报告:
cat /var/log/lynis-report.dat | grep sensetive_passwd_hashes
Lin.security是一款提权练习的虚拟机,通过模拟真实漏洞,将帮助您完善本地权限提升技能,技术和工具集。
通过bob/secret登入到Lin.security中,ifconfig查看虚拟机的ip:
bob@linsecurity:~/lynis-master/plugins$ ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:8a:eb:21:07 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.137.196 netmask 255.255.255.0 broadcast 192.168.137.255
inet6 fe80::a00:27ff:fed8:9fd6 prefixlen 64 scopeid 0x20<link>
ether 08:00:27:d8:9f:d6 txqueuelen 1000 (Ethernet)
RX packets 180 bytes 18259 (18.2 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 753 bytes 69400 (69.4 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 373 bytes 27495 (27.4 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 373 bytes 27495 (27.4 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
将插件拷贝进虚拟机plugins目录下:
scp plugin_check_* bob@192.168.137.196:/home/bob/lynis-master/plugins/
bob@192.168.137.196's password:
plugin_check_docker_group_phase2 100% 3858 899.2KB/s 00:00
plugin_check_hidden_file_phase2 100% 3628 3.7MB/s 00:00
plugin_check_passwd_shadow_phase2 100% 5010 6.4MB/s 00:00
plugin_check_pwnable_binary_phase2 100% 4970 6.5MB/s 00:00
plugin_check_suid_file_phase2 100% 3578 3.7MB/s 00:00
记得将default.prf也拷贝进去,覆盖原来的配置:
scp default.prf bob@192.168.137.196:/home/bob/lynis-master/
bob@192.168.137.196's password:
default.prf 100% 20KB 6.7MB/s 00:00
运行lynis:
bob@linsecurity:~/lynis-master$ bash lynis audit system
查看插件收集到的信息:
cat /tmp/lynis-report.dat | grep -E 'suid_file|pwnable_programs|sensetive_passwd_hashes|hidden_file|docker_group'
plugin_enabled_phase2[]=check_hidden_file||
hidden_file[]=/snap/core/4917/etc/.pwd.lock
hidden_file[]=/snap/core/4917/etc/cron.d/.placeholder
hidden_file[]=/snap/core/4917/etc/cron.daily/.placeholder
hidden_file[]=/snap/core/4917/etc/cron.hourly/.placeholder
hidden_file[]=/snap/core/4917/etc/cron.monthly/.placeholder
hidden_file[]=/snap/core/4917/etc/cron.weekly/.placeholder
hidden_file[]=/snap/core/4917/etc/init.d/.depend.boot
hidden_file[]=/snap/core/4917/etc/init.d/.depend.start
hidden_file[]=/snap/core/4917/etc/init.d/.depend.stop
hidden_file[]=/snap/core/4917/etc/skel/.bash_logout
hidden_file[]=/snap/core/4917/etc/skel/.bashrc
hidden_file[]=/snap/core/4917/etc/skel/.profile
hidden_file[]=/snap/core/4917/var/lib/apparmor/profiles/.apparmor.md5sums
hidden_file[]=/snap/core/4486/etc/.pwd.lock
hidden_file[]=/snap/core/4486/etc/cron.d/.placeholder
hidden_file[]=/snap/core/4486/etc/cron.daily/.placeholder
hidden_file[]=/snap/core/4486/etc/cron.hourly/.placeholder
hidden_file[]=/snap/core/4486/etc/cron.monthly/.placeholder
hidden_file[]=/snap/core/4486/etc/cron.weekly/.placeholder
hidden_file[]=/snap/core/4486/etc/init.d/.depend.boot
hidden_file[]=/snap/core/4486/etc/init.d/.depend.start
hidden_file[]=/snap/core/4486/etc/init.d/.depend.stop
hidden_file[]=/snap/core/4486/etc/skel/.bash_logout
hidden_file[]=/snap/core/4486/etc/skel/.bashrc
hidden_file[]=/snap/core/4486/etc/skel/.profile
hidden_file[]=/snap/core/4486/var/lib/apparmor/profiles/.apparmor.md5sums
hidden_file[]=/snap/core/5328/etc/.pwd.lock
hidden_file[]=/snap/core/5328/etc/cron.d/.placeholder
...
我们可以聚焦于最简便的pwnable_program部分:
pwnable_programs[]=/bin/ash (For more details, visit:https://gtfobins.github.io/gtfobins/ash)
pwnable_programs[]=/usr/bin/awk (For more details, visit:https://gtfobins.github.io/gtfobins/awk)
pwnable_programs[]=/bin/bash (For more details, visit:https://gtfobins.github.io/gtfobins/bash)
pwnable_programs[]=/bin/csh (For more details, visit:https://gtfobins.github.io/gtfobins/csh)
pwnable_programs[]=/usr/bin/curl (For more details, visit:https://gtfobins.github.io/gtfobins/curl)
pwnable_programs[]=/bin/dash (For more details, visit:https://gtfobins.github.io/gtfobins/dash)
pwnable_programs[]=/bin/ed (For more details, visit:https://gtfobins.github.io/gtfobins/ed)
pwnable_programs[]=/usr/bin/env (For more details, visit:https://gtfobins.github.io/gtfobins/env)
pwnable_programs[]=/usr/bin/expect (For more details, visit:https://gtfobins.github.io/gtfobins/expect)
pwnable_programs[]=/usr/bin/find (For more details, visit:https://gtfobins.github.io/gtfobins/find)
pwnable_programs[]=/usr/bin/ftp (For more details, visit:https://gtfobins.github.io/gtfobins/ftp)
pwnable_programs[]=/usr/bin/less (For more details, visit:https://gtfobins.github.io/gtfobins/less)
pwnable_programs[]=/usr/bin/man (For more details, visit:https://gtfobins.github.io/gtfobins/man)
pwnable_programs[]=/bin/more (For more details, visit:https://gtfobins.github.io/gtfobins/more)
pwnable_programs[]=/usr/bin/scp (For more details, visit:https://gtfobins.github.io/gtfobins/scp)
pwnable_programs[]=/usr/bin/socat (For more details, visit:https://gtfobins.github.io/gtfobins/socat)
pwnable_programs[]=/usr/bin/ssh (For more details, visit:https://gtfobins.github.io/gtfobins/ssh)
pwnable_programs[]=/usr/bin/vi (For more details, visit:https://gtfobins.github.io/gtfobins/vi)
pwnable_programs[]=/usr/bin/zsh (For more details, visit:https://gtfobins.github.io/gtfobins/zsh)
pwnable_programs[]=/usr/bin/pico (For more details, visit:https://gtfobins.github.io/gtfobins/pico)
pwnable_programs[]=/usr/bin/perl (For more details, visit:https://gtfobins.github.io/gtfobins/perl)
pwnable_programs[]=/usr/bin/tclsh (For more details, visit:https://gtfobins.github.io/gtfobins/tclsh)
pwnable_programs[]=/usr/bin/git (For more details, visit:https://gtfobins.github.io/gtfobins/git)
pwnable_programs[]=/usr/bin/scp (For more details, visit:https://gtfobins.github.io/gtfobins/scp)
访问链接https://gtfobins.github.io/gtfobins/awk/,用以下命令提权:
bob@linsecurity:~/lynis-master$ sudo awk 'BEGIN {system("/bin/sh")}'
# id
uid=0(root) gid=0(root) groups=0(root)
当然,可以使用插件发现的其他信息进行提权
结语
本文总结了一下lynis的插件编写的过程,借助lynis我们可以开发出各种用途的lynis插件包括但不局限于攻击溯源,提权信息收集等等,希望可以给小伙伴以启发开发出更多提高工作效率的插件
完整的代码:https://github.com/nancheal/lynis-plugins,欢迎点star~
*本文作者:nancce,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。