Guest User

PP_Conf_Anshuman

a guest
Oct 22nd, 2013
306
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 74.00 KB | None | 0 0
  1. #!/usr/bin/perl
  2.  
  3. ## pulledpork v(whatever it says below!)
  4.  
  5. # Copyright (C) 2009-2013 JJ Cummings and the PulledPork Team!
  6.  
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU General Public License
  9. # as published by the Free Software Foundation; either version 2
  10. # of the License, or (at your option) any later version.
  11.  
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16.  
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20.  
  21. use strict;
  22. use warnings;
  23. use File::Copy;
  24. use LWP::UserAgent;
  25. use HTTP::Request::Common;
  26. use HTTP::Status qw (is_success);
  27. use Crypt::SSLeay;
  28. use Sys::Syslog;
  29. use Digest::MD5;
  30. use File::Path;
  31. use File::Find;
  32. use Getopt::Long qw(:config no_ignore_case bundling);
  33. use Archive::Tar;
  34. use POSIX qw(:errno_h);
  35. use Cwd;
  36. use Carp;
  37. use Data::Dumper;
  38.  
  39. ### Vars here
  40.  
  41. # we are gonna need these!
  42. my ( $oinkcode, $temp_path, $rule_file, $Syslogging );
  43. my $VERSION = "PulledPork v0.7.0 - Swine Flu!";
  44. my $ua = LWP::UserAgent->new;
  45.  
  46. my ( $Hash, $ALogger, $Config_file, $Sorules, $Auto );
  47. my ( $Output, $Distro, $Snort, $sid_changelog, $ignore_files );
  48. my ( $Snort_config, $Snort_path, $Textonly, $grabonly, $ips_policy, );
  49. my ( $pid_path, $SigHup, $NoDownload, $sid_msg_map, @base_url );
  50. my ( $local_rules, $arch, $docs, @records, $enonly );
  51. my ( $rstate, $keep_rulefiles, $rule_file_path, $prefix, $black_list );
  52. my ( $Process, $hmatch, $bmatch , $sid_msg_version);
  53. my $Sostubs = 1;
  54.  
  55. # verbose and quiet control print()
  56. # default values if not set otherwise in getopt
  57. # $Verbose = 0 is normal output (default behaviour)
  58. # $Verbose = 1 is loud output
  59. # $Verbose = 2 is troubleshooting output
  60. # $Quiet = 0 leaves verbosity as default or otherwise set
  61. # $Quiet = 1 suppresses all but FAIL messages, eg, anything preceding an exit
  62.  
  63. my $Verbose = 0;
  64. my $Quiet = 0;
  65.  
  66. undef($Hash);
  67. undef($ALogger);
  68.  
  69. my %rules_hash = ();
  70. my %blacklist = ();
  71. my %oldrules_hash = ();
  72. my %sid_msg_map = ();
  73. my %sidmod = ();
  74. my $categories = ();
  75. undef %rules_hash;
  76. undef %oldrules_hash;
  77. undef %sid_msg_map;
  78.  
  79. ## initialize some vars
  80. my $rule_digest = "";
  81. my $md5 = "";
  82.  
  83. # Vars the subroutine to fetch config values
  84. my ($Config_key);
  85. my %Config_info = ();
  86.  
  87. ### Subroutines here
  88.  
  89. ## routine to grab our config from the defined config file
  90. sub parse_config_file {
  91. my ( $FileConf, $Config_val ) = @_;
  92. my ( $config_line, $Name, $Value );
  93.  
  94. if ( !open( CONFIG, "$FileConf" ) ) {
  95. carp "ERROR: Config file not found : $FileConf\n";
  96. syslogit( 'err|local0', "FATAL: Config file not found: $FileConf" )
  97. if $Syslogging;
  98. exit(1);
  99. }
  100. open( CONFIG, "$FileConf" );
  101. while (<CONFIG>) {
  102. $config_line = $_;
  103. chomp($config_line);
  104. $config_line = trim($config_line);
  105. if ( ( $config_line !~ /^#/ ) && ( $config_line ne "" ) ) {
  106. ( $Name, $Value ) = split( /=/, $config_line );
  107. if ( $Value =~ /,/ && $Name eq "rule_url" ) {
  108. push( @{ $$Config_val{$Name} }, split( /,/, $Value ) );
  109. }
  110. elsif ( $Name eq "rule_url" ) {
  111. push( @{ $$Config_val{$Name} }, split( /,/, $Value ) )
  112. if $Value;
  113. }
  114. else {
  115. $$Config_val{$Name} = $Value;
  116. }
  117. }
  118. }
  119.  
  120. close(CONFIG);
  121.  
  122. }
  123.  
  124. ## Help routine.. display help to stdout then exit
  125. sub Help {
  126. my $msg = shift;
  127. if ($msg) { print "\nERROR: $msg\n"; }
  128.  
  129. print <<__EOT;
  130. Usage: $0 [-dEgHklnRTPVvv? -help] -c <config filename> -o <rule output path>
  131. -O <oinkcode> -s <so_rule output directory> -D <Distro> -S <SnortVer>
  132. -p <path to your snort binary> -C <path to your snort.conf> -t <sostub output path>
  133. -h <changelog path> -I (security|connectivity|balanced) -i <path to disablesid.conf>
  134. -b <path to dropsid.conf> -e <path to enablesid.conf> -M <path to modifysid.conf>
  135. -r <path to docs folder> -K <directory for separate rules files>
  136.  
  137. Options:
  138. -help/? Print this help info.
  139. -b Where the dropsid config file lives.
  140. -C Path to your snort.conf
  141. -c Where the pulledpork config file lives.
  142. -d Do not verify signature of rules tarball, i.e. downloading fron non VRT or ET locations.
  143. -D What Distro are you running on, for the so_rules
  144. For latest supported options see http://www.snort.org/snort-rules/shared-object-rules
  145. Valid Distro Types:
  146. Debian-5-0, Debian-6-0, Ubuntu-8.04, Ubuntu-10-4
  147. Centos-4-8, Centos-5-4, FC-12, FC-14, RHEL-5-5, RHEL-6-0
  148. FreeBSD-7-3, FreeBSD-8-1
  149. OpenBSD-4-8
  150. Slackware-13-1
  151. -e Where the enablesid config file lives.
  152. -E Write ONLY the enabled rules to the output files.
  153. -g grabonly (download tarball rule file(s) and do NOT process)
  154. -h path to the sid_changelog if you want to keep one?
  155. -H Send a SIGHUP to the pids listed in the config file
  156. -I Specify a base ruleset( -I security,connectivity,or balanced, see README.RULESET)
  157. -i Where the disablesid config file lives.
  158. -k Keep the rules in separate files (using same file names as found when reading)
  159. -K Where (what directory) do you want me to put the separate rules files?
  160. -l Log Important Info to Syslog (Errors, Successful run etc, all items logged as WARN or higher)
  161. -L Where do you want me to read your local.rules for inclusion in sid-msg.map
  162. -m where do you want me to put the sid-msg.map file?
  163. -M where the modifysid config file lives.
  164. -n Do everything other than download of new files (disablesid, etc)
  165. -o Where do you want me to put generic rules file?
  166. -p Path to your Snort binary
  167. -P Process rules even if no new rules were downloaded
  168. -R When processing enablesid, return the rules to their ORIGINAL state
  169. -r Where do you want me to put the reference docs (xxxx.txt)
  170. -S What version of snort are you using (2.8.6 or 2.9.0) are valid values
  171. -s Where do you want me to put the so_rules?
  172. -T Process text based rules files only, i.e. DO NOT process so_rules
  173. -u Where do you want me to pull the rules tarball from
  174. ** E.g., ET, Snort.org. See pulledpork config rule_url option for value ideas
  175. -V Print Version and exit
  176. -v Verbose mode, you know.. for troubleshooting and such nonsense.
  177. -vv EXTRA Verbose mode, you know.. for in-depth troubleshooting and other such nonsense.
  178. __EOT
  179.  
  180. exit(0);
  181. }
  182.  
  183. ## OMG We MUST HAVE FLYING PIGS!
  184. sub pulledpork {
  185.  
  186. print <<__EOT;
  187.  
  188. http://code.google.com/p/pulledpork/
  189. _____ ____
  190. `----,\\ )
  191. `--==\\\\ / $VERSION
  192. `--==\\\\/
  193. .-~~~~-.Y|\\\\_ Copyright (C) 2009-2013 JJ Cummings
  194. \@_/ / 66\\_ cummingsj\@gmail.com
  195. | \\ \\ _(\")
  196. \\ /-| ||'--' Rules give me wings!
  197. \\_\\ \\_\\\\
  198. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  199.  
  200. __EOT
  201. }
  202.  
  203. ## subroutine to cleanup the temp rubbish!!!
  204. sub temp_cleanup {
  205. my $remove = rmtree( $temp_path . "tha_rules" );
  206. if ( $Verbose && !$Quiet ) {
  207. print "Cleanup....\n";
  208. print
  209. "\tremoved $remove temporary snort files or directories from $temp_path"
  210. . "tha_rules!\n";
  211. }
  212. }
  213.  
  214. # subroutine to extract the files to a temp path so that we can do what we need to do..
  215. sub rule_extract {
  216. my (
  217. $rule_file, $temp_path, $Distro, $arch, $Snort,
  218. $Sorules, $ignore, $docs, $prefix
  219. ) = @_;
  220. print "Prepping rules from $rule_file for work....\n" if !$Quiet;
  221. print "\textracting contents of $temp_path$rule_file...\n"
  222. if ( $Verbose && !$Quiet );
  223. mkpath( $temp_path . "tha_rules" );
  224. mkpath( $temp_path . "tha_rules/so_rules" );
  225. my $tar = Archive::Tar->new();
  226. $tar->read( $temp_path . $rule_file );
  227. $tar->setcwd( cwd() );
  228. local $Archive::Tar::CHOWN = 0;
  229. my @ignores = split( /,/, $ignore );
  230.  
  231. foreach (@ignores) {
  232. if ( $_ =~ /\.rules/ ) {
  233. print "\tIgnoring plaintext rules: $_\n" if ( $Verbose && !$Quiet );
  234. $tar->remove("rules/$_");
  235. }
  236. elsif ( $_ =~ /\.preproc/ ) {
  237. print "\tIgnoring preprocessor rules: $_\n"
  238. if ( $Verbose && !$Quiet );
  239. my $preprocfile = $_;
  240. $preprocfile =~ s/\.preproc/\.rules/;
  241. $tar->remove("preproc_rules/$preprocfile");
  242. }
  243. elsif ( $_ =~ /\.so/ ) {
  244. print "\tIgnoring shared object rules: $_\n"
  245. if ( $Verbose && !$Quiet );
  246. $tar->remove("so_rules/precompiled/$Distro/$arch/$Snort/$_");
  247. }
  248. else {
  249. print "\tIgnoring all rule types in $_ category!\n"
  250. if ( $Verbose && !$Quiet );
  251. $tar->remove("rules/$_.rules");
  252. $tar->remove("preproc_rules/$_.rules");
  253. $tar->remove("so_rules/precompiled/$Distro/$arch/$Snort/$_");
  254. }
  255. }
  256. my @files = $tar->get_files();
  257. foreach (@files) {
  258. my $filename = $_->name;
  259. my $singlefile = $filename;
  260. if ( $filename =~ /^(community-)?rules\/.*\.rules$/ ) {
  261. $singlefile =~ s/^(community-)?rules\///;
  262. $tar->extract_file( $filename,
  263. $temp_path . "/tha_rules/$prefix" . $singlefile );
  264. print "\tExtracted: /tha_rules/$prefix$singlefile\n"
  265. if ( $Verbose && !$Quiet );
  266. }
  267. elsif ( $filename =~ /^preproc_rules\/.*\.rules$/ ) {
  268. $singlefile =~ s/^preproc_rules\///;
  269. $tar->extract_file( $filename,
  270. $temp_path . "/tha_rules/$prefix" . $singlefile );
  271. print "\tExtracted: /tha_rules/$prefix$singlefile\n"
  272. if ( $Verbose && !$Quiet );
  273. }
  274. elsif ($Sorules
  275. && $filename =~
  276. /^so_rules\/precompiled\/($Distro)\/($arch)\/($Snort)\/.*\.so/
  277. && -d $Sorules
  278. && !$Textonly )
  279. {
  280. $singlefile =~
  281. s/^so_rules\/precompiled\/($Distro)\/($arch)\/($Snort)\///;
  282. $tar->extract_file( $filename, $Sorules . $singlefile );
  283. print "\tExtracted: $Sorules$singlefile\n"
  284. if ( $Verbose && !$Quiet );
  285. }
  286. elsif ($docs
  287. && $filename =~ /^(doc\/signatures\/)?.*\.txt/
  288. && -d $docs )
  289. {
  290. $singlefile =~ s/^doc\/signatures\///;
  291. $tar->extract_file( "doc/signatures/$filename",
  292. $docs . $singlefile );
  293. print "\tExtracted: $docs$singlefile\n"
  294. if ( $Verbose == 2 && !$Quiet );
  295. }
  296. }
  297. print "\tDone!\n" if ( !$Verbose && !$Quiet );
  298. }
  299.  
  300. ## subroutine to actually check the md5 values, if they match we move onto file manipulation routines
  301. sub compare_md5 {
  302. my (
  303. $oinkcode, $rule_file, $temp_path, $Hash,
  304. $base_url, $md5, $rule_digest, $Distro,
  305. $arch, $Snort, $Sorules, $ignore_files,
  306. $docs, $prefix, $Process, $hmatch,
  307. $fref
  308. ) = @_;
  309. if ( $rule_digest =~ $md5 && !$Hash) {
  310. if ( $Verbose && !$Quiet ) {
  311. print
  312. "\tThe MD5 for $rule_file matched $md5\n\n";
  313. }
  314. if ( !$Verbose && !$Quiet ) { print "\tThey Match\n\tDone!\n"; }
  315. return(1);
  316. }
  317. elsif ( $rule_digest !~ $md5 && !$Hash ) {
  318. if ( $Verbose && !$Quiet ) {
  319. print
  320. "\tThe MD5 for $rule_file did not match the latest digest... so I am gonna fetch the latest rules file!\n";
  321. }
  322. if ( !$Verbose && !$Quiet ) { print "\tNo Match\n\tDone\n"; }
  323. rulefetch( $oinkcode, $rule_file, $temp_path, $base_url );
  324. $rule_digest = md5sum( $rule_file, $temp_path );
  325. $fref->{EXTRACT}=1 if !$grabonly;
  326. return(compare_md5(
  327. $oinkcode, $rule_file, $temp_path, $Hash,
  328. $base_url, $md5, $rule_digest, $Distro,
  329. $arch, $Snort, $Sorules, $ignore_files,
  330. $docs, $prefix, $Process, $hmatch,
  331. $fref
  332. ));
  333. }
  334. elsif ($Hash) {
  335. if ( $Verbose && !$Quiet) {
  336. print
  337. "\tOk, not verifying the digest.. lame, but that's what you specified!\n";
  338. print
  339. "\tSo if the rules tarball doesn't extract properly and this script croaks.. it's your fault!\n";
  340. print "\tNo Verify Set\n\tDone!\n";
  341. }
  342. $fref->{EXTRACT}=1 if !$grabonly;
  343. return(1);
  344. } else {
  345. return($hmatch);
  346. }
  347. }
  348.  
  349. ## mimic LWP::Simple getstore routine - Thx pkthound!
  350. sub getstore {
  351. my ( $url, $file ) = @_;
  352. my $request = HTTP::Request->new( GET => $url );
  353. my $response = $ua->request( $request, $file );
  354. $response->code;
  355. }
  356.  
  357. ## time to grab the real 0xb33f
  358. sub rulefetch {
  359. my ( $oinkcode, $rule_file, $temp_path, $base_url ) = @_;
  360. print "Rules tarball download of $rule_file....\n" if ( !$Quiet && $rule_file !~/IPBLACKLIST/ );
  361. print "IP Blacklist download of $base_url....\n" if ( !$Quiet && $rule_file =~/IPBLACKLIST/ );
  362. $base_url = slash( 0, $base_url );
  363. my ($getrules_rule);
  364. if ( $Verbose && !$Quiet ) {
  365. print "\tFetching rules file: $rule_file\n" if $rule_file !~/IPBLACKLIST/;
  366. if ($Hash && $rule_file !~/IPBLACKLIST/) { print "But not verifying MD5\n"; }
  367. }
  368. if ( $base_url =~ /[^labs]\.snort\.org/i ) {
  369. $getrules_rule =
  370. getstore( "https://www.snort.org/reg-rules/$rule_file/$oinkcode",
  371. $temp_path . $rule_file );
  372. }
  373. elsif ($rule_file =~ /IPBLACKLIST/ && !$NoDownload){
  374. my $rand = rand(1000);
  375. $getrules_rule =
  376. getstore( $base_url, $temp_path . "$rand-black_list.rules");
  377. read_iplist(\%blacklist,$temp_path . "$rand-black_list.rules");
  378. unlink ($temp_path . "$rand-black_list.rules");
  379. }
  380. else {
  381. $getrules_rule =
  382. getstore( $base_url . "/" . $rule_file, $temp_path . $rule_file );
  383. }
  384. if ( $getrules_rule == 403 ) {
  385. print
  386. "\tA 403 error occurred, please wait for the 15 minute timeout\n\tto expire before trying again or specify the -n runtime switch\n",
  387. "\tYou may also wish to verfiy your oinkcode, tarball name, and other configuration options\n";
  388. syslogit( 'emerg|local0', "FATAL: 403 error occured" ) if $Syslogging;
  389. exit(1); # For you shirkdog
  390. }
  391. elsif ( $getrules_rule == 404 ) {
  392. print
  393. "\tA 404 error occurred, please verify your filenames and urls for your tarball!\n";
  394. syslogit( 'emerg|local0', "FATAL: 404 error occured" ) if $Syslogging;
  395. exit(1); # For you shirkdog
  396. }
  397. elsif ( $getrules_rule == 500 ) {
  398. print
  399. "\tA 500 error occurred, please verify that you have recently updated your root certificates!\n";
  400. syslogit( 'emerg|local0', "FATAL: 500 error occured" ) if $Syslogging;
  401. exit(1); # Certs bitches!
  402. }
  403. unless ( is_success($getrules_rule) ) {
  404. syslogit( 'emerg|local0',
  405. "FATAL: Error $getrules_rule when fetching $rule_file" )
  406. if $Syslogging;
  407. croak "\tError $getrules_rule when fetching " . $rule_file;
  408. }
  409.  
  410. if ( $Verbose && !$Quiet && $rule_file !~/IPBLACKLIST/) {
  411. print("\tstoring file at: $temp_path$rule_file\n\n");
  412. }
  413. if ( !$Verbose && !$Quiet ) { "\tDone!\n"; }
  414. }
  415.  
  416. ## subroutine to deterine the md5 digest of the current rules file
  417. sub md5sum {
  418. my ( $rule_file, $temp_path ) = @_;
  419. open( MD5FILE, "$temp_path$rule_file" )
  420. or croak $!;
  421. binmode(MD5FILE);
  422. $rule_digest = Digest::MD5->new->addfile(*MD5FILE)->hexdigest;
  423. close(MD5FILE);
  424. if ($@) {
  425. print $@;
  426. return "";
  427. }
  428. if ( $Verbose && !$Quiet ) {
  429. print "\tcurrent local rules file digest: $rule_digest\n";
  430. }
  431. return $rule_digest;
  432. }
  433.  
  434. ## subroutine to fetch the latest md5 digest signature file from snort.org
  435. sub md5file {
  436. my ( $oinkcode, $rule_file, $temp_path, $base_url ) = @_;
  437. my ( $getrules_md5, $md5 );
  438. $base_url = slash( 0, $base_url );
  439. print "Checking latest MD5 for $rule_file....\n" if !$Quiet;
  440. print "\tFetching md5sum for: " . $rule_file . ".md5\n"
  441. if ( $Verbose && !$Quiet );
  442. if ( $base_url =~ /[^labs]\.snort\.org/i ) {
  443. $getrules_md5 =
  444. getstore( "https://www.snort.org/reg-rules/$rule_file.md5/$oinkcode",
  445. $temp_path . $rule_file . ".md5" );
  446. }
  447. elsif ( $base_url =~ /(emergingthreats\.net|emergingthreatspro\.com|snort-org.*community)/i ) {
  448. $getrules_md5 = getstore(
  449. "$base_url/$rule_file" . ".md5",
  450. $temp_path . $rule_file . ".md5"
  451. );
  452. }
  453. if ( $getrules_md5 == 403 ) {
  454. print
  455. "\tA 403 error occurred, please wait for the 15 minute timeout\n\tto expire before trying again or specify the -n runtime switch\n",
  456. "\tYou may also wish to verfiy your oinkcode, tarball name, and other configuration options\n";
  457. }
  458. elsif ( $getrules_md5 == 404 ) {
  459. print
  460. "\tA 404 error occurred, please verify your filenames and urls for your tarball!\n";
  461. }
  462. croak "\tError $getrules_md5 when fetching "
  463. . $base_url . "/"
  464. . $rule_file . ".md5"
  465. unless is_success($getrules_md5);
  466. open( FILE, "$temp_path$rule_file.md5" )
  467. or croak $!;
  468. $md5 = <FILE>;
  469. chomp($md5);
  470. close(FILE);
  471. $md5 =~ /\w{32}/
  472. ; ## Lets just grab the hash out of the string.. don't care about the rest!
  473. $md5 = $&;
  474.  
  475. if ( $Verbose && !$Quiet ) {
  476. print "\tmost recent rules file digest: $md5\n";
  477. }
  478. return $md5;
  479. }
  480.  
  481. ## This sub allows for ip-reputation list de-duplication and processing:
  482. sub read_iplist {
  483. my ($href,$path) = @_;
  484. print "\t" if ( $Verbose && !$Quiet );
  485. print "Reading IP List...\n" if !$Quiet;
  486. open (FH,'<',$path) || croak "Couldn't read $path - $!\n";
  487. while (<FH>) {
  488. chomp();
  489. # we only want valid IP addresses, otherwise shiz melts!
  490. next unless $_ =~ /(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/;
  491. $_ = trim($_);
  492. $_ =~ s/,*//;
  493. $href->{$_}=1;
  494. }
  495. close(FH);
  496. }
  497.  
  498. ## This replaces the copy_rules routine and allows for in-memory processing
  499. # of disablesid, enablesid, dropsid and other sid functions.. here we place
  500. # all of the rules values into a hash as {$gid}{$sid}=$rule
  501. sub read_rules {
  502. my ( $hashref, $path, $extra_rules ) = @_;
  503. my ( $file, $sid, $gid, @elements );
  504. print "\t" if ( $Verbose && !$Quiet );
  505. print "Reading rules...\n" if !$Quiet;
  506. my @local_rules = split( /,/, $extra_rules );
  507. foreach (@local_rules) { #First let's read our local rules and assign a gid of 0
  508. $extra_rules = slash( 0, $_ );
  509. if ( $extra_rules && -f $extra_rules ) {
  510. open( DATA, "$extra_rules" )
  511. || croak "Couldn't read $extra_rules - $!\n";
  512. my @extra_raw = <DATA>;
  513. close(DATA);
  514. my $trk = 0;
  515. my $record;
  516. foreach my $row (@extra_raw) {
  517. $row = trim($row);
  518. chomp($row);
  519. if ( $row =~ /^\s*#*\s*(alert|drop|pass)/i || $trk == 1 ) {
  520. if ( ( $row !~ /^#/ ) && ( $row ne "" ) ) {
  521. if ( $row =~ /\\$/ ) { # handle multiline rules here
  522. $row =~ s/\\$//;
  523. $record .= $row;
  524. $trk = 1;
  525. }
  526. elsif ( $row !~ /\\$/ && $trk == 1 )
  527. { # last line of multiline rule here
  528. $record .= $row;
  529. if ( $record =~ /sid:\s*\d+\s*;/i ) {
  530. $sid = $&;
  531. $sid =~ s/sid:\s*//;
  532. $sid =~ s/\s*;//;
  533. $$hashref{0}{ trim($sid) }{'rule'} = $record;
  534. }
  535. $trk = 0;
  536. undef $record;
  537. }
  538. else {
  539. if ( $row =~ /sid:\s*\d+\s*;/i ) {
  540. $sid = $&;
  541. $sid =~ s/sid:\s*//;
  542. $sid =~ s/\s*;//;
  543. $$hashref{0}{ trim($sid) }{'rule'} = $row;
  544. }
  545. $trk = 0;
  546. }
  547. }
  548. }
  549. }
  550. undef @extra_raw;
  551. }
  552. }
  553. if ( -d $path ) {
  554. opendir( DIR, "$path" );
  555. while ( defined( $file = readdir DIR ) ) {
  556. open( DATA, "$path$file" ) || croak "Couldn't read $file - $!\n";
  557. @elements = <DATA>;
  558. close(DATA);
  559.  
  560. foreach my $rule (@elements) {
  561. chomp($rule);
  562. $rule = trim($rule);
  563. if ( $rule =~ /^\s*#*\s*(alert|drop|pass)/i ) {
  564.  
  565. if ( $rule =~ /sid:\s*\d+\s*;/i ) {
  566. $sid = $&;
  567. $sid =~ s/sid:\s*//;
  568. $sid =~ s/\s*;//;
  569. if ( $rule =~ /gid:\s*\d+/i ) {
  570. $gid = $&;
  571. $gid =~ s/gid:\s*//;
  572. }
  573. else { $gid = 1; }
  574. if ( $rule =~ /flowbits:\s*((un)?set(x)?|toggle)/i ) {
  575.  
  576. # There is a much cleaner way to do this, I just don't have the time to do it right now!
  577. my ( $header, $options ) =
  578. split( /^[^"]* \(/, $rule );
  579. my @optarray = split( /(?<!\\);(\t|\s)*/, $options )
  580. if $options;
  581. foreach my $option ( reverse(@optarray) ) {
  582. my ( $kw, $arg ) = split( /:/, $option )
  583. if $option;
  584. next
  585. unless ( $kw && $arg && $kw eq "flowbits" );
  586. my ( $flowact, $flowbit ) = split( /,/, $arg );
  587. next unless $flowact =~ /^\s*((un)?set(x)?|toggle)/i;
  588. $$hashref{ trim($gid) }{ trim($sid) }
  589. { trim($flowbit) } = 1;
  590. }
  591.  
  592. }
  593. if ( $rule =~ /^\s*\#+/ ) {
  594. $$hashref{ trim($gid) }{ trim($sid) }{'state'} = 0;
  595. }
  596. elsif ( $rule =~ /^\s*(alert|pass|drop)/ ) {
  597. $$hashref{ trim($gid) }{ trim($sid) }{'state'} = 1;
  598. }
  599. $file =~ s/\.rules//;
  600. $file = "VRT-SO-$file" if ($gid == 3 && $file !~ /VRT-SO/);
  601. $$hashref{ trim($gid) }{ trim($sid) }{'rule'} = $rule;
  602. $$hashref{ trim($gid) }{ trim($sid) }{'category'} =
  603. $file;
  604. push @ {$categories->{$file}{trim($gid)}},($sid);
  605. }
  606. }
  607. }
  608. }
  609. close(DIR);
  610. }
  611. elsif ( -f $path ) {
  612. open( DATA, "$path" ) || croak "Couldn't read $path - $!";
  613. @elements = <DATA>;
  614. close(DATA);
  615.  
  616. foreach my $rule (@elements) {
  617. if ( $rule =~ /^\s*#*\s*(alert|drop|pass)/i ) {
  618. if ( $rule =~ /sid:\s*\d+/ ) {
  619. $sid = $&;
  620. $sid =~ s/sid:\s*//;
  621. if ( $rule =~ /gid:\s*\d+/i ) {
  622. $gid = $&;
  623. $gid =~ s/gid:\s*//;
  624. }
  625. else { $gid = 1; }
  626. if ( $rule =~ /flowbits:\s*((un)?set(x)?|toggle)/ ) {
  627. my ( $header, $options ) = split( /^[^"]* \(/, $rule );
  628.  
  629. # There is a much cleaner way to do this, I just don't have the time to do it right now!
  630. my @optarray = split( /(?<!\\);(\t|\s)*/, $options )
  631. if $options;
  632. foreach my $option ( reverse(@optarray) ) {
  633. my ( $kw, $arg ) = split( /:/, $option ) if $option;
  634. next unless ( $kw && $arg && $kw eq "flowbits" );
  635. my ( $flowact, $flowbit ) = split( /,/, $arg );
  636. next unless $flowact =~ /^\s*((un)?set(x)?|toggle)/i;
  637. $$hashref{ trim($gid) }{ trim($sid) }
  638. { trim($flowbit) } = 1;
  639. }
  640.  
  641. }
  642. $$hashref{ trim($gid) }{ trim($sid) }{'rule'} = $rule;
  643. }
  644. }
  645. }
  646. }
  647. undef @elements;
  648. }
  649.  
  650. ## sub to generate stub files using the snort --dump-dynamic-rules option
  651. sub gen_stubs {
  652. my ( $Snort_path, $Snort_config, $Sostubs ) = @_;
  653. print "Generating Stub Rules....\n" if !$Quiet;
  654. unless ( -B $Snort_path ) {
  655. Help("$Snort_path is not a valid binary file");
  656. }
  657. if ( -d $Sostubs && -B $Snort_path && -f $Snort_config ) {
  658. if ( $Verbose && !$Quiet ) {
  659. print(
  660. "\tGenerating shared object stubs via:$Snort_path -c $Snort_config --dump-dynamic-rules=$Sostubs\n"
  661. );
  662. }
  663. open( FH,
  664. "$Snort_path -c $Snort_config --dump-dynamic-rules=$Sostubs 2>&1|"
  665. );
  666. while (<FH>) {
  667. print "\t$_" if $_ =~ /Dumping/i && ( $Verbose && !$Quiet );
  668. next unless $_ =~ /(err|warn|fail)/i;
  669. syslogit( 'warning|local0', "FATAL: An error occured: $_" )
  670. if $Syslogging;
  671. print "\tAn error occurred: $_\n";
  672.  
  673. # Yes, this is lame error reporting to stdout ...
  674. }
  675. close(FH);
  676. }
  677. else {
  678. print(
  679. "Something failed in the gen_stubs sub, please verify your shared object config!\n"
  680. );
  681. if ( $Verbose && !$Quiet ) {
  682. unless ( -d $Sostubs ) {
  683. Help(
  684. "The path that you specified: $Sostubs does not exist! Please verify your configuration.\n"
  685. );
  686. }
  687. unless ( -f $Snort_path ) {
  688. Help(
  689. "The file that you specified: $Snort_path does not exist! Please verify your configuration.\n"
  690. );
  691. }
  692. unless ( -f $Snort_config ) {
  693. Help(
  694. "The file that you specified: $Snort_config does not exist! Please verify your configuration.\n"
  695. );
  696. }
  697. }
  698. }
  699. print "\tDone\n" if !$Quiet;
  700. }
  701.  
  702. sub vrt_policy {
  703. my ( $ids_policy, $rule ) = @_;
  704. my ( $gid, $sid );
  705. if ( $rule =~ /policy\s$ids_policy/i && $rule !~ /flowbits\s*:\s*noalert/i )
  706. {
  707. $rule =~ s/^#*\s*//;
  708. }
  709. elsif ( $rule !~ /^\s*#/ ) {
  710. $rule = "# $rule";
  711. }
  712. return $rule;
  713. }
  714.  
  715. sub policy_set {
  716. my ( $ids_policy, $hashref ) = @_;
  717. if ($hashref) {
  718. if ( $ids_policy ne "Disabled" && $ids_policy ne "" ) {
  719. print "Activating $ids_policy rulesets....\n" if !$Quiet;
  720. foreach my $k ( sort keys %$hashref ) {
  721. foreach my $k2 ( keys %{ $$hashref{$k} } ) {
  722. $$hashref{$k}{$k2}{'rule'} =
  723. vrt_policy( $ids_policy, $$hashref{$k}{$k2}{'rule'} );
  724. }
  725. }
  726.  
  727. print "\tDone\n" if !$Quiet;
  728. }
  729. }
  730. }
  731.  
  732. ## this allows the user to use regular expressions to modify rule contents
  733. sub modify_sid {
  734. my ( $href, $file ) = @_;
  735. my @arry;
  736. print "Modifying Sids....\n" if !$Quiet;
  737. open( FH, "<$file" ) || carp "Unable to open $file\n";
  738. while (<FH>) {
  739. next if ( ( $_ =~ /^\s*#/ ) || ( $_ eq " " ) );
  740. if ( $_ =~ /([\d+|,|\*]*)\s+"(.+)"\s+"(.*)"/ ) {
  741. my ( $sids, $from, $to ) = ( $1, $2, $3 );
  742. @arry = split( /,/, $sids ) if $sids !~ /\*/;
  743. @arry = "*" if $sids =~ /\*/;
  744. foreach my $sid (@arry) {
  745. $sid = trim($sid);
  746. if ( $sid ne "*" && defined $$href{1}{$sid}{'rule'} ) {
  747. print "\tModifying SID:$sid from:$from to:$to\n"
  748. if ( $Verbose && !$Quiet );
  749. $$href{1}{$sid}{'rule'} =~ s/$from/$to/;
  750. }
  751. elsif ( $sid eq "*" ) {
  752. print "\tModifying ALL SIDS from:$from to:$to\n"
  753. if ( $Verbose && !$Quiet );
  754. foreach my $k ( sort keys %{ $$href{1} } ) {
  755. $$href{1}{$k}{'rule'} =~ s/$from/$to/;
  756. }
  757. }
  758. }
  759. undef @arry;
  760. }
  761. }
  762. print "\tDone!\n" if !$Quiet;
  763. close(FH);
  764. }
  765.  
  766. ## this relaces the enablesid, disablesid and dropsid functions..
  767. # speed ftw!
  768. sub modify_state {
  769. my ( $function, $SID_conf, $hashref, $rstate ) = @_;
  770. my ( @sid_mod, $sidlist);
  771. print "Processing $SID_conf....\n" if !$Quiet;
  772. print "\tSetting rules specified in $SID_conf to their default state!\n"
  773. if ( !$Quiet && $function eq 'enable' && $rstate );
  774. if ( -f $SID_conf ) {
  775. open( DATA, "$SID_conf" ) or carp "unable to open $SID_conf $!";
  776. while (<DATA>) {
  777. next unless ($_ !~ /^\s*#/ && $_ ne "");
  778. $sidlist = (split '#',$_)[0];
  779. chomp($sidlist);
  780. $sidlist = trim($sidlist);
  781. if (!@sid_mod )
  782. {
  783. @sid_mod = split( /,/, $sidlist );
  784. }
  785. elsif (@sid_mod)
  786. {
  787. push( @sid_mod, split( /,/, $sidlist ) );
  788. }
  789. }
  790. close(DATA);
  791. if ($hashref) {
  792. my $sidcount = 0;
  793. foreach (@sid_mod) {
  794.  
  795. # ranges
  796. if ( $_ =~ /^(\d+):\d+-\1:\d+/ ) {
  797. my ( $lsid, $usid ) = split( /-/, $& );
  798. my $gid = $lsid;
  799. $sid_mod[$sidcount] = $lsid;
  800. $gid =~ s/:\d+//;
  801. $lsid =~ s/\d+://;
  802. $usid =~ s/\d+://;
  803. while ( $lsid < $usid ) {
  804. $lsid++;
  805. push( @sid_mod, $gid . ':' . $lsid );
  806. }
  807. }
  808.  
  809. # pcres
  810. elsif ( $_ =~ /^pcre\:.+/i ) {
  811. my ( $pcre, $regex ) = split( /\:/, $& );
  812. foreach my $k1 ( keys %$hashref ) {
  813. foreach my $k2 ( keys %{ $$hashref{$k1} } ) {
  814. next unless defined $$hashref{$k1}{$k2}{'rule'};
  815. $sid_mod[$sidcount] = $k1 . ":" . $k2
  816. if (
  817. ( $$hashref{$k1}{$k2}{'rule'} =~ /($regex)/i )
  818. && ( $sid_mod[$sidcount] =~ /[a-xA-X](\w|\W)*/ )
  819. );
  820. push( @sid_mod, $k1 . ":" . $k2 )
  821. if (
  822. ( $$hashref{$k1}{$k2}{'rule'} =~ /($regex)/i )
  823. && ( $sid_mod[$sidcount] =~ /\d+:\d+/ ) );
  824. }
  825. }
  826. }
  827.  
  828. # specific sid
  829. elsif ( $_ =~ /^[a-xA-X]+\:.+/ ) {
  830. my $regex = $&;
  831. $regex =~ s/\:/,/;
  832. foreach my $k1 ( keys %$hashref ) {
  833. foreach my $k2 ( keys %{ $$hashref{$k1} } ) {
  834. next unless defined $$hashref{$k1}{$k2}{'rule'};
  835. $sid_mod[$sidcount] = $k1 . ":" . $k2
  836. if (
  837. ( $$hashref{$k1}{$k2}{'rule'} =~ /($regex)/i )
  838. && ( $sid_mod[$sidcount] =~ /[a-xA-X](\w|\W)*/ )
  839. );
  840. push( @sid_mod, $k1 . ":" . $k2 )
  841. if (
  842. ( $$hashref{$k1}{$k2}{'rule'} =~ /($regex)/i )
  843. && ( $sid_mod[$sidcount] =~ /\d+:\d+/ ) );
  844. }
  845. }
  846. }
  847.  
  848. # MS reference
  849. elsif ( $_ =~ /^MS\d+-.+/i ) {
  850. my $regex = $&;
  851. foreach my $k1 ( keys %$hashref ) {
  852. foreach my $k2 ( keys %{ $$hashref{$k1} } ) {
  853. next unless defined $$hashref{$k1}{$k2}{'rule'};
  854. $sid_mod[$sidcount] = $k1 . ":" . $k2
  855. if (
  856. ( $$hashref{$k1}{$k2}{'rule'} =~ /($regex)/i )
  857. && ( $sid_mod[$sidcount] =~ /[a-xA-X](\w|\W)*/ )
  858. );
  859. push( @sid_mod, $k1 . ":" . $k2 )
  860. if (
  861. ( $$hashref{$k1}{$k2}{'rule'} =~ /($regex)/i )
  862. && ( $sid_mod[$sidcount] =~ /\d+:\d+/ ) );
  863. }
  864. }
  865. }
  866.  
  867. # Category
  868. elsif ( $_ =~ /[a-xA-X]+(-|\w)*/ ) {
  869. my $category = $&;
  870. foreach my $k1 ( keys %$hashref ) {
  871. foreach my $k2 ( keys %{ $$hashref{$k1} } ) {
  872. next
  873. unless defined $$hashref{$k1}{$k2}{'category'};
  874. next
  875. unless $$hashref{$k1}{$k2}{'category'} =~
  876. /($category)/;
  877. $sid_mod[$sidcount] = $k1 . ":" . $k2;
  878. push( @sid_mod, $k1 . ":" . $k2 )
  879. if $sid_mod[$sidcount] =~ /\d+:\d+/;
  880. }
  881. }
  882. }
  883. $sidcount++;
  884. }
  885. $sidcount = 0;
  886. foreach (@sid_mod) {
  887. if ( $_ =~ /^\d+:\d+/ ) {
  888. my $gid = $&;
  889. my $sid = $gid;
  890. if ( $gid && $sid ) {
  891. $gid =~ s/:\d+//;
  892. $sid =~ s/\d+://;
  893. if ($function) {
  894. if ($function eq "enable") {
  895. if ( exists $$hashref{$gid}{$sid}
  896. && $$hashref{$gid}{$sid}{'rule'} =~
  897. /^\s*#\s*(alert|drop|pass)/i
  898. && !$rstate )
  899. {
  900. $$hashref{$gid}{$sid}{'rule'} =~
  901. s/^\s*#+\s*//;
  902. if ( $Verbose && !$Quiet ) {
  903. print "\tEnabled $gid:$sid\n";
  904. }
  905. $sidcount++;
  906. }
  907.  
  908. # Return State!
  909. next unless $$hashref{$gid}{$sid};
  910. next unless $$hashref{$gid}{$sid}{rule};
  911. if ( $$hashref{$gid}{$sid}{'state'} && $$hashref{$gid}{$sid}{'state'} == 0
  912. && $$hashref{$gid}{$sid}{'rule'} =~
  913. /^\s*(alert|drop|pass)/
  914. && $rstate )
  915. {
  916. $$hashref{$gid}{$sid}{'rule'} =
  917. "# " . $$hashref{$gid}{$sid}{'rule'};
  918. $sidcount++;
  919. if ( $Verbose && !$Quiet ) {
  920. print "\tRe-Disabled $gid:$sid\n";
  921. }
  922. }
  923. elsif ( $$hashref{$gid}{$sid}{'state'} && $$hashref{$gid}{$sid}{'state'} == 1
  924. && $$hashref{$gid}{$sid}{'rule'} =~
  925. /^\s*#+\s*(alert|drop|pass)/
  926. && $rstate )
  927. {
  928. $$hashref{$gid}{$sid}{'rule'} =~
  929. s/^\s*#+\s*//;
  930. $sidcount++;
  931. if ( $Verbose && !$Quiet ) {
  932. print "\tRe-Enabled $gid:$sid\n";
  933. }
  934. }
  935. }
  936. elsif ($function eq "drop") {
  937. if ( exists $$hashref{$gid}{$sid}
  938. && $$hashref{$gid}{$sid}{'rule'} =~
  939. /^\s*#*\s*alert/i )
  940. {
  941. $$hashref{$gid}{$sid}{'rule'} =~
  942. s/^\s*#*\s*//;
  943. $$hashref{$gid}{$sid}{'rule'} =~
  944. s/^alert/drop/;
  945. if ( $Verbose && !$Quiet ) {
  946. print "\tWill drop $gid:$sid\n";
  947. }
  948. $sidcount++;
  949. }
  950. }
  951. elsif ($function eq "disable") {
  952. if ( exists $$hashref{$gid}{$sid}
  953. && $$hashref{$gid}{$sid}{'rule'} =~
  954. /^\s*(alert|drop|pass)/i )
  955. {
  956. $$hashref{$gid}{$sid}{'rule'} =
  957. "# " . $$hashref{$gid}{$sid}{'rule'};
  958. $$hashref{$gid}{$sid}{'disabled'} = 1;
  959. if ( $Verbose && !$Quiet ) {
  960. print "\tDisabled $gid:$sid\n";
  961. }
  962. $sidcount++;
  963. }
  964. elsif ( exists $$hashref{$gid}{$sid}
  965. && $$hashref{$gid}{$sid}{'rule'} =~
  966. /^\s*#*\s*(alert|drop|pass)/i )
  967. {
  968. $$hashref{$gid}{$sid}{'disabled'} = 1;
  969. }
  970. }
  971. }
  972. }
  973. }
  974. }
  975. print "\tModified $sidcount rules\n" if !$Quiet;
  976. }
  977. }
  978. print "\tDone\n" if !$Quiet;
  979. undef @sid_mod;
  980. }
  981.  
  982. ## iprep control socket!
  983. sub iprep_control {
  984. my ($bin,$path) = @_;
  985. return unless -f $bin;
  986. my $cmd = "$bin $path 1361";
  987. return unless (-f $bin && -f $path);
  988. print "Issuing reputation socket reload command\n";
  989. print "Command: $cmd\n" if $Verbose;
  990. open(FH,"$cmd 2>&1 |");
  991. while(<FH>){
  992. chomp();
  993. next unless $_ =~ /(warn|err|unable)/i;
  994. print "$_\n";
  995. }
  996. close(FH);
  997. }
  998.  
  999. ## goodbye
  1000. sub sig_hup {
  1001. my ($pidlist) = @_;
  1002. my @pids = split( /,/, $pidlist );
  1003. my $pid;
  1004. print "HangUP Time....\n";
  1005. foreach $pid (@pids) {
  1006. open( FILE, "$pid" )
  1007. or croak $!;
  1008. my $realpid = <FILE>;
  1009. chomp($realpid);
  1010. close(FILE);
  1011. my $hupres = kill 1, $realpid;
  1012. if ( $Verbose && !$Quiet ) {
  1013. print
  1014. "\tSent kill signal to $realpid from $pid with result $hupres\n";
  1015. }
  1016. }
  1017. if ( !$Verbose && !$Quiet ) { print "\tDone!\n"; }
  1018. undef @pids;
  1019. }
  1020.  
  1021. ## make the sid-msg.map
  1022. sub sid_msg {
  1023. my ( $ruleshash, $sidhash, $enonly ) = @_;
  1024. my ( $gid, $arg, $msg );
  1025. print "Generating sid-msg.map....\n" if !$Quiet;
  1026. foreach my $k ( sort keys %$ruleshash ) {
  1027. foreach my $k2 ( sort keys %{ $$ruleshash{$k} } ) {
  1028. next if ((defined $enonly) && $$ruleshash{$k}{$k2}{'rule'} !~ /^\s*(alert|drop|pass)/);
  1029. ( my $header, my $options ) =
  1030. split( /^[^"]* \(\s*/, $$ruleshash{$k}{$k2}{'rule'} )
  1031. if defined $$ruleshash{$k}{$k2}{'rule'};
  1032. my @optarray = split( /(?<!\\);\s*/, $options ) if $options;
  1033. foreach my $option ( reverse(@optarray) ) {
  1034. my ( $kw, $arg ) = split( /:\s*/, $option ) if $option;
  1035. my $gid = $k;
  1036. $gid = 1 if $k == 0;
  1037. next unless ($kw && $arg && $kw =~ /(reference|msg|rev|classtype|priority)/);
  1038. if ( $kw eq "reference" ) {
  1039. push( @{ $$sidhash{$gid}{$k2}{refs} }, trim($arg));
  1040. } elsif ($kw eq "msg"){
  1041. $arg =~ s/"//g;
  1042. $$sidhash{$gid}{$k2}{msg} = trim($arg);
  1043. } elsif ($kw eq "rev"){
  1044. $$sidhash{$gid}{$k2}{rev} = trim($arg);
  1045. } elsif ($kw eq "classtype") {
  1046. $$sidhash{$gid}{$k2}{classtype} = trim($arg);
  1047. } elsif ($kw eq "priority") {
  1048. $$sidhash{$gid}{$k2}{priority} = trim($arg);
  1049. }
  1050. }
  1051. }
  1052. }
  1053. print "\tDone\n" if !$Quiet;
  1054. }
  1055.  
  1056. ## write the rules files to unique output files!
  1057. sub rule_category_write {
  1058. my ( $hashref, $filepath, $enonly ) = @_;
  1059. print "Writing rules to unique destination files....\n" if !$Quiet;
  1060. print "\tWriting rules to $filepath\n" if !$Quiet;
  1061.  
  1062. my %hcategory = ();
  1063. my $file;
  1064. foreach my $k (sort keys %$categories){
  1065. my $file = "$k.rules";
  1066. open( WRITE,'>',"$filepath$file" );
  1067. print WRITE "\n\n# ----- Begin $k Rules Category ----- #\n";
  1068. foreach my $k2 (sort keys %{$categories->{$k}}) {
  1069. print WRITE "\n# -- Begin GID:$k2 Based Rules -- #\n\n";
  1070. foreach my $k3 (sort @{$categories->{$k}{$k2}}){
  1071. next unless defined $$hashref{$k2}{$k3}{'rule'};
  1072. if ( $enonly
  1073. && $$hashref{$k2}{$k3}{'rule'} =~ /^\s*(alert|drop|pass)/ )
  1074. {
  1075. print WRITE $$hashref{$k2}{$k3}{'rule'} . "\n";
  1076. }
  1077. elsif ( !$enonly ) {
  1078. print WRITE $$hashref{$k2}{$k3}{'rule'} . "\n";
  1079. }
  1080. }
  1081. }
  1082. close(WRITE);
  1083. }
  1084. print "\tDone\n" if !$Quiet;
  1085. }
  1086.  
  1087. ## write our blacklist and blacklist version file!
  1088. sub blacklist_write{
  1089. my ($href,$path) = @_;
  1090. my $blv = $Config_info{'IPRVersion'}."IPRVersion.dat";
  1091. my $blver = 0;
  1092.  
  1093. # First lets be sure that our data is new, if not skip the rest of it!
  1094. # We will MD5 our HREF then convert it to an integer.
  1095. my $hobj = Digest::MD5->new;
  1096. $hobj->add(%$href);
  1097. my $hash = $hobj->hexdigest;
  1098. my $ver = unpack ("i",$hash);
  1099.  
  1100. if (-f $blv){
  1101. open(FH,'<',$blv);
  1102. while(<FH>){
  1103. next unless $_ =~ /\d+/;
  1104. $blver = $_;
  1105. }
  1106. close(FH);
  1107. }
  1108.  
  1109. if ($blver != $ver){
  1110. print "Writing Blacklist File $path....\n" if !$Quiet;
  1111. open (FH,'>',$path) || croak("Unable to open $path for writing! - $!\n");
  1112. foreach (sort keys %$href){
  1113. print FH "$_\n";
  1114. }
  1115. close(FH);
  1116.  
  1117. print "Writing Blacklist Version $ver to $blv....\n" if !$Quiet;
  1118. open (FH,'>',$blv) || croak("Unable to open $blv for writing! - $!\n");
  1119. print FH $ver;
  1120. close(FH);
  1121. return(1);
  1122. } else {
  1123. print "Blacklist version is unchanged, not updating!\n" if !$Quiet;
  1124. return(0);
  1125. }
  1126.  
  1127. }
  1128.  
  1129. ## write the rules to a single output file!
  1130. sub rule_write {
  1131. my ( $hashref, $file, $enonly ) = @_;
  1132. print "Writing $file....\n" if !$Quiet;
  1133. open( WRITE,'>',"$file" ) || croak "Unable to write $file - $!\n";
  1134. #if ( $gid == 1 ) {
  1135. foreach my $k (sort keys %$categories){
  1136. print WRITE "\n\n# ----- Begin $k Rules Category ----- #\n";
  1137. foreach my $k2 (sort keys %{$categories->{$k}}) {
  1138. print WRITE "\n# -- Begin GID:$k2 Based Rules -- #\n\n";
  1139. foreach my $k3 (sort @{$categories->{$k}{$k2}}){
  1140. next unless defined $$hashref{$k2}{$k3}{'rule'};
  1141. if ( $enonly
  1142. && $$hashref{$k2}{$k3}{'rule'} =~ /^\s*(alert|drop|pass)/ )
  1143. {
  1144. print WRITE $$hashref{$k2}{$k3}{'rule'} . "\n";
  1145. }
  1146. elsif ( !$enonly ) {
  1147. print WRITE $$hashref{$k2}{$k3}{'rule'} . "\n";
  1148. }
  1149. }
  1150. }
  1151. }
  1152. close(WRITE);
  1153. print "\tDone\n" if !$Quiet;
  1154. }
  1155.  
  1156. ## sid file time!
  1157. sub sid_write {
  1158. my ( $hashref, $file, $sid_msg_version ) = @_;
  1159. print "Writing v$sid_msg_version $file....\n" if !$Quiet;
  1160. open( WRITE, ">$file" ) || croak "Unable to write $file -$!";
  1161. print WRITE "#v$sid_msg_version\n"; # Version biznits!
  1162. print WRITE "# sid-msg.map autogenerated by PulledPork - DO NOT MODIFY BY HAND!\n";
  1163. foreach my $k ( sort keys %$hashref ) {
  1164. foreach my $k2 ( sort keys %{$$hashref{$k}}){
  1165. if ($sid_msg_version == 2){
  1166. print WRITE "$k || $k2 || $hashref->{$k}{$k2}{rev} || ";
  1167. if ($hashref->{$k}{$k2}{classtype}) {
  1168. print WRITE "$hashref->{$k}{$k2}{classtype} || ";
  1169. }
  1170. else { print WRITE "NOCLASS || "; }
  1171. if ($hashref->{$k}{$k2}{priority}) {
  1172. print WRITE "$hashref->{$k}{$k2}{priority} || ";
  1173. }
  1174. else { print WRITE "0 || "; }
  1175. } else {
  1176. print WRITE "$k2 || ";
  1177. }
  1178. print WRITE "$hashref->{$k}{$k2}{msg}";
  1179. foreach (@{$hashref->{$k}{$k2}{refs}}) {
  1180. print WRITE " || $_";
  1181. }
  1182. print WRITE "\n";
  1183. }
  1184. }
  1185. close(WRITE);
  1186. print "\tDone\n" if !$Quiet;
  1187. }
  1188.  
  1189. ## Pull the flowbits requirements from the currently enabled rules.
  1190. # TODO: add extended functionality for setx, toggle and groups (Q1 of 2013)
  1191. sub flowbit_check {
  1192. my ( $rule, $aref ) = @_;
  1193. my ( $header, $options ) = split( /^[^"]* \(\s*/, $rule );
  1194. my @optarray = split( /(?<!\\);\s*/, $options ) if $options;
  1195. foreach my $option ( reverse(@optarray) ) {
  1196. my ( $kw, $arg ) = split( /:/, $option ) if $option;
  1197. next unless ( $kw && $arg && $kw eq "flowbits" );
  1198. my ( $flowact, $flowbit ) = split( /,/, $arg );
  1199. next unless $flowact =~ /is(not)?set/i;
  1200. push( @$aref, trim($flowbit) ) if $flowbit !~ /(&|\|)/;
  1201. push( @$aref, split(/(&|\|)/,trim($flowbit))) if $flowbit =~ /(&|\|)/;
  1202. }
  1203. }
  1204.  
  1205. ## Enable flowbits if there is a rule that requires them!
  1206. sub flowbit_set {
  1207. my $href = shift;
  1208. my $counter = 0;
  1209. my @flowbits;
  1210. foreach my $k1 ( keys %$href ) {
  1211. foreach my $k2 ( keys %{ $$href{$k1} } ) {
  1212. next unless $$href{$k1}{$k2}{'rule'} =~ /^(alert|drop|pass)/;
  1213. next
  1214. unless $$href{$k1}{$k2}{'rule'} =~
  1215. /flowbits:\s*is(not)?set\s*,\s*[^;]+/i;
  1216. flowbit_check( $$href{$k1}{$k2}{'rule'}, \@flowbits );
  1217. }
  1218. }
  1219. my %dups;
  1220. map { $dups{$_} = 1 } @flowbits;
  1221. @flowbits = keys %dups;
  1222. undef %dups;
  1223. foreach my $k1 ( keys %$href ) {
  1224. foreach my $k2 ( keys %{ $$href{$k1} } ) {
  1225. foreach my $flowbit (@flowbits) {
  1226. next
  1227. unless defined $$href{$k1}{$k2}{$flowbit}
  1228. && $$href{$k1}{$k2}{'rule'} =~
  1229. /^\s*#\s*(alert|drop|pass)/i;
  1230. $$href{$k1}{$k2}{'rule'} =~ s/^\s*#\s*//;
  1231. if ( defined $$href{$k1}{$k2}{'disabled'} ) {
  1232. print "\tWARN - $k1:$k2 is re-enabled by a",
  1233. " check of the $flowbit flowbit!\n"
  1234. if $Verbose && !$Quiet;
  1235. }
  1236. $counter++;
  1237. }
  1238. }
  1239. }
  1240. undef @flowbits;
  1241. print "\tEnabled $counter flowbits\n" if ( $counter > 0 && !$Quiet );
  1242. return $counter;
  1243. }
  1244.  
  1245. ## Make some changelog fun!
  1246. sub changelog {
  1247. my ( $changelog, $hashref, $hashref2, $hashref3, $ips_policy, $enonly, $hmatch ) = @_;
  1248.  
  1249. print "Writing $changelog....\n" if !$Quiet;
  1250. my ( @newsids, @delsids );
  1251. undef @newsids;
  1252. undef @delsids;
  1253. my $rt = 0;
  1254. my $dt = 0;
  1255. my $dropped = 0;
  1256. my $enabled = 0;
  1257. my $disabled = 0;
  1258. my $ips = 0;
  1259.  
  1260. foreach my $k1 ( keys %rules_hash ) {
  1261.  
  1262. foreach my $k2 ( keys %{ $$hashref{$k1} } ) {
  1263. next if ( ($enonly) && ( $$hashref{$k1}{$k2}{'rule'} =~ /^\s*#/ ) );
  1264. if ( !defined $$hashref2{$k1}{$k2}{'rule'} ) {
  1265. my $msg_holder = $$hashref{$k1}{$k2}{'rule'};
  1266. if ( $msg_holder =~ /msg:"[^"]+";/i ) {
  1267. $msg_holder = $&;
  1268. $msg_holder =~ s/msg:"//;
  1269. $msg_holder =~ s/";//;
  1270. }
  1271. else { $msg_holder = "Unknown MSG" }
  1272. push( @newsids, "$msg_holder ($k1:$k2)" );
  1273. }
  1274. $rt++ unless defined $$hashref2{$k1}{$k2}{'rule'};
  1275. next unless defined $$hashref{$k1}{$k2}{'rule'};
  1276. if ( $$hashref{$k1}{$k2}{'rule'} =~ /^\s*(alert|pass)/ ) {
  1277. $enabled++;
  1278. }
  1279. elsif ( $$hashref{$k1}{$k2}{'rule'} =~ /^\s*drop/ ) {
  1280. $dropped++;
  1281. }
  1282. elsif (
  1283. $$hashref{$k1}{$k2}{'rule'} =~ /^\s*#*\s*(alert|drop|pass)/ )
  1284. {
  1285. $disabled++;
  1286. }
  1287. }
  1288. }
  1289. foreach my $k1 ( sort keys %$hashref2 ) {
  1290. for my $k2 ( sort keys %{ $$hashref2{$k1} } ) {
  1291. next if defined $$hashref{$k1}{$k2}{'rule'};
  1292. next
  1293. if ( ($enonly) && ( $$hashref2{$k1}{$k2}{'rule'} =~ /^\s*#/ ) );
  1294. my $msg_holder = $$hashref2{$k1}{$k2}{'rule'};
  1295. if ( $msg_holder =~ /msg:"[^"]+";/ ) {
  1296. $msg_holder = $&;
  1297. $msg_holder =~ s/msg:"//;
  1298. $msg_holder =~ s/";//;
  1299. }
  1300. else { $msg_holder = "Unknown MSG" }
  1301. push( @delsids, "$msg_holder ($k1:$k2)" );
  1302. $dt++;
  1303. }
  1304. }
  1305. if (%$hashref3){
  1306. $ips = keys(%$hashref3);
  1307. }
  1308. if ( -f $changelog ) {
  1309. open( WRITE,'>>',$changelog ) || croak "$changelog $!\n";
  1310. }
  1311. else {
  1312. open( WRITE,'>',$changelog ) || croak "$changelog $!\n";
  1313. print WRITE
  1314. "-=BEGIN PULLEDPORK SNORT RULES CHANGELOG, Tracking started on "
  1315. . gmtime(time)
  1316. . " GMT=-\n\n\n";
  1317. }
  1318. print WRITE "\n-=Begin Changes Logged for " . gmtime(time) . " GMT=-\n";
  1319. if ($Process) {
  1320. print WRITE "\nNew Rules\n" if @newsids;
  1321. @newsids = sort(@newsids);
  1322. @delsids = sort(@delsids);
  1323. foreach (@newsids) { print WRITE "\t" . $_ . "\n"; }
  1324. print WRITE "\nDeleted Rules\n" if @delsids;
  1325. foreach (@delsids) { print WRITE "\t" . $_ . "\n"; }
  1326. print WRITE "\nSet Policy: $ips_policy\n" if $ips_policy;
  1327. print WRITE "\nRule Totals\n";
  1328. print WRITE "\tNew:-------$rt\n";
  1329. print WRITE "\tDeleted:---$dt\n";
  1330. print WRITE "\tEnabled:---$enabled\n";
  1331. print WRITE "\tDropped:---$dropped\n";
  1332. print WRITE "\tDisabled:--$disabled\n";
  1333. print WRITE "\tTotal:-----" . ( $enabled + $disabled + $dropped ) . "\n";
  1334. } else { print WRITE "\nNo Rule Changes\n"; }
  1335. if ($bmatch){
  1336. print WRITE "\nIP Blacklist Stats\n\tTotal IPs:-----$ips\n" if $ips;
  1337. } else { print WRITE "\nNo IP Blacklist Changes\n"; }
  1338. print WRITE "\n-=End Changes Logged for " . gmtime(time) . " GMT=-\n";
  1339. close(WRITE);
  1340.  
  1341. if ( !$Quiet ) {
  1342. print "\tDone\n";
  1343. if ($Process) {
  1344. print "Rule Stats...\n";
  1345. print "\tNew:-------$rt\n";
  1346. print "\tDeleted:---$dt\n";
  1347. print "\tEnabled Rules:----$enabled\n";
  1348. print "\tDropped Rules:----$dropped\n";
  1349. print "\tDisabled Rules:---$disabled\n";
  1350. print "\tTotal Rules:------"
  1351. . ( $enabled + $dropped + $disabled );
  1352. } else { print "\nNo Rule Changes\n"; }
  1353. if ($bmatch){
  1354. print "\nIP Blacklist Stats...\n\tTotal IPs:-----$ips\n" if $ips;
  1355. } else { print "\nNo IP Blacklist Changes\n"; }
  1356. print "\nDone\n";
  1357. print "Please review $sid_changelog for additional details\n"
  1358. if $sid_changelog;
  1359. }
  1360. undef @newsids;
  1361. undef @delsids;
  1362. }
  1363.  
  1364. ## Trim it up, loves the trim!
  1365. sub trim {
  1366. my ($trimmer) = @_;
  1367. if ($trimmer) {
  1368. $trimmer =~ s/^\s*//;
  1369. $trimmer =~ s/\s*$//;
  1370. return $trimmer;
  1371. }
  1372. }
  1373.  
  1374. ## Does it hurt when I slash you?
  1375. sub slash {
  1376. my ( $operation, $string ) = @_;
  1377. if ( $operation == 0 && $string =~ /\/$/ && $string ne "" ) {
  1378. $string =~ s/\/$//;
  1379. }
  1380. elsif ( $operation == 1 && $string !~ /\/$/ && $string ne "" ) {
  1381. $string = $string . "/";
  1382. }
  1383. return $string;
  1384. }
  1385.  
  1386. ## uh, yeah
  1387. sub Version {
  1388. print("$VERSION\n\n");
  1389. exit(0);
  1390. }
  1391.  
  1392. ## find the snort version baby!
  1393. sub snort_version {
  1394. my $cmd = shift;
  1395. $cmd .= " -V";
  1396. my $version;
  1397. open( FH, "$cmd 2>&1 |" );
  1398. while (<FH>) {
  1399. next unless $_ =~ /Version/;
  1400. if ( $_ =~ /\d\.\d\.\d\.\d/ ) {
  1401. $version = $&;
  1402. }
  1403. elsif ( $_ =~ /\d\.\d\.\d/ ) {
  1404. $version = $& . ".0";
  1405. }
  1406. }
  1407. close(FH);
  1408. return $version;
  1409. }
  1410.  
  1411. ## our arch
  1412. sub get_arch {
  1413. my $cmd = "uname -a";
  1414. open( FH, "$cmd |" );
  1415. my $arch;
  1416. while (<FH>) {
  1417. next unless $_ =~ /(i386|x86-64|x86_64|i686|amd64)/i;
  1418. $arch = $&;
  1419. $arch =~ s/_/-/;
  1420. $arch =~ s/i686/i386/;
  1421. $arch =~ s/amd64/x86-64/;
  1422. }
  1423. close(FH);
  1424. return $arch;
  1425. }
  1426.  
  1427. ## log to syslog
  1428. sub syslogit {
  1429. my ( $level, $msg ) = @_;
  1430.  
  1431. openlog( 'pulledpork', 'ndelay,pid', 'local0' );
  1432. syslog( $level, $msg );
  1433. closelog;
  1434. }
  1435.  
  1436. ## Create some backup and archive foo!
  1437. sub archive {
  1438. my ( $data, $filename ) = @_;
  1439. my @records;
  1440. my $compression = "COMPRESS_GZIP";
  1441. $filename .= "." . time() . ".tgz";
  1442. print "Creating backup at: $filename\n" unless $Quiet;
  1443. foreach my $record (@$data) {
  1444. if ( -f $record ) {
  1445. print "\tAdding file: $record\n" if $Verbose && !$Quiet;
  1446. push( @records, $record );
  1447. }
  1448. elsif ( -d $record ) {
  1449. print "\tAdding dir: $record\n" if $Verbose && !$Quiet;
  1450. find( \&archive_wanted, $record );
  1451. }
  1452. }
  1453. print "\tWriting Archive: $filename - may take several minutes!\n"
  1454. if $Verbose && !$Quiet;
  1455. Archive::Tar->create_archive( $filename, $compression, @records );
  1456. }
  1457.  
  1458. ## Define what we will find for the archive sub when dir is found!
  1459. sub archive_wanted {
  1460. return unless -f $_;
  1461. print "\tAdding file: $File::Find::name\n" if $Verbose == 2 && !$Quiet;
  1462. push( @records, $File::Find::name );
  1463. }
  1464.  
  1465. ###
  1466. ### Main here, let's get on with it already
  1467. ###
  1468.  
  1469. if ( $#ARGV == -1 ) {
  1470. Help(
  1471. "Please read the README for runtime options and configuration documentation"
  1472. );
  1473. }
  1474.  
  1475. ## Lets grab any runtime values and insert into our variables using getopt::long
  1476. GetOptions(
  1477. "a!" => \$Auto,
  1478. "b=s" => \$sidmod{drop},
  1479. "c=s" => \$Config_file,
  1480. "C=s" => \$Snort_config,
  1481. "d!" => \$Hash,
  1482. "D=s" => \$Distro,
  1483. "E!" => \$enonly,
  1484. "e=s" => \$sidmod{enable},
  1485. "g!" => \$grabonly,
  1486. "H!" => \$SigHup,
  1487. "h=s" => \$sid_changelog,
  1488. "i=s" => \$sidmod{disable},
  1489. "I=s" => \$ips_policy,
  1490. "k!" => \$keep_rulefiles,
  1491. "K=s" => \$rule_file_path,
  1492. "l!" => \$Syslogging,
  1493. "L=s" => \$local_rules,
  1494. "M=s" => \$sidmod{modify},
  1495. "m=s" => \$sid_msg_map,
  1496. "n!" => \$NoDownload,
  1497. "o=s" => \$Output,
  1498. "p=s" => \$Snort_path,
  1499. "P!" => \$Process,
  1500. "q" => \$Quiet,
  1501. "R!" => \$rstate,
  1502. "r=s" => \$docs,
  1503. "S=s" => \$Snort,
  1504. "s=s" => \$Sorules,
  1505. "T!" => \$Textonly,
  1506. "u=s" => \@base_url,
  1507. "V!" => sub { Version() },
  1508. "v+" => \$Verbose,
  1509. "help|?" => sub { Help() }
  1510. );
  1511.  
  1512. ## Fly piggy fly!
  1513. pulledpork() if !$Quiet;
  1514.  
  1515. # Dump our variables for verbose/debug output
  1516.  
  1517. if ( !$Config_file ) { Help("No configuration file specified"); }
  1518.  
  1519. # Call the subroutine to fetch config values
  1520. parse_config_file( $Config_file, \%Config_info );
  1521.  
  1522. if ( $Verbose && !$Quiet ) {
  1523. print "Config File Variable Debug $Config_file\n";
  1524. foreach $Config_key ( keys %Config_info ) {
  1525. if ( $Config_info{$Config_key} ) {
  1526. print "\t$Config_key = $Config_info{$Config_key}\n";
  1527. }
  1528. }
  1529.  
  1530. }
  1531.  
  1532. if ( exists $Config_info{'version'} ) {
  1533. croak "You are not using the current version of pulledpork.conf!\n",
  1534. "Please use the version of pulledpork.conf that shipped with $VERSION!\n\n"
  1535. if $Config_info{'version'} ne "0.7.0";
  1536. }
  1537. else {
  1538. croak
  1539. "You are not using the current version of pulledpork.conf!\nPlease use the version that shipped with $VERSION!\n\n";
  1540. }
  1541.  
  1542. # Check to see if we have command line inputs, if so, they super-seed any config file values!
  1543. # We also begin sub execution here
  1544.  
  1545. $pid_path = ( $Config_info{'pid_path'} ) if exists $Config_info{'pid_path'};
  1546. $ignore_files = ( $Config_info{'ignore'} ) if exists $Config_info{'ignore'};
  1547.  
  1548. if ($rule_file_path) {
  1549. $keep_rulefiles = 1;
  1550. }
  1551.  
  1552. $sid_msg_version = $Config_info{'sid_msg_version'};
  1553.  
  1554. if ( $keep_rulefiles && defined $Config_info{'out_path'} && !$rule_file_path ) {
  1555. $rule_file_path = $Config_info{'out_path'};
  1556. }
  1557.  
  1558. if ($rule_file_path) {
  1559. $rule_file_path = slash( 1, "$rule_file_path" );
  1560. }
  1561.  
  1562. if ( !$ips_policy && defined $Config_info{'ips_policy'} ) {
  1563. $ips_policy = $Config_info{'ips_policy'};
  1564. }
  1565.  
  1566. if ( !$black_list && defined $Config_info{'black_list'} ) {
  1567. $black_list = $Config_info{'black_list'};
  1568. }
  1569.  
  1570. if ( !$sidmod{enable} && defined $Config_info{'enablesid'} ) {
  1571. $sidmod{enable} = $Config_info{'enablesid'};
  1572. }
  1573.  
  1574. if ( !$sidmod{modify} && defined $Config_info{'modifysid'} ) {
  1575. $sidmod{modify} = $Config_info{'modifysid'};
  1576. }
  1577.  
  1578. if ( !$sidmod{drop} && defined $Config_info{'dropsid'} ) {
  1579. $sidmod{drop} = $Config_info{'dropsid'};
  1580. }
  1581.  
  1582. if ( !$sidmod{disable} && defined $Config_info{'disablesid'} ) {
  1583. $sidmod{disable} = $Config_info{'disablesid'};
  1584. }
  1585.  
  1586. my @sidact = ( 'enable', 'drop', 'disable' );
  1587.  
  1588. if ( defined $Config_info{'state_order'} ) {
  1589. (@sidact) = split( /,/, $Config_info{'state_order'} );
  1590. }
  1591.  
  1592. if ( !@base_url ) {
  1593. @base_url = @{ $Config_info{'rule_url'} };
  1594. if ( !@base_url ) {
  1595. Help(
  1596. "You need to specify one rule_url at a minimum to fetch the rules files from!\n"
  1597. );
  1598. }
  1599. }
  1600.  
  1601. if ( !$Output ) {
  1602. $Output = ( $Config_info{'rule_path'} );
  1603. if ( !$Output ) { Help("You need to specify an output rules file!"); }
  1604. }
  1605. $Output = slash( 0, $Output );
  1606.  
  1607. if ( !$Sorules ) {
  1608. $Sorules = ( $Config_info{'sorule_path'} );
  1609. }
  1610. $Sorules = slash( 1, $Sorules ) if $Sorules;
  1611.  
  1612. if ( !$docs ) {
  1613. $docs = ( $Config_info{'docs'} );
  1614. }
  1615.  
  1616. undef $Sostubs if ($Textonly);
  1617. undef $Sorules if ($Textonly);
  1618.  
  1619. if ( !$Distro ) {
  1620. $Distro = ( $Config_info{'distro'} );
  1621. }
  1622.  
  1623. if ( !$Snort ) {
  1624. $Snort = ( $Config_info{'snort_version'} );
  1625. }
  1626.  
  1627. if ( !$Snort_path ) {
  1628. $Snort_path = ( $Config_info{'snort_path'} );
  1629. $Snort = snort_version($Snort_path) if ( -B $Snort_path && !$Snort );
  1630. $arch = get_arch();
  1631. $Textonly = 1 unless $Snort;
  1632. }
  1633.  
  1634. if ( !$local_rules && ( $Config_info{'local_rules'} ) ) {
  1635. $local_rules = ( $Config_info{'local_rules'} );
  1636. }
  1637. elsif ( !$local_rules && !( $Config_info{'local_rules'} ) ) {
  1638. $local_rules = 0;
  1639. }
  1640.  
  1641. if ( !$Snort_config ) {
  1642. $Snort_config = ( $Config_info{'config_path'} );
  1643. }
  1644.  
  1645. if ( !$sid_msg_map ) {
  1646. $sid_msg_map = ( $Config_info{'sid_msg'} );
  1647. }
  1648. if ( !$sid_changelog ) {
  1649. $sid_changelog = ( $Config_info{'sid_changelog'} );
  1650. }
  1651.  
  1652. if ( !$ips_policy ) {
  1653. $ips_policy = "Disabled";
  1654. }
  1655.  
  1656. if ( $Verbose && !$Quiet ) {
  1657. print "MISC (CLI and Autovar) Variable Debug:\n";
  1658. if ($Process) { print "\tProcess flag specified!\n"; }
  1659. if ($arch) { print "\tarch Def is: $arch\n"; }
  1660. if ($Config_file) { print "\tConfig Path is: $Config_file\n"; }
  1661. if ($Distro) { print "\tDistro Def is: $Distro\n"; }
  1662. if ($docs) { print "\tDocs Reference Location is: $docs\n"; }
  1663. if ($keep_rulefiles) { print "\tKeep rulefiles flag is Set\n"; }
  1664. if ($rule_file_path) { print "\tKeep rulefiles path: $rule_file_path\n"; }
  1665. if ($enonly) { print "\tWrite ONLY enabled rules flag is Set\n"; }
  1666. if ($grabonly) { print "\tgrabonly Flag is Set, only gonna download!"; }
  1667.  
  1668. if ($Hash) {
  1669. print
  1670. "\tNo MD5 Flag is Set, uhm, ok? I'm gonna fetch the latest file no matter what!\n";
  1671. }
  1672. if ($ips_policy) { print "\t$ips_policy policy specified\n"; }
  1673. if ($local_rules) { print "\tlocal.rules path is: $local_rules\n"; }
  1674. if ($NoDownload) { print "\tNo Download Flag is Set\n"; }
  1675. if ($Output) { print "\tRules file is: $Output\n"; }
  1676. if ($rstate) { print "\tReturn State flag is Set\n"; }
  1677. if ($rule_file) { print "\tRule File is: $rule_file\n"; }
  1678. if ( $sidmod{disable} ) {
  1679. print "\tPath to disablesid file: $sidmod{disable}\n";
  1680. }
  1681. if ( $sidmod{drop} ) { print "\tPath to dropsid file: $sidmod{drop}\n"; }
  1682. if ( $sidmod{enable} ) {
  1683. print "\tPath to enablesid file: $sidmod{enable}\n";
  1684. }
  1685. if ( $sidmod{modify} ) {
  1686. print "\tPath to modifysid file: $sidmod{modify}\n";
  1687. }
  1688. if ($sid_changelog) {
  1689. print "\tsid changes will be logged to: $sid_changelog\n";
  1690. }
  1691. if ($sid_msg_map) { print "\tsid-msg.map Output Path is: $sid_msg_map\n"; }
  1692. if ($SigHup) { print "\tSIGHUP Flag is Set\n"; }
  1693. if ($Snort) { print "\tSnort Version is: $Snort\n"; }
  1694. if ($Snort_config) { print "\tSnort Config File: $Snort_config\n"; }
  1695. if ($Snort_path) { print "\tSnort Path is: $Snort_path\n"; }
  1696. if ($Sorules) { print "\tSO Output Path is: $Sorules\n"; }
  1697. if ($Sostubs) { print "\tWill process SO rules\n"; }
  1698. if ($Syslogging) { print "\tLogging Flag is Set\n"; }
  1699. if ($Textonly) { print "\tText Rules only Flag is Set\n"; }
  1700. if ( $Verbose == 2 ) { print "\tExtra Verbose Flag is Set\n"; }
  1701. if ($Verbose) { print "\tVerbose Flag is Set\n"; }
  1702. if (@base_url) { print "\tBase URL is: @base_url\n"; }
  1703. }
  1704.  
  1705. # We need a temp path to work with the files while we do magics on them.. make sure you have plenty
  1706. # of space in this path.. ~200mb is a good starting point
  1707. $temp_path = ( $Config_info{'temp_path'} );
  1708. if ( !$temp_path ) {
  1709. Help("You need to specify a valid temp path, check permissions too!");
  1710. }
  1711. $temp_path = slash( 1, $temp_path );
  1712. if ( !-d $temp_path ) {
  1713. Help("Temporary file path $temp_path does not exist.\n");
  1714. }
  1715.  
  1716. # Validate sid_msg_map version
  1717. Help("Please specify version 1 or 2 for sid_msg_version in your config file\n") unless $sid_msg_version =~ /(1|2)/;
  1718.  
  1719. # set some UserAgent and other connection configs
  1720. $ua->agent("$VERSION");
  1721. $ua->show_progress(1) if ( $Verbose && !$Quiet );
  1722.  
  1723. # New Settings to allow proxy connections to use proper SSL formating - Thx pkthound!
  1724. $ua->timeout(60);
  1725. $ua->cookie_jar( {} );
  1726. $ua->protocols_allowed( [ 'http', 'https' ] );
  1727. $ua->proxy( ['http'], $ENV{http_proxy} ) if $ENV{http_proxy};
  1728. $ua->proxy( ['https'], $ENV{https_proxy} ) if $ENV{https_proxy};
  1729.  
  1730. # Pull in our env vars before we load any of the modules!
  1731. BEGIN {
  1732. my $proxy = $ENV{http_proxy};
  1733. if ($proxy) {
  1734. #Let's handle proxy variables with username / passphrase in them!
  1735. if ( $proxy =~ /^(http|https):\/\/([^:]+):([^:]+)@(.+)$/i ) {
  1736. my $proxytype = $1;
  1737. my $proxyuser = $2;
  1738. my $proxypass = $3;
  1739. my $proxyaddy = $4;
  1740.  
  1741. $ENV{HTTP_PROXY} = "$proxytype://$proxyaddy";
  1742. $ENV{HTTP_PROXY_USERNAME} = $proxyuser;
  1743. $ENV{HTTP_PROXY_PASSWORD} = $proxypass;
  1744. $ENV{HTTPS_PROXY} = "$proxytype://$proxyaddy";
  1745. $ENV{HTTPS_PROXY_USERNAME} = $proxyuser;
  1746. $ENV{HTTPS_PROXY_PASSWORD} = $proxypass;
  1747. }
  1748. else {
  1749. $ENV{HTTPS_PROXY} = $proxy;
  1750. $ENV{HTTP_PROXY} = $proxy;
  1751. }
  1752. }
  1753. undef $proxy;
  1754. $proxy = $ENV{https_proxy}; #check for https_proxy env var
  1755. if ($proxy) {
  1756.  
  1757. #Let's handle proxy variables with username / passphrase in them!
  1758. if ( $proxy =~ /^(http|https):\/\/([^:]+):([^:]+)@(.+)$/i ) {
  1759. my $proxytype = $1;
  1760. my $proxyuser = $2;
  1761. my $proxypass = $3;
  1762. my $proxyaddy = $4;
  1763.  
  1764. $ENV{HTTPS_PROXY} = "$proxytype://$proxyaddy";
  1765. $ENV{HTTPS_PROXY_USERNAME} = $proxyuser;
  1766. $ENV{HTTPS_PROXY_PASSWORD} = $proxypass;
  1767. }
  1768. else {
  1769. $ENV{HTTPS_PROXY} = $proxy;
  1770. }
  1771. }
  1772. }
  1773.  
  1774. if ( $Verbose == 2 ) {
  1775. $ENV{HTTPS_DEBUG} = 1;
  1776. $ENV{HTTP_DEBUG} = 1;
  1777. print "\n\nMY HTTPS PROXY = $ENV{HTTPS_PROXY}\n" if ( $ENV{HTTPS_PROXY} && !$Quiet );
  1778. print "\n\nMY HTTP PROXY = $ENV{HTTP_PROXY}\n" if ( $ENV{HTTP_PROXY} && !$Quiet );
  1779. }
  1780.  
  1781. # let's fetch the most recent md5 file then compare and do our foo
  1782. if ( @base_url && -d $temp_path ) {
  1783.  
  1784. if ( -d $temp_path . "tha_rules" ) {
  1785. print
  1786. "\tdoh, we need to perform some cleanup ... an unclean run last time?\n"
  1787. if ( $Verbose && !$Quiet );
  1788. temp_cleanup($temp_path);
  1789. }
  1790.  
  1791. if ( !$NoDownload ) {
  1792. # Crate a local hash that we will iterate through later for processing
  1793. my $filelist = ();
  1794. my $blk=0;
  1795. # Iterate through all of our urls and check md5 then process accordingly etc...
  1796. foreach (@base_url) {
  1797. undef $Hash if ($Hash && $Hash == 2);
  1798. #undef $Process if ($Process && $Process ==2);
  1799. my ( $base_url, $rule_file, $oinkcode ) = split( /\|/, $_ );
  1800. croak
  1801. "You need to define an oinkcode, please review the rule_url section of the pulledpork config file!\n"
  1802. unless $oinkcode;
  1803. croak(
  1804. "please define the rule_url correctly in the pulledpork.conf\n")
  1805. unless defined $base_url;
  1806. croak(
  1807. "please define the rule_url correctly in the pulledpork.conf\n")
  1808. unless defined $rule_file;
  1809.  
  1810. if ( $base_url =~ /[^labs]\.snort\.org/i ) {
  1811. $prefix = "VRT-";
  1812. unless ( $rule_file =~ /snortrules-snapshot-\d{4}\.tar\.gz/
  1813. || $rule_file =~ /opensource\.gz/ )
  1814. {
  1815. croak(
  1816. "The specified Snort binary does not exist!\nPlease correct the value or specify the FULL",
  1817. " rules tarball name in the pulledpork.conf!\n"
  1818. ) unless $Snort;
  1819. my $Snortv = $Snort;
  1820. $Snortv =~ s/\.//g;
  1821. $rule_file = "snortrules-snapshot-$Snortv.tar.gz";
  1822. }
  1823. }
  1824. elsif ( $base_url =~ /(emergingthreats.net|emergingthreatspro.com)/ ) {
  1825. $prefix = "ET-";
  1826. if ($Snort =~ /(?<=\d\.\d\.\d)\.\d/) {
  1827. my $Snortv = $Snort;
  1828. $Snortv =~ s/(?<=\d\.\d\.\d)\.\d//;
  1829. $base_url .= "$oinkcode/snort-$Snortv/";
  1830. } elsif ($Snort =~ /suricata/i) {
  1831. $base_url .= "$oinkcode/$Snort/";
  1832. }
  1833. }
  1834. elsif ( $base_url =~ /snort-org.+community/ ){
  1835. $prefix = "Snort-Community-"
  1836. }
  1837.  
  1838. $prefix = "Custom-" unless $prefix;
  1839.  
  1840. $Hash = 2 unless $base_url =~ /(emergingthreats|[^labs]\.snort\.org)|snort-org/;
  1841. if ($rule_file =~/IPBLACKLIST/) {
  1842. $Hash = 2;
  1843. $rule_file.=$blk++;
  1844. }
  1845.  
  1846. if ( !$Hash ) {
  1847. $md5 = md5file( $oinkcode, $rule_file, $temp_path, $base_url );
  1848. }
  1849.  
  1850. # and now lets determine the md5 of the last saved rules file if it exists
  1851. if ( -f "$temp_path" . "$rule_file" && !$Hash ) {
  1852. $rule_digest = md5sum( $rule_file, $temp_path );
  1853. }
  1854. else { # the file didn't exsist so lets get it
  1855. rulefetch( $oinkcode, $rule_file, $temp_path, $base_url );
  1856. $Process = 1 unless $rule_file =~ /IPBLACKLIST/;
  1857. if ( -f "$temp_path" . "$rule_file" && !$Hash ) {
  1858. $rule_digest = md5sum( $rule_file, $temp_path );
  1859. }
  1860. }
  1861.  
  1862. # Don't need to perform the following on the IP blacklist stuff...
  1863. next if $rule_file =~ /IPBLACKLIST/;
  1864.  
  1865. # Stuff it all into a hash for use in a bit...
  1866. $filelist->{$rule_file} = {
  1867. 'oinkcode' => $oinkcode,
  1868. 'temp_path' => $temp_path,
  1869. 'Hash' => $Hash,
  1870. 'base_url' => $base_url,
  1871. 'md5' => $md5,
  1872. 'rule_digest' => $rule_digest,
  1873. 'Distro' => $Distro,
  1874. 'arch' => $arch,
  1875. 'Snort' => $Snort,
  1876. 'Sorules' => $Sorules,
  1877. 'ignore_files' => $ignore_files,
  1878. 'docs' => $docs,
  1879. 'prefix' => $prefix,
  1880. 'Process' => $Process,
  1881. 'hmatch' => $hmatch
  1882. };
  1883.  
  1884. # compare the online current md5 against against the md5 of the rules file on system
  1885. $hmatch = compare_md5(
  1886. $oinkcode, $rule_file, $temp_path, $Hash,
  1887. $base_url, $md5, $rule_digest, $Distro,
  1888. $arch, $Snort, $Sorules, $ignore_files,
  1889. $docs, $prefix, $Process, $hmatch,
  1890. $filelist
  1891. );
  1892. }
  1893. # If any of our rules tarballs are new or changed then we need to process all of them
  1894. if (($filelist->{EXTRACT} && $filelist->{EXTRACT} == 1) || $Process) {
  1895. foreach (keys %{$filelist}){
  1896. next if $_ eq "EXTRACT";
  1897. $Process = 1;
  1898. rule_extract(
  1899. $_, $filelist->{$_}{temp_path},
  1900. $filelist->{$_}{Distro}, $filelist->{$_}{arch},
  1901. $filelist->{$_}{Snort}, $filelist->{$_}{Sorules},
  1902. $filelist->{$_}{ignore_files}, $filelist->{$_}{docs},
  1903. $filelist->{$_}{prefix}
  1904. );
  1905. }
  1906. }
  1907. }
  1908. # Only process a local rules tarball
  1909. if ( $NoDownload && !$grabonly ) {
  1910. foreach (@base_url) {
  1911. my ( $base_url, $rule_file ) = split( /\|/, $_ );
  1912. next if $rule_file =~ /IPBLACKLIST/;
  1913. if ( $base_url =~ /[^labs]\.snort\.org/i ) {
  1914. $prefix = "VRT-";
  1915. unless ( $rule_file =~ /snortrules-snapshot-\d{4}\.tar\.gz/ ) {
  1916. croak(
  1917. "The specified Snort rules tarball does not exist!\nPlease correct the value or specify the FULL",
  1918. " rules tarball name in the pulledpork.conf!\n"
  1919. ) unless $Snort;
  1920. my $Snortv = $Snort;
  1921. $Snortv =~ s/\.//g;
  1922. $rule_file = "snortrules-snapshot-$Snortv.tar.gz";
  1923. }
  1924. }
  1925. croak "file $temp_path/$rule_file does not exist!\n"
  1926. unless (-f "$temp_path/$rule_file");
  1927. $prefix = "ET-" if $base_url =~ /(emergingthreats.net|emergingthreatspro.com)/;
  1928. $prefix = "Snort-Community-" if $base_url =~ /snort-org.+community/;
  1929. rule_extract(
  1930. $rule_file, $temp_path, $Distro,
  1931. $arch, $Snort, $Sorules,
  1932. $ignore_files, $docs, $prefix
  1933. ) if !$grabonly;
  1934. }
  1935. }
  1936. # Read our rules and stuff them into a hash
  1937. if ( $Output && !$grabonly && $Process) {
  1938. read_rules( \%rules_hash, "$temp_path" . "tha_rules/", $local_rules );
  1939. }
  1940. # If we are using SO rules, generate the stubs and then stuff them into a hash
  1941. if ( $Sorules
  1942. && -e $Sorules
  1943. && $Distro
  1944. && $Snort
  1945. && !$Textonly
  1946. && !$grabonly
  1947. && $Process)
  1948. {
  1949. gen_stubs( $Snort_path, $Snort_config,
  1950. "$temp_path" . "tha_rules/so_rules/" );
  1951. read_rules( \%rules_hash, "$temp_path" . "tha_rules/so_rules/",
  1952. $local_rules );
  1953. }
  1954. }
  1955. else { Help("Check your oinkcode, temp path and freespace!"); }
  1956.  
  1957. # Read our old rules so that we can determine what is new / changed / deleted
  1958. if ($Output && !$grabonly && $Process) {
  1959. if ( $sid_changelog && -f $Output && !$keep_rulefiles ) {
  1960. read_rules( \%oldrules_hash, "$Output", $local_rules );
  1961. }
  1962. if ( $sid_changelog && $keep_rulefiles && -d $rule_file_path ) {
  1963. read_rules( \%oldrules_hash, "$rule_file_path", $local_rules );
  1964. }
  1965. #print Dumper(%oldrules_hash);
  1966. }
  1967.  
  1968. # Clean up temp path
  1969. if ( -d $temp_path ) {
  1970. temp_cleanup();
  1971. }
  1972.  
  1973. # Process our blacklist data.. need to add a conditional where if we are not linux, we don't
  1974. # use the control socket (linux only)
  1975. if ($black_list && %blacklist && !$NoDownload){
  1976. $bmatch = blacklist_write(\%blacklist,$black_list);
  1977. iprep_control($Config_info{'snort_control'},$Config_info{'IPRVersion'}) if $bmatch;
  1978. }
  1979.  
  1980. # Set our rule states, based on config files and specified base policy, also set our flowbit dependencies
  1981. if ($Output && !$grabonly && $Process) {
  1982. if ( $ips_policy ne "Disabled" ) {
  1983. policy_set( $ips_policy, \%rules_hash );
  1984. }
  1985.  
  1986. if ( $sidmod{modify} && -f $sidmod{modify} ) {
  1987. modify_sid( \%rules_hash, $sidmod{modify} );
  1988. }
  1989.  
  1990. foreach (@sidact) {
  1991. if ( $sidmod{$_} && -f $sidmod{$_} ) {
  1992. modify_state( $_, $sidmod{$_}, \%rules_hash, $rstate );
  1993. }
  1994. elsif ( $sidmod{$_} && !-f $sidmod{$_} ) {
  1995. carp "Unable to read: $sidmod{$_} - $!\n";
  1996. }
  1997. }
  1998.  
  1999. print "Setting Flowbit State....\n"
  2000. if ( !$Quiet );
  2001.  
  2002. my $fbits = 1;
  2003. while ( $fbits > 0 ) {
  2004. $fbits = flowbit_set( \%rules_hash );
  2005. }
  2006. print "\tDone\n"
  2007. if ( !$Quiet );
  2008.  
  2009. if ($Output && $Process) {
  2010. rule_write( \%rules_hash, $Output, $enonly ) unless $keep_rulefiles;
  2011. rule_category_write( \%rules_hash, $rule_file_path, $enonly )
  2012. if $keep_rulefiles;
  2013. }
  2014.  
  2015. if ($sid_msg_map && $Process) {
  2016. sid_msg( \%rules_hash, \%sid_msg_map, $enonly );
  2017. sid_write( \%sid_msg_map, $sid_msg_map, $sid_msg_version );
  2018. }
  2019.  
  2020. if ( $SigHup && $pid_path ne "" && $Process) {
  2021. sig_hup($pid_path) unless $Sostubs;
  2022. print "WARNING, cannot send sighup if also processing SO rules\n",
  2023. "\tsee README.SHAREDOBJECTS\n", "\tor use -T flag!\n"
  2024. if ( $Sostubs && !$Quiet );
  2025. }
  2026.  
  2027. if ( $Config_info{backup} ) {
  2028. @records = split( /,/, $Config_info{backup} );
  2029. archive( \@records, $Config_info{backup_file} )
  2030. if $Config_info{backup_file};
  2031. if ($Verbose) {
  2032. print
  2033. "\tWARN - Unable to create a backup without defining backup_file in config!\n",
  2034. unless $Config_info{backup_file};
  2035. }
  2036. print "\tDone\n" unless $Quiet;
  2037. }
  2038. }
  2039.  
  2040. if ( $sid_changelog && -f $Output ) {
  2041. changelog(
  2042. $sid_changelog, \%rules_hash, \%oldrules_hash,
  2043. \%blacklist, $ips_policy, $enonly, $hmatch, $bmatch
  2044. );
  2045. }
  2046.  
  2047. print("Fly Piggy Fly!\n") if !$Quiet;
  2048. syslogit( 'warning|local0', "INFO: Finished Cleanly" ) if $Syslogging;
  2049.  
  2050. __END__
Advertisement
Add Comment
Please, Sign In to add comment