第28章 安全审计与日志:logcat安全审计、kernel日志保护、事件监控
说到安全审计,我脑子里第一个蹦出来的画面,是几年前帮一家厂商做漏洞分析。他们系统被攻破了,但查日志时发现——logcat里干干净净,kernel日志被清过,连事件监控都没触发。说白了,攻击者比你更懂日志在哪儿。
所以这一章,我们来聊聊怎么让日志真正成为你的“眼睛”。不是摆设,不是事后诸葛亮,而是能实时告诉你系统发生了什么。
28.1 logcat安全审计:别让日志变成后门
logcat是Android最常用的日志工具。但很多人不知道,它也是个巨大的信息泄露源。我在项目中遇到过,某款App在logcat里打印了用户的登录token,结果被恶意应用通过READ_LOGS权限抓走了。
28.1.1 logcat的权限模型
Android 4.1之前,任何应用都能读logcat。后来Google意识到问题,加上了READ_LOGS权限,且只授予系统应用或签名应用。但说实话,这还不够。
为什么?因为即使有权限限制,攻击者可以通过adb shell来读——只要设备开启了USB调试。嗯,这里要注意:生产环境的设备,一定要关掉USB调试。
核心原则:logcat里永远不要打印敏感信息。密码、token、密钥、用户数据——这些都不该出现在logcat里。
28.1.2 安全审计的logcat策略
我个人习惯的做法是:
- 分级输出:debug日志只在debug版本输出,release版本只输出warn和error级别
- 动态开关:通过系统属性控制日志输出,比如
persist.log.tag - 日志过滤:使用
logcat -b events只抓事件日志,避免信息泄露
来看一个实际的安全审计脚本:
# 只抓取安全相关的事件日志
adb logcat -b events -v threadtime | grep -E "security|audit|SELinux|permission"
# 或者用logcat的过滤器
adb logcat -s "SecurityAudit:*" "SELinux:*" "Permission:*"
我曾经用这个脚本在测试中抓到了某个系统服务偷偷读取联系人数据的日志。你想想看,如果没有这个审计,那个行为根本不会被发现。
28.2 kernel日志保护:dmesg不是谁都能看的
kernel日志(dmesg)比logcat更敏感。它包含了硬件信息、内核地址、驱动加载细节。攻击者可以利用这些信息来定位内核漏洞。
28.2.1 dmesg_restrict机制
Android从4.4开始引入了dmesg_restrict。这个机制说白了就是:只有root用户才能看kernel日志。
检查方法很简单:
# 查看当前状态
cat /proc/sys/kernel/dmesg_restrict
# 如果返回1,表示已启用
# 如果返回0,表示任何人都能看dmesg
警告:很多厂商的出厂设备,dmesg_restrict默认是0。我见过某款手机,普通App就能通过dmesg读到内核基址,然后利用KASLR绕过漏洞进行攻击。一定要在生产环境中设置为1。
28.2.2 kptr_restrict:隐藏内核指针
除了dmesg_restrict,还有kptr_restrict。它控制的是/proc/kallsyms等文件中的内核符号地址是否可见。
| 值 | 含义 | 安全等级 |
|---|---|---|
| 0 | 所有用户都能看到内核指针 | 低 |
| 1 | 只有root用户能看到 | 中 |
| 2 | 只有root用户能看到,且需要CAP_SYSLOG权限 | 高 |
我建议设置kptr_restrict=2。为什么?因为即使攻击者拿到了root,如果没有CAP_SYSLOG,他依然看不到内核指针。这算是一道额外的防线。
28.3 事件监控:auditd与selinux审计
logcat和dmesg是“被动”的日志。而事件监控是“主动”的——它会在安全事件发生时,立刻通知你。
28.3.1 Android的auditd架构
Android使用Linux内核的audit子系统。当SELinux拒绝访问、权限检查失败、系统调用异常时,auditd会记录事件。
来看一个典型的SELinux审计日志:
type=1400 audit(1234567890.123:456): avc: denied { read } for pid=1234
comm="app_process" name="secret.txt" dev="mmcblk0p25" ino=5678
scontext=u:r:untrusted_app:s0:c512,c768
tcontext=u:object_r:secret_file:s0 tclass=file permissive=0
这条日志告诉我们:一个untrusted_app试图读取secret.txt,被SELinux阻止了。permissive=0表示这是强制模式,不是宽容模式。
小技巧:我习惯用audit2allow工具来分析这些日志。它会自动生成SELinux策略,省去手动写规则的麻烦。
28.3.2 事件监控的实战配置
在Android中,事件监控主要通过logd和auditd配合实现。我个人习惯的做法是:
- 开启SELinux审计:确保
auditd服务正常运行 - 配置审计规则:在
/etc/audit/audit.rules中添加关键路径的监控 - 日志轮转:防止日志占满磁盘
一个简单的审计规则示例:
-w /data/system/packages.xml -p wa -k package_change
-w /data/misc/keystore -p wa -k keystore_access
-w /proc/ -p r -k proc_read
我曾经用这个规则抓到了某个恶意应用频繁读取/proc/self/maps的行为——那是典型的ASLR绕过尝试。
28.4 知识体系总览
下面这张图,是我对本章知识结构的总结。你可以把它当作一个快速索引:
28.5 避坑指南与最佳实践
最后,分享几个我踩过的坑:
我曾经犯过的错:
- 在logcat里打印了AES密钥,结果被测试工具抓取——后来我强制所有日志输出都要经过
Log.d()的封装检查 - 忘记设置dmesg_restrict,导致内核地址泄露——现在我的checklist里第一条就是检查这个值
- 审计日志没有轮转,导致/data分区被写满——系统直接挂掉,教训深刻
嗯,总结一下:安全审计不是事后诸葛亮,而是实时监控。logcat管好应用层,kernel日志管好内核层,事件监控管好行为层。三层配合,才能构建完整的审计体系。
记住一句话:攻击者最怕的不是你的防御有多强,而是你知不知道他来过。
公众号:蓝海资料掘金营,微信deep3321