-
Notifications
You must be signed in to change notification settings - Fork 30
/
EnterpriseLinuxIdentifier.cpp
142 lines (105 loc) · 3.02 KB
/
EnterpriseLinuxIdentifier.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include "EnterpriseLinuxIdentifier.h"
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/optional.hpp>
#include <boost/regex.hpp>
using namespace std;
using namespace boost;
// compiled from various sources, may be incomplete
const unordered_map<string, int> EnterpriseLinuxIdentifier::BundledVersions = unordered_map<string, int> {
{ "6.6.1p1", 7 },
{ "6.6p1", 6 },
{ "5.3", 6 },
{ "5.2p1", 6 },
{ "4.3", 5 },
{ "3.9", 4 },
};
bool EnterpriseLinuxIdentifier::Scan(Host* host)
{
auto isEL = false, isCO = false;
string sshVer;
optional<int> elVer, secUpd;
// check if any SSH services are available
// ver -> OpenSSH version
// tag -> any EL tag, if not present, below groups will not match
// elver -> version number of RHEL/CentOS
// secver -> if elver also matched, the installed security patch
static regex sshbnr("OpenSSH_(?<ver>[^\\s\\-_$]+)(?:[\\s\\-_](?<tag>CentOS|RHEL|Red.?Hat)[\\s\\-_]?(?<elver>\\d+)(?:.*\\-(?<secver>\\d+)))?", regex::icase);
// match any EL tag
static regex enttag("\\b(centos|red.?hat|rhel)\\b", regex::icase);
// replace "7.2p1" to "7.2:p1" in the CPE
static regex cpever("(\\d)p(\\d+)", regex::icase);
static string cpepatch = "\\1:p\\2";
for (auto service : *host->services)
{
if (service->banner.empty())
{
continue;
}
smatch sm;
// if the service is not SSH, just check if there is an EL tag in it anywhere
if (!starts_with(service->banner, "SSH-2.0-OpenSSH_"))
{
if (regex_search(service->banner, sm, enttag))
{
isEL = true;
auto c = sm[1].str()[0];
isCO = c == 'C' || c == 'c';
}
continue;
}
// if the service is SSH, special handling follows
if (!regex_search(service->banner, sm, sshbnr))
{
continue;
}
// while we're at it, append the CPE name
sshVer = sm["ver"].str();
service->cpe.push_back("a:openbsd:openssh:" + regex_replace(sshVer, cpever, cpepatch));
// if there is an EL tag in the banner, this host runs EL for sure
if (sm["tag"].matched)
{
isEL = true;
auto c = sm["tag"].str()[0];
isCO = c == 'C' || c == 'c';
}
// check if release version is present
if (sm["elver"].matched)
{
elVer = stoi(sm["elver"].str());
if (sm["secver"].matched)
{
secUpd = stoi(sm["secver"].str());
}
}
}
// if we are certain the host is running EL, we have OpenSSH version,
// but no EL distribution version, try to deduce it
if (isEL && !sshVer.empty() && !elVer.is_initialized())
{
auto ver = sshVer;
trim(ver);
to_lower(ver);
auto bver = BundledVersions.find(ver);
if (bver != BundledVersions.end())
{
elVer = (*bver).second;
}
}
// save information
if (isEL)
{
auto cpe = "o:" + string(isCO ? "centos:centos" : "redhat:enterprise_linux");
host->opSys = OpSys::EnterpriseLinux;
if (elVer.is_initialized())
{
cpe += ":" + to_string(elVer.get());
host->osVer = elVer.get();
}
host->cpe.push_back(cpe);
}
return isEL;
}
EnterpriseLinuxIdentifier::~EnterpriseLinuxIdentifier()
{
}