SnortSignature.java
1 |
package net.lukeMurphey.nsia.scanRules; |
---|---|
2 |
|
3 |
import java.util.regex.*; |
4 |
import java.util.*; |
5 |
|
6 |
/**
|
7 |
* This class provides the ability to parse Snort rules and evaluate content to determine if the Snort rules match.
|
8 |
* @author luke
|
9 |
*
|
10 |
*/
|
11 |
public class SnortSignature implements ContentSignature{ |
12 |
|
13 |
private String snortSignatureName; |
14 |
private int sid; |
15 |
private int revision; |
16 |
private String classtype; |
17 |
|
18 |
private Vector<Option> options = new Vector<Option>(); |
19 |
private Vector<Reference> references = new Vector<Reference>(); |
20 |
|
21 |
private static final Pattern SNORT_RULE_REGEX = Pattern.compile("([a-zA-Z]+) ([a-zA-Z]+) ([0-9a-zA-Z$_]+) ([0-9a-zA-Z$_]+) (\\->|<>|<\\-) ([0-9a-zA-Z$_]+) ([0-9a-zA-Z$_]+) (\\()([ /()a-zA-Z%0-9=!?._;:,\\-$\\\"\\']*)(\\))"); |
22 |
private static final Pattern SNORT_OPTIONS_REGEX = Pattern.compile("([ ]*([()a-zA-Z0-9._,\\-$']+)(:[ ]*(([ /()a-zA-Z%0-9=!?._,\\-$']+)|\"([ /()a-zA-Z%0-9!?=._,\\-$']+)\"))?)+"); |
23 |
|
24 |
/**
|
25 |
* This class represents a snort rule option.
|
26 |
* @author luke
|
27 |
*
|
28 |
*/
|
29 |
public class Option{ |
30 |
|
31 |
private String name; |
32 |
private String value = null; |
33 |
|
34 |
public Option( String name ){ |
35 |
this.name = name;
|
36 |
} |
37 |
|
38 |
public Option( String name, String value ){ |
39 |
this.name = name;
|
40 |
this.value = value;
|
41 |
} |
42 |
|
43 |
public String getName(){ |
44 |
return name;
|
45 |
} |
46 |
|
47 |
public String getValue(){ |
48 |
return value;
|
49 |
} |
50 |
|
51 |
public boolean hasValue(){ |
52 |
if( value == null ) |
53 |
return false; |
54 |
else
|
55 |
return true; |
56 |
} |
57 |
} |
58 |
|
59 |
/**
|
60 |
* This class represents a Snort signature reference (URL, CVE entry, etc.).
|
61 |
* @author luke
|
62 |
*
|
63 |
*/
|
64 |
public static class Reference{ |
65 |
|
66 |
public final static Type BUGTRAQ = new Type(1, "http://www.securityfocus.com/bid/"); |
67 |
public final static Type CVE = new Type(2, "http://cve.mitre.org/cgi-bin/cvename.cgi?name="); |
68 |
public final static Type NESSUS = new Type(3, "http://cgi.nessus.org/plugins/dump.php3?id="); |
69 |
public final static Type ARACHNIDS = new Type(4, "http://www.whitehats.com/info/IDS"); |
70 |
public final static Type MCAFEE = new Type(5, "http://vil.nai.com/vil/dispVirus.asp?virus_k="); |
71 |
public final static Type URL = new Type(6, "http://"); |
72 |
|
73 |
/**
|
74 |
* This class represents the possible Snort reference types.
|
75 |
* See http://www.snort.org/docs/snort_htmanuals/htmanual_2.4/node18.html#SECTION00442000000000000000
|
76 |
* @author luke
|
77 |
*
|
78 |
*/
|
79 |
public static class Type{ |
80 |
|
81 |
private int id; |
82 |
private String urlPrefix; |
83 |
|
84 |
protected Type( int id, String urlPrefix ){ |
85 |
this.id = id;
|
86 |
this.urlPrefix = urlPrefix;
|
87 |
} |
88 |
|
89 |
public String getUrlPrefix(){ |
90 |
return urlPrefix;
|
91 |
} |
92 |
|
93 |
public boolean equals(Type type){ |
94 |
|
95 |
// 0 -- Precondition check
|
96 |
if( type == null ){ |
97 |
throw new IllegalArgumentException("Type cannot be null"); |
98 |
} |
99 |
|
100 |
// 1 -- Compare the types
|
101 |
return type.id == id;
|
102 |
} |
103 |
} |
104 |
|
105 |
private Type type; |
106 |
private String value; |
107 |
|
108 |
public Reference(Type type, String value){ |
109 |
this.type = type;
|
110 |
this.value = value;
|
111 |
} |
112 |
|
113 |
public static Reference parse(String value) throws SignatureParseException{ |
114 |
int firstComma = value.indexOf(','); |
115 |
|
116 |
String type = value.substring(0, firstComma).trim(); |
117 |
String argument = value.substring(firstComma+1).trim(); |
118 |
|
119 |
if( type.equalsIgnoreCase("bugtraq") ){ |
120 |
return new Reference(Reference.BUGTRAQ, argument); |
121 |
} |
122 |
else if( type.equalsIgnoreCase("cve") ){ |
123 |
return new Reference(Reference.CVE, argument); |
124 |
} |
125 |
else if( type.equalsIgnoreCase("nessus") ){ |
126 |
return new Reference(Reference.NESSUS, argument); |
127 |
} |
128 |
else if( type.equalsIgnoreCase("arachnids") ){ |
129 |
return new Reference(Reference.ARACHNIDS, argument); |
130 |
} |
131 |
else if( type.equalsIgnoreCase("mcafee") ){ |
132 |
return new Reference(Reference.MCAFEE, argument); |
133 |
} |
134 |
else if( type.equalsIgnoreCase("url") ){ |
135 |
return new Reference(Reference.URL, argument); |
136 |
} |
137 |
else{
|
138 |
throw new SignatureParseException("Reference name (\"" + type + "\" is invalid"); |
139 |
} |
140 |
} |
141 |
|
142 |
public String toString(){ |
143 |
return type.getUrlPrefix() + value;
|
144 |
} |
145 |
} |
146 |
|
147 |
/**
|
148 |
* Takes a Snort rule string and creates an SnortSignature object that is capable of evaluting content for exploits.
|
149 |
* @param snortRule
|
150 |
* @return
|
151 |
* @throws SignatureParseException
|
152 |
*/
|
153 |
public static SnortSignature parse(String snortRule) throws SignatureParseException{ |
154 |
|
155 |
// 0 -- Precondition check
|
156 |
|
157 |
// 0.1 --Rule must not be null
|
158 |
if( snortRule == null ){ |
159 |
throw new IllegalArgumentException("Snort rule must not be null"); |
160 |
} |
161 |
|
162 |
// 0.2 --Rule must not be empty
|
163 |
if( snortRule.isEmpty() ){
|
164 |
throw new IllegalArgumentException("Snort rule must not be empty"); |
165 |
} |
166 |
|
167 |
|
168 |
// 1 -- Identify the section with the options
|
169 |
String options;
|
170 |
Matcher ruleMatcher = SNORT_RULE_REGEX.matcher(snortRule);
|
171 |
|
172 |
if( !ruleMatcher.matches() || ruleMatcher.groupCount() < 10 ){ |
173 |
throw new SignatureParseException("The snort rule given does not match a known rule format"); |
174 |
} |
175 |
else{
|
176 |
options = ruleMatcher.group(9);
|
177 |
} |
178 |
|
179 |
// 2 -- Parse out the rule options
|
180 |
SnortSignature snortSig = new SnortSignature();
|
181 |
|
182 |
Matcher optionsMatcher = SNORT_OPTIONS_REGEX.matcher(options);
|
183 |
|
184 |
while (optionsMatcher.find()){
|
185 |
|
186 |
// 2.1 --Get the option name
|
187 |
String optionName = optionsMatcher.group(2).trim(); |
188 |
|
189 |
// 2.2 -- Get the option value (if exists)
|
190 |
String optionValue = null; |
191 |
|
192 |
if( optionsMatcher.group(6) != null ){ |
193 |
optionValue = optionsMatcher.group(6);
|
194 |
} |
195 |
else{
|
196 |
optionValue = optionsMatcher.group(5);
|
197 |
} |
198 |
|
199 |
// 2.3 -- Save the option to the list
|
200 |
if(optionValue == null){ |
201 |
snortSig.options.add( snortSig.new Option( optionName ) );
|
202 |
} |
203 |
else{
|
204 |
snortSig.options.add( snortSig.new Option( optionName, optionValue) );
|
205 |
} |
206 |
|
207 |
|
208 |
} |
209 |
|
210 |
|
211 |
// 3 -- Populate the signature from the options
|
212 |
for( int c = 0; c < snortSig.options.size(); c++){ |
213 |
Option option = snortSig.options.get(c);
|
214 |
|
215 |
if( option.getName().equalsIgnoreCase("msg") ){ |
216 |
snortSig.snortSignatureName = option.getValue(); |
217 |
} |
218 |
|
219 |
if( option.getName().equalsIgnoreCase("sid") ){ |
220 |
snortSig.sid = Integer.parseInt( option.getValue() );
|
221 |
} |
222 |
|
223 |
if( option.getName().equalsIgnoreCase("rev") ){ |
224 |
snortSig.revision = Integer.parseInt( option.getValue() );
|
225 |
} |
226 |
|
227 |
if( option.getName().equalsIgnoreCase("classtype") ){ |
228 |
snortSig.classtype = option.getValue(); |
229 |
} |
230 |
|
231 |
if( option.getName().equalsIgnoreCase("reference") ){ |
232 |
snortSig.references.add( Reference.parse( option.getValue() ) );
|
233 |
} |
234 |
} |
235 |
|
236 |
return snortSig;
|
237 |
} |
238 |
|
239 |
|
240 |
|
241 |
public String getName() { |
242 |
String description = "This signature detects attacks originating from monitored resource. Details are below:\n\nSignature: " + snortSignatureName; |
243 |
|
244 |
if( references != null && references.size() > 0 ){ |
245 |
return description + "\nReferences:\n" + getNotes(); |
246 |
} |
247 |
else
|
248 |
return description;
|
249 |
} |
250 |
|
251 |
public String getNotes() { |
252 |
StringBuffer notes = new StringBuffer(""); |
253 |
if( references != null ){ |
254 |
for( int c = 0; c < references.size(); c++){ |
255 |
if( c > 0 ){ |
256 |
notes.append("\n" + references.get(c).toString());
|
257 |
} |
258 |
else{
|
259 |
notes.append(references.get(c).toString()); |
260 |
} |
261 |
} |
262 |
} |
263 |
|
264 |
return notes.toString();
|
265 |
} |
266 |
|
267 |
public String getCategoryName(){ |
268 |
return "Exploit Signature"; |
269 |
} |
270 |
|
271 |
public String getClassType() { |
272 |
return classtype;
|
273 |
} |
274 |
|
275 |
public Reference[] getReferences() { |
276 |
return (Reference[])references.toArray(); |
277 |
} |
278 |
|
279 |
public String getSubCategoryName() { |
280 |
return snortSignatureName;
|
281 |
} |
282 |
|
283 |
public int getID() { |
284 |
return sid;
|
285 |
} |
286 |
|
287 |
public int getRevision() { |
288 |
return revision;
|
289 |
} |
290 |
|
291 |
public Option[] getOptions(){ |
292 |
Option[] optionsArray = new Option[options.size()]; |
293 |
options.toArray(optionsArray); |
294 |
|
295 |
return optionsArray;
|
296 |
} |
297 |
|
298 |
} |