目录
调研要求:根据zeek中的事件处理尝试获取主机的用户名、主机名、mac地址、操作系统、IP地址等
zeek就是bro了。这里的用户名还不太清楚是什么意思(可能从telnet着手)。
主机名很好理解,在ubuntu系统中,可以用hostname指令查看主机名:
如上图所示,lw-B85M-D3V就是主机名了。在windows系统中,可以右击“我的电脑”以查看主机名:
其中的 china-fd4ca8a51.应该就是主机名了。
在之前的项目中,网络情况比较简单,当时就以IP地址作为主机的唯一标识了。然而,实际的网络情况不能这样,比如我们学校的网络UESTC-WiFi,我们每次登录的IP地址是不同的。所以,我们希望用一个n元组来唯一标识一台主机,正如调研目标里面所说的,需要主机的主机名,ip地址,mac地址等信息。
收集主机基本信息的日志:
这里跟之前一样,使用bro提供的日志框架。
module HOST_INFO;
export{
# Create an ID for our new stream. By convention, this is
# called "HOST_INFO_LOG".
redef enum Log::ID += { HOST_INFO_LOG };
# redef LogAscii::use_json = T;
# Define the record type that will contain the data to log.
type host_info: record{
ts: time &log;
ip: addr &log;
username: string &default="" &log;
hostname: string &default="" &log;
mac: string &default="" &log;
os: string &default="" &log;
description: string &default="" &log;
};
}
主机基本信息放在一个名为host_info的记录中。其中ts是网络时间,这个时间很重要,我们可以根据这个值看到主机信息是如何变化的。ip字段没有给默认值,因为bro的事件处理函数中,往往都有一个类型为connection的参数,可以很方便地获取ip地址信息。其它字段默认为空,在写日志的时候根据需要来填充,这主要是因为很难从某一个bro事件中拿到所有的主机信息,往往只是其中的两条或者三条,就先把拿到的内容填上,别的字段就暂时先空着。最后有一个description字段,描述一个本条记录是通过什么方式获取到的。
下面说一下如何获取这些字段,由于事件太多,很难一下写全,慢慢补充完整吧。
获取ip地址和mac地址的序偶:
看到mac地址,就自然而然想到arp协议,arp协议负责ip地址和mac地址之间的映射。再看一下bro提供的arp协议的事件,比较有用的就是arp_request和arp_reply事件,分别表示arp请求和arp响应。
# There is no point in removing dulipcated messages for a specific ip.
# Becuase ip addresses should not be the unique identification of a specific host.
# We should identity a specific host by ip and mac pairs which have the lastest network time.
event arp_reply(mac_src: string, mac_dst: string, SPA: addr, SHA: string, TPA: addr, THA: string){
# print "arp reply";
# print fmt("source mac: %s, destination mac: %s, SPA: %s, SHA: %s, TPA: %s, THA: %s", mac_src, mac_dst, SPA, SHA, TPA, THA);
# record ip and its mac address
# we don't need these form of mac addresses:
# 00:00:00:00:00:00 and ff:ff:ff:ff:ff:ff
if(SHA != "ff:ff:ff:ff:ff:ff" && SHA != "00:00:00:00:00:00" && SPA != 0.0.0.0){
local rec1: HOST_INFO::host_info = [$ts = network_time(), $ip = SPA, $mac = SHA, $description = "arp_reply" ];
Log::write(HOST_INFO::HOST_INFO_LOG, rec1);
}
if(THA != "ff:ff:ff:ff:ff:ff" && THA != "00:00:00:00:00:00" && TPA != 0.0.0.0){
local rec2: HOST_INFO::host_info = [$ts = network_time(), $ip = TPA, $mac = THA, $description = "arp_reply" ];
Log::write(HOST_INFO::HOST_INFO_LOG, rec2);
}
}
event arp_request(mac_src: string, mac_dst: string, SPA: addr, SHA: string, TPA: addr, THA: string){
# print "arp request";
# print fmt("source mac: %s, destination mac: %s, SPA: %s, SHA: %s, TPA: %s, THA: %s", mac_src, mac_dst, SPA, SHA, TPA, THA);
if(SHA != "ff:ff:ff:ff:ff:ff" && SHA != "00:00:00:00:00:00" && SPA != 0.0.0.0){
local rec1: HOST_INFO::host_info = [$ts = network_time(), $ip = SPA, $mac = SHA, $description = "arp_request" ];
Log::write(HOST_INFO::HOST_INFO_LOG, rec1);
}
if(THA != "ff:ff:ff:ff:ff:ff" && THA != "00:00:00:00:00:00" && TPA != 0.0.0.0){
local rec2: HOST_INFO::host_info = [$ts = network_time(), $ip = TPA, $mac = THA, $description = "arp_request" ];
Log::write(HOST_INFO::HOST_INFO_LOG, rec2);
}
}
其中参数SPA,SHA,TPA,THA分别表示源ip地址,源物理地址,目的ip地址,目的物理地址。物理地址就是我们想要的mac地址了。再把ff:ff:ff:ff:ff:ff和00:00:00:00:00:00形式的物理地址去掉,就收集到我们想要的ip地址和mac地址的序偶了。
除了arp协议,DHCP协议也包含了物理地址的信息。我们知道DHCP是一个局域网的网络协议,它的重要用途就是自动分配IP地址,在动态分配方式中,就很容易出现某一台主机前后几次登入同一个局域网但却得到几个不同的ip地址的情况。
bro提供的dhcp_message事件就给我们提供了mac信息:
event dhcp_message(c: connection, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options){
# print "A dhcp message is coming!";
# print msg;
# print options;
if(msg$yiaddr != 0.0.0.0){
local rec: HOST_INFO::host_info = [$ts = network_time(), $ip = msg$yiaddr, $mac = msg$chaddr, $description = "dhcp_message" ];
Log::write(HOST_INFO::HOST_INFO_LOG, rec);
}
}
获取主机操作系统信息:
我们知道nmap提供了以主动探测的方式获取目标主机操作系统的方法。nmap需要发送一系列精心设计的报文来搜集目标主机的指纹信息。然而在被动嗅探的情形下,搜集指纹信息就要困难得多(看脸了,没有你要的流量,你也没办法判别别人的操作系统呀)。p0f工具提供了被动嗅探获取操作系统的方法,bro就借用了p0f的功能,提供了OS_version_found事件。
event OS_version_found(c: connection, host: addr, OS: OS_version){
# print "an operating system has been fingerprinted";
# print fmt("the host running this OS is %s", host);
# print OS;
if(OS$genre != "UNKNOWN"){
local os_detail = fmt("%s %s", OS$genre, OS$detail);
local rec: HOST_INFO::host_info = [$ts = network_time(), $ip = host, $os = os_detail, $description = "OS_version_found"];
Log::write(HOST_INFO::HOST_INFO_LOG, rec);
}
# e.g [genre=UNKNOWN, detail=, dist=36, match_type=direct_inference]
# How to utilize this message?
}
尴尬的是,有的时候这个事件确实响应了,但给出的记录里面的genre是UNKNOWN,没办法,只能把它过滤掉了。当它不是UNKNOWN的时候,genre是大的类别,比如Windows,details是对genre的描述,比如是xp sp3。
获取主机名信息:
我们平时上网,输入的网址,里面的域名,其实就是一种主机名。这种说法在教科书里面有,《计算机网络-自顶向下方法》第2章。
书中写到DNS记录和消息,根据记录的类型,里面的Name和Value的含义与此有关。
记录是这样形式的元组:(Name, Value, Type, TTL)
If Type=A, then Name is a hostname and Value is the IP address for the hostname.(引自课本132页)
完整ip和主机名信息要在dns应答报文中查看。bro提供了dns_A_reply事件,针对A类型报文的响应:
event dns_A_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr){
print "********************************TYPE A REPLY*********************";
# print c;
print msg;#[id=0, opcode=0, rcode=0, QR=T, AA=T, TC=F, RD=F, RA=F, Z=0, num_queries=0, num_answers=1, num_auth=0, num_addl=0]
print ans;#[answer_type=1, query=brwa86bad339915.local, qtype=1, qclass=32769, TTL=4.0 mins]
print a;#192.168.1.108
local rec: HOST_INFO::host_info = [$ts = network_time(), $ip = a, $hostname = ans$query, $description = "dns_A_reply" ];
Log::write(HOST_INFO::HOST_INFO_LOG, rec);
}
还有类型为AAAA的dns报文,这是针对ipv6的,bro也提供了一个名为dns_AAAA_reply的事件,这是针对ipv6设置的。
event dns_AAAA_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr){
local rec: HOST_INFO::host_info = [$ts = network_time(), $ip = a, $hostname = ans$query, $description = "dns_AAAA_reply" ];
Log::write(HOST_INFO::HOST_INFO_LOG, rec);
}
可以通过ssh获取主机名信息,下面是bro给出的一个示例脚本,这个脚本是用于演示通知框架的,其中包含了主机名信息:
##! This script will generate a notice if an apparent SSH login originates
##! or heads to a host with a reverse hostname that looks suspicious. By
##! default, the regular expression to match "interesting" hostnames includes
##! names that are typically used for infrastructure hosts like nameservers,
##! mail servers, web servers and ftp servers.
@load base/frameworks/notice
module SSH;
export {
redef enum Notice::Type += {
## Generated if a login originates or responds with a host where
## the reverse hostname lookup resolves to a name matched by the
## :bro:id:`SSH::interesting_hostnames` regular expression.
Interesting_Hostname_Login,
};
## Strange/bad host names to see successful SSH logins from or to.
option interesting_hostnames =
/^d?ns[0-9]*\./ |
/^smtp[0-9]*\./ |
/^mail[0-9]*\./ |
/^pop[0-9]*\./ |
/^imap[0-9]*\./ |
/^www[0-9]*\./ |
/^ftp[0-9]*\./;
}
function check_ssh_hostname(id: conn_id, uid: string, host: addr)
{
when ( local hostname = lookup_addr(host) )
{
if ( interesting_hostnames in hostname )
{
NOTICE([$note=Interesting_Hostname_Login,
$msg=fmt("Possible SSH login involving a %s %s with an interesting hostname.",
Site::is_local_addr(host) ? "local" : "remote",
host == id$orig_h ? "client" : "server"),
$sub=hostname, $id=id, $uid=uid]);
}
}
}
event ssh_auth_successful(c: connection, auth_method_none: bool)
{
for ( host in set(c$id$orig_h, c$id$resp_h) )
{
check_ssh_hostname(c$id, c$uid, host);
}
}
可以考虑一下 其中的ssh_auth_successful事件。
在DHCP报文中也可以获取主机名信息:(对前面的略作修改,options中有主机名信息)
event dhcp_message(c: connection, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options){
if(options ?$ host_name && options ?$ addr_request){
local rec1: HOST_INFO::host_info = [$ts = network_time(), $ip = options$addr_request, $mac = options$client_id$hwaddr, $hostname = options$host_name, $description = "dhcp_message1" ];
Log::write(HOST_INFO::HOST_INFO_LOG, rec1);
} else{
if(msg$yiaddr != 0.0.0.0){
local rec2: HOST_INFO::host_info = [$ts = network_time(), $ip = msg$yiaddr, $mac = msg$chaddr, $description = "dhcp_message2" ];
Log::write(HOST_INFO::HOST_INFO_LOG, rec2);
}
}
}
先判断options中的host_name字段是否存在,若存在将其写入日志。
获取用户名信息:
计算机用户名,如下图所示
横线划出的Administrator,test_user和test_guest都是用户名。其中前两个是管理员,最后一个是普通用户。考虑一下什么数据包中会出现用户名的信息呢?很快就能想到TELNET。看看bro有没有和TELNET相关的事件呢?暂时没有找到。。。
但有一个叫做NTLM的协议,bro提供了于NTLM相关的事件。NTLM是windows中于telnet相关的一个很重要的协议,具体的可以百度。
event ntlm_authenticate(c: connection, request: NTLM::Authenticate){
print c;
print request;
if(request ?$ user_name){
print fmt("username: %s", request$user_name);
}
}
尝试拿到用户名。NTLM::Authenticate是record类型:
NTLM::Authenticate
Type: record
flags: NTLM::NegotiateFlags
The negotiate flags
domain_name: string &optional
The domain or computer name hosting the account
user_name: string &optional
The name of the user to be authenticated.
workstation: string &optional
The name of the computer to which the user was logged on.
session_key: string &optional
The session key
version: NTLM::Version &optional
The Windows version information, if supplied
官方的解释中说有user_name这个字段。
下面是日志的部分结果:
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path host-info
#open 2018-12-20-15-52-56
#fields ts ip username hostname mac os description
#types time addr string string string string string
1545292376.816973 37.6.154.16 (empty) adsl-16.37.6.154.tellas.gr (empty) (empty) dns_A_reply
1545292376.816973 37.6.154.16 (empty) adsl-16.37.6.154.tellas.gr (empty) (empty) dns_A_reply
1545292376.824954 37.6.154.16 (empty) adsl-16.37.6.154.tellas.gr (empty) (empty) dns_A_reply
1545292378.672939 113.54.192.233 (empty) (empty) ac:bc:32:77:d7:ff (empty) arp_request
1545292378.672939 113.54.192.58 (empty) (empty) 60:f8:1d:ad:37:8e (empty) arp_request
1545292378.672939 113.54.192.145 (empty) (empty) f4:f5:db:dc:73:32 (empty) arp_request
1545292378.768879 113.54.199.140 (empty) (empty) 98:5f:d3:e3:12:d0 (empty) arp_request
1545292379.588908 113.54.199.140 (empty) (empty) 98:5f:d3:e3:12:d0 (empty) arp_request
1545292380.240919 37.6.154.16 (empty) adsl-16.37.6.154.tellas.gr (empty) (empty) dns_A_reply
其中有arp请求和dns响应的信息。
1545293561.492880 2a00:1450:4013:c02::5f (empty) safebrowsing.googleapis.com (empty) (empty) dns_AAAA_reply
1545293561.492880 2a00:1450:400e:809::200a (empty) safebrowsing.googleapis.com (empty) (empty) dns_AAAA_reply
这是dns的AAAA类型响应信息。
1545292439.644890 176.119.7.30 (empty) (empty) (empty) NMAP syn scan (1) OS_version_found
这有一条os的信息,明显p0f不靠谱啊。
1545292415.636904 113.54.221.53 (empty) (empty) 5c:c3:07:71:72:c7 (empty) arp_request
1545292415.636904 211.83.98.39 (empty) (empty) 24:0a:64:ba:2e:35 (empty) arp_request
这是arp响应信息。
1545912025.949787 192.168.1.141 (empty) 0K4KAIREJZJCN5X 3c:97:0e:54:df:59 (empty) dhcp_message1
1545912334.065743 192.168.1.79 (empty) (empty) f4:b7:e2:45:41:37 (empty) dhcp_message2
这是dhcp信息,1是带主机名的,2是没带主机名的。
bro提供了很多很多事件,肯定还有很多别的事件也可以获取这些主机基本信息,以后再补充。
(未完,待补充)