作者:wzt
原文鏈接:https://mp.weixin.qq.com/s/G6kE52o7OZaGYPqnuUwggQ

1 簡介

以linux kernel 5.6.7內核代碼為例,闡述內核的audit子系統是如何實現的,以及它的一些設計缺陷和繞過方法。

2 架構

2.1 總體架構

Linux audit系統分為用戶層和內核層兩部分,用戶層通過auditctl工具生成rule規則,發送給auditd守護進程,后者通過netlink協議與內核進行交互,包括規則下發等功能。

內核在啟動階段初始化audit子系統時,創建一個netlink socket,啟動audit_receive內核線程處理來自用戶層的請求,包括解析rule規則,使能audit開關等工作。

內核維護一個日志隊列,通過kaudit_thread內核線程來操作,當隊列長度大于audit_backlog_limit時進行休眠,否則從隊列里取下一個節點進行日志格式化處理,然后寫入到printk子系統里。

對于rule規則的審計有以下幾個入口點,后面章節會詳細介紹:

  • system call系統調用階段
  • fork進程時
  • 文件系統狀態改變

這幾個入口點都可以觸發對rule規則的檢查,然后寫入內核日志隊列,最后通過上述的kaudit_thread內核線程處理。

2.2 內核與用戶通訊接口

2.2.1 協議接口

內核使用數字9代表audit子系統使用的netlink協議:

include/uapi/linux/netlink.h
#define NETLINK_AUDIT           9       /* auditing */

kernel/audit.c
static int __net_init audit_net_init(struct net *net)
{
        struct netlink_kernel_cfg cfg = {
                .input  = audit_receive,
                .bind   = audit_bind,
                .flags  = NL_CFG_F_NONROOT_RECV,
                .groups = AUDIT_NLGRP_MAX,
        };
...
        aunet->sk = netlink_kernel_create(net, NETLINK_AUDIT, &cfg);
...
}

Audit_receive函數處理接受來自用戶層的數據協議:

audit_receive()->audit_receive_msg:
static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
...
        switch (msg_type) {
        case AUDIT_GET:
case AUDIT_SET:
case AUDIT_GET_FEATURE:
case AUDIT_SET_FEATURE:
        case AUDIT_USER:
        case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:
        case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
        case AUDIT_ADD_RULE:
        case AUDIT_DEL_RULE:
case AUDIT_LIST_RULES:
case AUDIT_TRIM:
case AUDIT_MAKE_EQUIV:
case AUDIT_SIGNAL_INFO:
case AUDIT_TTY_GET:
case AUDIT_TTY_SET:
...
}

Nlh為用戶態auditd進程使用netlink協議包裝的數據,其中msg_type代表auditd能使用的功能里列表:

  • AUDIT_GET
    獲取audit子系統的狀態信息,使用auditctl -s進行獲取。

  • AUDIT_SET
    設置audit子系統狀態,包括audit功能是否開啟,注冊和銷毀來自用戶態auditd進程的請求。Linux audit子系統每次只允許一個auditd進程注冊,并且auditd進程只能來自init_namespace, 也就是說來自docker等其他容器的進程是不扮演auditd進程與內核通訊的。

  • AUDIT_GET_FEATURE
    AUDIT_SET_FEATURE
    獲取和設置功能列表

  • AUDIT_USER
    AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG
    AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2
    處理來自用戶態自定義的audit日志內容

  • AUDIT_ADD_RULE
    AUDIT_DEL_RULE
    AUDIT_LIST_RULES
    獲取和設置rule規則

  • AUDIT_TRIM
    銷毀之前被監控的目錄樹

  • AUDIT_MAKE_EQUIV
    附加到之前被監控的目錄樹

  • AUDIT_SIGNAL_INFO
    獲取發送給auditd信號的進程信息

  • AUDIT_TTY_GET
    AUDIT_TTY_SET
    獲取和設置tty監控信息,這是個神奇的功能,后面會有詳細分析。

2.2.2 規則添加

下面以添加一條規則為例,看下audit子系統是如何在內核操作的。

kernel/auditfilter.c:
int audit_rule_change(int type, int seq, void *data, size_t datasz)
{
        switch (type) {
        case AUDIT_ADD_RULE:
                entry = audit_data_to_entry(data, datasz);
                if (IS_ERR(entry))
                        return PTR_ERR(entry);
                err = audit_add_rule(entry);
}

audit_data_to_entry用來將用戶態的規則轉為內核態的規則,來自用戶態的規則數據結構體為:

include/uapi/linux/audit.h:
struct audit_rule_data {
        __u32           flags;  /* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */
        __u32           action; /* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */
        __u32           field_count;
        __u32           mask[AUDIT_BITMASK_SIZE]; /* syscall(s) affected */
        __u32           fields[AUDIT_MAX_FIELDS];
        __u32           values[AUDIT_MAX_FIELDS];
        __u32           fieldflags[AUDIT_MAX_FIELDS];
        __u32           buflen; /* total length of string fields */
        char            buf[0]; /* string fields buffer */
};

這個結構其實就是由auditctl的參數負責填充的,mask數組就是audit -S 指定要過濾的系統調用表,fields數組為audit -F 指定的操作表,格式為type op value。

內核態的規則結構體為:

struct audit_krule {
        u32                     pflags;
        u32                     flags;
        u32                     listnr;
        u32                     action;
        u32                     mask[AUDIT_BITMASK_SIZE];
        u32                     buflen; /* for data alloc on list rules */
        u32                     field_count;
        char                    *filterkey; /* ties events to rules */
        struct audit_field      *fields;
        struct audit_field      *arch_f; /* quick access to arch field */
        struct audit_field      *inode_f; /* quick access to an inode field */
        struct audit_watch      *watch; /* associated watch */
        struct audit_tree       *tree;  /* associated watched tree */
        struct audit_fsnotify_mark      *exe;
        struct list_head        rlist;  /* entry in audit_{watch,tree}.rules list */
        struct list_head        list;   /* for AUDIT_LIST* purposes only */
        u64                     prio;
};

field在內核中的結構體為:

struct audit_field {
        u32                             type;
        union {
                u32                     val;
                kuid_t                  uid;
                kgid_t                  gid;
                struct {
                        char            *lsm_str;
                        void            *lsm_rule;
                };
        };
        u32                             op;
};

Type的類型可以為:

include/uapi/linux/audit.h:

#define AUDIT_PID       0
#define AUDIT_UID       1
#define AUDIT_EUID      2
#define AUDIT_SUID      3
#define AUDIT_FSUID     4
#define AUDIT_GID       5
#define AUDIT_EGID      6
#define AUDIT_SGID      7
#define AUDIT_FSGID     8
#define AUDIT_LOGINUID  9
#define AUDIT_PERS      10
#define AUDIT_ARCH      11
#define AUDIT_MSGTYPE   12
#define AUDIT_SUBJ_USER 13      /* security label user */
#define AUDIT_SUBJ_ROLE 14      /* security label role */
#define AUDIT_SUBJ_TYPE 15      /* security label type */
#define AUDIT_SUBJ_SEN  16      /* security label sensitivity label */
#define AUDIT_SUBJ_CLR  17      /* security label clearance label */
#define AUDIT_PPID      18
#define AUDIT_OBJ_USER  19
#define AUDIT_OBJ_ROLE  20
#define AUDIT_OBJ_TYPE  21
#define AUDIT_OBJ_LEV_LOW       22
#define AUDIT_OBJ_LEV_HIGH      23
#define AUDIT_LOGINUID_SET      24
#define AUDIT_SESSIONID 25      /* Session ID */
#define AUDIT_FSTYPE    26      /* FileSystem Type */
#define AUDIT_DEVMAJOR  100
#define AUDIT_DEVMINOR  101
#define AUDIT_INODE     102
#define AUDIT_EXIT      103
#define AUDIT_SUCCESS   104     /* exit >= 0; value ignored */
#define AUDIT_WATCH     105
#define AUDIT_PERM      106
#define AUDIT_DIR       107
#define AUDIT_FILETYPE  108
#define AUDIT_OBJ_UID   109
#define AUDIT_OBJ_GID   110
#define AUDIT_FIELD_COMPARE     111
#define AUDIT_EXE       112
#define AUDIT_SADDR_FAM 113

#define AUDIT_ARG0      200
#define AUDIT_ARG1      (AUDIT_ARG0+1)
#define AUDIT_ARG2      (AUDIT_ARG0+2)
#define AUDIT_ARG3      (AUDIT_ARG0+3)

#define AUDIT_FILTERKEY 210

Op的操作可以為:

/* These are the supported operators.
 *      4  2  1  8
 *      =  >  <  ?
 *      ----------
 *      0  0  0  0      00      nonsense
 *      0  0  0  1      08      &  bit mask
 *      0  0  1  0      10      <
 *      0  1  0  0      20      >
 *      0  1  1  0      30      !=
 *      1  0  0  0      40      =
 *      1  0  0  1      48      &=  bit test
 *      1  0  1  0      50      <=
 *      1  1  0  0      60      >=
 *      1  1  1  1      78      all operators
 */
#define AUDIT_BIT_MASK                  0x08000000
#define AUDIT_LESS_THAN                 0x10000000
#define AUDIT_GREATER_THAN              0x20000000
#define AUDIT_NOT_EQUAL                 0x30000000
#define AUDIT_EQUAL                     0x40000000
#define AUDIT_BIT_TEST                  (AUDIT_BIT_MASK|AUDIT_EQUAL)
#define AUDIT_LESS_THAN_OR_EQUAL        (AUDIT_LESS_THAN|AUDIT_EQUAL)
#define AUDIT_GREATER_THAN_OR_EQUAL     (AUDIT_GREATER_THAN|AUDIT_EQUAL)
#define AUDIT_OPERATORS                 (AUDIT_EQUAL|AUDIT_NOT_EQUAL|AUDIT_BIT_MASK)

就是說audit子系統可以對以上的type類型做一些簡單的邏輯操作, 這對于審計作用已經足夠了。比如它可以讓audit子系統只針對一個uid或一個group組進行審計;可以對系統調用的前四個參數進行審計, 但是目前只支持整型的參數,指針或數據結構則沒有支持,因為這要設計更復雜的數據結構, 指針可以包含更多級指針, 數據結構可以嵌套數據結構,數據結構又可以嵌套指針等等。雖然目前功能有限,但這個filed結構體的操作可以說是linux audit子系統的一個重大改進了。

audit_data_to_entry->audit_to_entry_common解析用戶態的struct audit_rule_data數據為struct audit_krule:

struct audit_krule {
        u32                     pflags;
        u32                     flags;
        u32                     listnr;
        u32                     action;
        u32                     mask[AUDIT_BITMASK_SIZE];
        u32                     buflen; /* for data alloc on list rules */
        u32                     field_count;
        char                    *filterkey; /* ties events to rules */
        struct audit_field      *fields;
        struct audit_field      *arch_f; /* quick access to arch field */
        struct audit_field      *inode_f; /* quick access to an inode field */
        struct audit_watch      *watch; /* associated watch */
        struct audit_tree       *tree;  /* associated watched tree */
        struct audit_fsnotify_mark      *exe;
        struct list_head        rlist;  /* entry in audit_{watch,tree}.rules list */
        struct list_head        list;   /* for AUDIT_LIST* purposes only */
        u64                     prio;
};

audit_rule_change->audit_add_rule將新生成的krule掛接到對應的規則鏈表里:

static inline int audit_add_rule(struct audit_entry *entry)
{
e = audit_find_rule(entry, &list);[1]
        if (watch) {[2]
                err = audit_add_watch(&entry->rule, &list);
        if (tree) {
                err = audit_add_tree_rule(&entry->rule);[3]
        if (entry->rule.flags & AUDIT_FILTER_PREPEND) {[4]
                list_add(&entry->rule.list,[5]
                         &audit_rules_list[entry->rule.listnr]);
                list_add_rcu(&entry->list, list);
                entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
        } else {
                list_add_tail(&entry->rule.list,
                              &audit_rules_list[entry->rule.listnr]);
                list_add_tail_rcu(&entry->list, list);
        }
}

內核維護以下幾個關于規則的鏈表:

struct list_head audit_filter_list[AUDIT_NR_FILTERS];

static struct list_head audit_rules_list[AUDIT_NR_FILTERS];

audit_filter_list數組保存的都是鏈表頭,它們對應于不同過濾類型的鏈表,如之前所述的task、exit、user、exclude等審計入口用到的規則,在審計規則時用到。

audit_rules_list數組鏈表保存的是全部規則節點,僅在auditd -l獲取所有規則列表時使用。

struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];

現在的aduit子系統不僅可以對系統調用號進行審計,還可以對文件進行監控,[2]處的操作就是解析auditctl -w指定要監控的文件路徑。audit_inode_hash為一個針對inode過濾的哈希表,前面講過struct filed結構體可以對inode進行審計,同時audit_add_watch也會對文件轉化為inode, 插入到audit_inode_hash哈希鏈表中。

這些數據結構之間的關系圖為:

2.3 內核審計入口

Audit子系統的審計入口最初來自于系統調用的入口,后面在支持了對文件監控的功能后,審計入口還被加入到了文件系統某些接口里。同時在進程創建的時候也會對其進行審計。所以在linux的man手冊中,auditctl有如下入口列表:

Task - 進程創建時進行審計
Exit - 系統調用結束時進行審計
User - 來自用戶態自定義的內核日志
Exclude - 在日志記錄前進行審計

2.3.1 系統調用入口

以x86架構為例:

arch/x86/entry/common.c:
entry_SYSCALL_64()->do_syscall_64()
__visible void do_syscall_64(unsigned long nr, struct pt_regs *regs)
{
        if (READ_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY)
                nr = syscall_trace_enter(regs);[1]
                regs->ax = sys_call_table[nr](regs);[2]
syscall_return_slowpath(regs);[3]
}

在[1]出判斷當前線程標志_TIF_WORK_SYSCALL_ENTRY是否被置位,如果是則調用 syscall_trace_enter進行系統調用審計前的準備。它則調用do_audit_syscall_entry()以及 __audit_syscall_entry進一步初始化:

kernel/auditsc.c:
void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2,
                           unsigned long a3, unsigned long a4)
{
        context->argv[0]    = a1;
        context->argv[1]    = a2;
        context->argv[2]    = a3;
        context->argv[3]    = a4;
}

注意每個進程的audit context結構是在進程fork時創建的,在這個路徑里,只保存系統調用的前4個參數的內容。這個操作比較讓筆者費解,為什么只記錄前四個參數內容,按照X86的調用約定并且在系統調用處理程序開始時前六個參數無論是否存在,都已經保存在內核棧里了。如果參數是指針的話,那么保存的就是指針的值,在后面的日志輸出時會打印出來,這里保存的是用戶態的指針,不會泄露內核指針,所以不會有安全風險。

我們看到[1]處函數的作用只是保存了系統調用參數和設置context的一些值, 規則判斷和將日志發送到日志隊列里是在[2]處的系統調用完成之后的[3]處處理的。這里充分表達了audit子系統的任務是只做審計之用,不做攔截的操作,攔截的事情可以交給secomp去做,所以audit子系統不能代替secomp做沙箱功能。但是secomp有個弊端,使用的bpf規則不能動態更改,而audit卻可以使用netlink協議實時更新,后續在我的AKSP內核自保護項目中會對audit進行改進,使其具備沙箱的能力。

[3] 處的syscall_slow_exit_work調用audit_syscall_exit做真正的過濾處理操作:

void __audit_syscall_exit(int success, long return_code)
{
                audit_filter_syscall(current, context,
                                     &audit_filter_list[AUDIT_FILTER_EXIT]);
                audit_filter_inodes(current, context);
}

可以看到audit_filter_syscall取的是audit_filter_list數組里AUDIT_FILTER_EXIT為索引的鏈表。

static enum audit_state audit_filter_syscall(struct task_struct *tsk,
                                             struct audit_context *ctx,
                                             struct list_head *list)
{
        struct audit_entry *e;
        enum audit_state state;

        if (auditd_test_task(tsk))
                return AUDIT_DISABLED;

        rcu_read_lock();
        list_for_each_entry_rcu(e, list, list) {
                if (audit_in_mask(&e->rule, ctx->major) &&
                    audit_filter_rules(tsk, &e->rule, ctx, NULL,
                                       &state, false)) {
                        rcu_read_unlock();
                        ctx->current_state = state;
                        return state;
                }
        }
        rcu_read_unlock();
        return AUDIT_BUILD_CONTEXT;
}

auditd_test_task識別當前進程是否為auditd進程,linux audit子系統對auditd進程不做審計的。開始遍歷每個audit_entry節點,audit_in_mask判斷當前系統調用號是否為需要匹配的系統調用號,命中規則后,在繼續調用audit_filter_rules做更進一步的規則匹配:

static int audit_filter_rules(struct task_struct *tsk,
                              struct audit_krule *rule,
                              struct audit_context *ctx,
                              struct audit_names *name,
                              enum audit_state *state,
                              bool task_creation)
{
        for (i = 0; i < rule->field_count; i++) {
                case AUDIT_UID:
                        result = audit_uid_comparator(cred->uid, f->op, f->uid);
                case AUDIT_INODE:
                        if (name)
                                result = audit_comparator(name->ino, f->op, f->val);
                        else if (ctx) {
                                list_for_each_entry(n, &ctx->names_list, list) {
                                        if (audit_comparator(n->ino, f->op, f->val)) {
                                                ++result;
                                                break;
                                        }
                                }
                        }
                case AUDIT_WATCH:
                        if (name) {
                                result = audit_watch_compare(rule->watch,
                                                             name->ino,
                                                             name->dev);
}

循環遍歷rule的每個filed結構體,對其進行比較操作,比如對文件監控時,調用 audit_watch_compare進行比對:

int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev)
{
        return (watch->ino != AUDIT_INO_UNSET) &&
                (watch->ino == ino) &&
                (watch->dev == dev);
}

注意對于inode的監控,audit子系統可以通過auditctl -F 單獨對用戶指定的一個inode節點做監控,也可以使用audit_filter_inodes做監控。

void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx)
{
struct audit_names *n;

        list_for_each_entry(n, &ctx->names_list, list) {
                if (audit_filter_inode_name(tsk, n, ctx))
                        break;
        }
}

循環遍歷一個struct audit_names類型的結構體鏈表。

struct audit_names {
        struct list_head        list;           /* audit_context->names_list */

        struct filename         *name;
        int                     name_len;       /* number of chars to log */
        bool                    hidden;         /* don't log this record */

        unsigned long           ino;
        dev_t                   dev;
        umode_t                 mode;
        kuid_t                  uid;
        kgid_t                  gid;
        dev_t                   rdev;
        u32                     osid;
        struct audit_cap_data   fcap;
        unsigned int            fcap_ver;
        unsigned char           type;           /* record type */
        /*
         * This was an allocated audit_names and not from the array of
         * names allocated in the task audit context.  Thus this name
         * should be freed on syscall exit.
         */
        bool                    should_free;
};

這個結構體的創建不是來自用戶態,而是通過預埋的文件系統接口動態生成的。文件系統里很多對inode的操作會調用audit_inode和audit_inode_child來進行審計。

void __audit_inode(struct filename *name, const struct dentry *dentry,
                   unsigned int flags)
{
        list_for_each_entry_rcu(e, list, list) {[1]
                for (i = 0; i < e->rule.field_count; i++) {
                        struct audit_field *f = &e->rule.fields[i];

                        if (f->type == AUDIT_FSTYPE
                            && audit_comparator(inode->i_sb->s_magic,
                                                f->op, f->val)
                            && e->rule.action == AUDIT_NEVER) {
                                rcu_read_unlock();
                                return;
                        }
                }
        }

        n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN);[2]
}
static struct audit_names *audit_alloc_name(struct audit_context *context,
                                                unsigned char type)
{
        list_add_tail(&aname->list, &context->names_list);
}

首先在[1]處遍歷匹配現有的規則, 對于新生成的inode節點通過[2]分配 Struct audit_name結構并掛接到context->names_list鏈表中, 這就是前面看到對inode審計的數據來源。

2.3.2 進程建立入口

在fork進程時,copy_process()->audit_alloc調用audit_filter_task進行一次規則過濾操作:

kernel/auditsc.c:
static enum audit_state audit_filter_task(struct task_struct *tsk, char **key)
{
        list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
                if (audit_filter_rules(tsk, &e->rule, NULL, NULL,
                                       &state, true)) {
                        if (state == AUDIT_RECORD_CONTEXT)
                                *key = kstrdup(e->rule.filterkey, GFP_ATOMIC);
                        rcu_read_unlock();
                        return state;
                }
        }
}

可以看到內核正是從audit_filter_list數組選取的AUDIT_FILTER_TASK為索引的鏈表做過濾。

2.3.3 文件系統入口

在前面講inode規則過濾時,講到audit子系統在文件系統很多接口中預埋了audit hook接口,這些包括audit_inode和audit_inode_child。因為對于文件的監控,audit子系統目前不支持系統調用參數的解析,所以只能通過文件系統接口形式進行狀態獲取。

2.3.4 用戶自定義入口

Audit子系統允許從用戶態傳遞一個字符串,輸出在內核日志中:

[root@localhost linux-5.6.7]# auditctl -m "wzt"
You have mail in /var/spool/mail/root
[root@localhost linux-5.6.7]# grep 'wzt' /var/log/audit/audit.log
type=USER msg=audit(1608786569.953:85791): pid=67638 uid=0 auid=0 ses=291 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='wzt exe="/usr/sbin/auditctl" hostname=? addr=? terminal=pts/17 res=success'

筆者認為這個功能沒有多大用處, 反而會帶來一些潛在的安全問題,惡意軟件進程可以殺死auditd進程,重新與netlink進行連接,這樣可以向內核日志寫入垃圾數據。

3 缺陷

3.1 設計缺陷

通過對audit子系統的全面分析,筆者發現了它的一些設計缺陷。

3.1.1 系統調用參數過濾不完整

只能記錄系統調用的前四個參數內容,且只能為整型,不支持指針或結構體及其之間的嵌套定義。 不支持具體某個參數的過濾,比如open系統調用,只想過濾第2個參數是否為/etc/passwd,目前的audit子系統是做不到的。

3.1.2 系統調用過濾丟失

Audit在完成過濾操作后,會把日志發送到一個隊列中,在這之前還有一個日志產生頻率的檢查:

struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
                                     int type)
{
          if (audit_rate_check() && printk_ratelimit())
                 pr_warn("audit_backlog=%d > audit_backlog_limit=%d\n",
                          skb_queue_len(&audit_queue),
                          audit_backlog_limit);
          audit_log_lost("backlog limit exceeded");
}

audit_rate_check判斷一秒內日志生成的數量是否大于audit_rate_limit, 如果大于這條日志就直接被忽略掉了,這樣惡意軟件可以使用一些tricky的手段繞過對關鍵系統調用的審計。

3.1.3 tty passwd審計

筆者認為這是一個奇葩的功能,auditd可以向內核要求記錄tty關閉回顯的信息,這樣passwd的操作內容就會被記錄下來,這難道不是一個后門功能嗎?

static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{

        case AUDIT_TTY_SET: {
                        t = s.enabled | (-s.log_passwd & AUDIT_TTY_LOG_PASSWD);
                        t = xchg(¤t->signal->audit_tty, t);
                }
}

當current->signal->audit_tty被置位后,tty_audit_add_data()將會關閉回顯的信息推送給audit子系統。

void tty_audit_add_data(struct tty_struct *tty, const void *data, size_t size)
{
        if ((~audit_tty & AUDIT_TTY_LOG_PASSWD) && icanon && !L_ECHO(tty))
                return;
        do {
                if (buf->valid == N_TTY_BUF_SIZE)
                        tty_audit_buf_push(buf);
        } while (size != 0);
}

下面我們來驗證下這個可怕的功能:

修改/etc/pam.d/password-auth和/etc/pam.d/system-auth文件,加入:

session     required      pam_tty_audit.so disable=* enable=root log_passwd

新開啟一個終端,然后輸入以下命令:

[root@localhost ~]# passwd test
Changing password for user test.
New password:
[root@localhost ~]# aureport --tty
TTY Report
===============================================
# date time event auid term sess comm data
===============================================
5. 12/23/2020 21:41:42 88650 0 pts21 4405 bash "ls",<ret>
6. 12/23/2020 21:41:45 88659 0 ? 4405 ? "passwd test"
7. 12/23/2020 21:41:43 88654 0 pts21 4405 bash <ret>
8. 12/23/2020 21:41:43 88656 0 pts21 4405 bash <ret>
9. 12/23/2020 21:41:43 88652 0 pts21 4405 bash <ret>
10. 12/23/2020 21:41:45 88658 0 pts21 4405 bash "passwd test",<ret>
11. 12/23/2020 21:41:45 88685 0 pts21 4405 passwd "123456",<nl>
12. 12/23/2020 21:41:47 88687 0 pts21 4405 passwd "123456",<nl>
20. 12/23/2020 21:41:51 88727 0 pts21 4405 bash <ret>
21. 12/23/2020 21:41:56 88730 0 ? 4405 ? "aureport --tty"
22. 12/23/2020 21:41:56 88729 0 pts21 4405 bash "aure",<tab>,"--tty",<ret>

3.2 關閉audit審計功能

對于rootkit或exploit,要想在系統中完美的規避主機HIPS的檢測,需要關閉audit子系統,以下為筆者總結的方法:

A、 Struct audit_context ctx; Ctx->current_state = AUDIT_DISABLED 或者ctx = NULL
B、 Struct audit_krule = rule; rule->prio = 0
C、 Audit_enabled = 0; audit_ever_enabled = 0
D、 Clear_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT)


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1594/