Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added EvilStrGen and ReDoSHunter #83

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@
path = src/detect/src/detectors/shen-ReScue
url = https://github.com/davisjam/ReScue
branch = SupportForVRD
[submodule "src/detect/src/detectors/mclaughlin-regulator"]
path = src/detect/src/detectors/mclaughlin-regulator
url = https://github.com/ucsb-seclab/regulator-dynamic.git
[submodule "src/detect/src/detectors/hong-EvilStrGen"]
path = src/detect/src/detectors/hong-EvilStrGen
url = https://github.com/Anonymous89813/EvilStrGen.git
[submodule "src/detect/src/detectors/li-ReDoSHunter"]
path = src/detect/src/detectors/li-ReDoSHunter
url = https://github.com/yetingli/ReDoSHunter.git
1 change: 1 addition & 0 deletions src/detect/src/detectors/hong-EvilStrGen
Submodule hong-EvilStrGen added at a07d7a
1 change: 1 addition & 0 deletions src/detect/src/detectors/li-ReDoSHunter
Submodule li-ReDoSHunter added at 1d8d62
1 change: 1 addition & 0 deletions src/detect/src/detectors/mclaughlin-regulator
Submodule mclaughlin-regulator added at 9b40c0
186 changes: 186 additions & 0 deletions src/detect/src/drivers/query-hong-EvilStrGen.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#!/usr/bin/env perl
# Author: Jamie Davis <[email protected]>
# Description: Query the shen-ReScue detector for pattern safety
#
# Dependencies:
# - VULN_REGEX_DETECTOR_ROOT must be defined

use strict;
use warnings;

use IPC::Cmd qw[can_run]; # Check PATH
use JSON::PP; # I/O
use Data::Dumper;
use Carp;

# Check dependencies.
if (not defined $ENV{VULN_REGEX_DETECTOR_ROOT}) {
die "Error, VULN_REGEX_DETECTOR_ROOT must be defined\n";
}

my $EvilStrGenDir = "$ENV{VULN_REGEX_DETECTOR_ROOT}/src/detect/src/detectors/hong-EvilStrGen";
if (not -d $EvilStrGenDir) {
die "Error, could not find EvilStrGenDir <$EvilStrGenDir>\n";
}

if (not can_run("java")) {
die "Error, cannot find 'java'\n";
}

# Check args.
if (scalar(@ARGV) != 1) {
die "Usage: $0 pattern.json\n";
}

my $patternFile = $ARGV[0];
if (not -f $patternFile) {
die "Error, no such patternFile $patternFile\n";
}

# Read.
my $cont = &readFile("file"=>$patternFile);
my $pattern = decode_json($cont);
my $regex = $pattern->{pattern};

# Write out to a tmp file for EvilStrGen format.
my $tmpFile = "/tmp/query-hong-EvilStrGen-$$.regex";
my $tmpOutputDir = "/tmp/query-hong-EvilStrGen-$$-output";
my $regexEngine = 14; # Python
my $maxAttackSize = 100;
unlink $tmpFile;
unlink $tmpOutputDir;
&writeToFile("file"=>$tmpFile, "contents"=>$regex);
print STDERR "CLEANUP: $tmpFile\n"; # If we time out, the parent can clean up for us.

# Run.
&cmd("cd $ENV{VULN_REGEX_DETECTOR_ROOT}/src/detect 2>&1");
my $cmdString = "./src/detectors/hong-EvilStrGen/build/EvilStrGen $tmpFile $tmpOutputDir $regexEngine $maxAttackSize 0";
my ($rc, $out) = &cmd("$cmdString 2>&1");
unlink $tmpFile;

# Parse to determine opinion
my $opinion = { };

print STDOUT &cmd("ls $tmpOutputDir");

# Check if EvilStrGen detected ReDoS
if ($out =~ m/writing file\n/) {
# Initialize an array to store all attack strings
my @attackStrings;

# Iterate over all files in the output directory that match the pattern "*.txt"
opendir(my $dir, $tmpOutputDir) or die "Cannot open directory: $!";
while (my $file = readdir($dir)) {
next unless ($file =~ /\.txt$/); # Process only .txt files

# Read the content of each file and add it to the array
my $attackStr = &readFile("file"=>"$tmpOutputDir/$file");
push @attackStrings, $attackStr;
}
closedir($dir);

# Populate opinion
$opinion->{canAnalyze} = 1;
$opinion->{isSafe} = "NO";
$opinion->{evilInput} = \@attackStrings;

} elsif (length($out) == 0) {
$opinion->{canAnalyze} = 1;
$opinion->{isSafe} = "YES";
$opinion->{evilInput} = [];
} else {
$opinion->{canAnalyze} = 0;
$opinion->{isSafe} = "UNKNOWN";
$opinion->{evilInput} = ["COULD-NOT-PARSE"];
}

unlink $tmpOutputDir;

&log("\n\n--------------------\n\nOutput:\n$out");

# Update $pattern.
$pattern->{opinion} = $opinion;

# Emit.
print STDOUT encode_json($pattern) . "\n";


#####################

# input: ($cmd)
# output: ($rc, $out)
sub cmd {
my ($cmd) = @_;
&log("CMD: $cmd");
my $out = `$cmd`;
return ($? >> 8, $out);
}

sub log {
my ($msg) = @_;
print STDERR "$msg\n";
}

# input: %args: keys: file contents
# output: $file
sub writeToFile {
my %args = @_;

open(my $fh, '>', $args{file});
print $fh $args{contents};
close $fh;

return $args{file};
}

# input: ($exploitString) JSON object
# fields: separators[] pumps[] suffix
# separators and pumps have the same length
# output: ($translatedExploitString) hashref
# fields: pumpPairs[] suffix
# Each pumpPair is an object with keys: prefix pump
sub translateExploitString {
my ($es) = @_;

if (defined($es->{separators}) and defined($es->{pumps}) and defined($es->{suffix})) {
&log("exploitString looks valid");
}
else {
croak("Invalid exploitString: " . Dumper($es));
}

# Convert Weideman's format to something more sensible:
# pumpPairs[]
# suffix

my @separators = @{$es->{separators}};
my @pumps = @{$es->{pumps}};
if (scalar(@separators) ne scalar(@pumps)) {
croak("Invalid exploitString: " . Dumper($es));
}

my @pumpPairs;
for (my $i = 0; $i < scalar(@separators); $i++) {
push @pumpPairs, { "prefix" => $separators[$i],
"pump" => $pumps[$i]
};
}

my $suffix = $es->{suffix};

return { "pumpPairs" => \@pumpPairs,
"suffix" => $suffix
};
}

# input: %args: keys: file
# output: $contents
sub readFile {
my %args = @_;

open(my $FH, '<', $args{file}) or confess "Error, could not read $args{file}: $!";
my $contents = do { local $/; <$FH> }; # localizing $? wipes the line separator char, so <> gets it all at once.
close $FH;

return $contents;
}
186 changes: 186 additions & 0 deletions src/detect/src/drivers/query-li-ReDoSHunter.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#!/usr/bin/env perl
# Author: Jamie Davis <[email protected]>
# Description: Query the shen-ReScue detector for pattern safety
#
# Dependencies:
# - VULN_REGEX_DETECTOR_ROOT must be defined

use strict;
use warnings;

use IPC::Cmd qw[can_run]; # Check PATH
use JSON::PP; # I/O
use Data::Dumper;
use File::Glob ':glob';
use Carp;


# Check dependencies.
if (not defined $ENV{VULN_REGEX_DETECTOR_ROOT}) {
die "Error, VULN_REGEX_DETECTOR_ROOT must be defined\n";
}

my $ReDoSHunterDir = "$ENV{VULN_REGEX_DETECTOR_ROOT}/src/detect/src/detectors/li-ReDoSHunter";
if (not -d $ReDoSHunterDir) {
die "Error, could not find ReDoSHunterDir <$ReDoSHunterDir>\n";
}

if (not can_run("java")) {
die "Error, cannot find 'java'\n";
}

# Check args.
if (scalar(@ARGV) != 1) {
die "Usage: $0 pattern.json\n";
}

my $patternFile = $ARGV[0];
if (not -f $patternFile) {
die "Error, no such patternFile $patternFile\n";
}

# Read.
my $cont = &readFile("file"=>$patternFile);
my $pattern = decode_json($cont);
my $regex = $pattern->{pattern};

# Write out to a tmp file for ReDoSHunter format.
my $filePath = "$ReDoSHunterDir/data/paper_dataset";
my $fileName = "query-li-ReDoSHunter-$$.txt";
my $tmpFile = "$filePath/$fileName";
unlink $tmpFile;
&writeToFile("file"=>$tmpFile, "contents"=>$regex);
print STDERR "CLEANUP: $tmpFile\n"; # If we time out, the parent can clean up for us.

# Run ReDoSHunter
my $jvmNoDumpFlags = ""; # TODO Is there a portable way to do this? "-XXnoJrDump -XXdumpSize:none"; # Disable crash files (generated if ulimit on memory exceeded).
my $cmdString = "cd $ReDoSHunterDir && java $jvmNoDumpFlags -jar $ReDoSHunterDir/target/ReDoSHunter-1.0.0.jar $filePath $fileName";
my ($rc, $out) = &cmd("$cmdString 2>&1");
unlink $tmpFile;

# Read results
my $resDir = "$ReDoSHunterDir/data/expr";
my $result = "";
my ($resFile) = bsd_glob("$resDir/*redos_s_java*");
if ($resFile) {
$result = &readFile("file"=>$resFile);
} else {
die "Error: No file found containing 'redos_s_java' in $resDir\n";
}
unlink glob("$resDir/*") or warn "Could not delete files in $resDir: $!";

# Parse to determine opinion
print STDOUT "Result: $result\n";
my $opinion = { };
if ($result =~ m/.*RESULT-TRUE.*/s) {
$opinion->{canAnalyze} = 1;
$opinion->{isSafe} = "NO";

# Capture attack sstrings
my @attack_strings;
while ($result =~ /AttackString:(.+?)\s*$/gm) {
push @attack_strings, $1; # Capture the attack string content
}
$opinion->{evilInput} = \@attack_strings;
} elsif ($result =~ m/.*RESULT-FALSE.*/s) {
if ($result =~ m/.*TIME-OUT.*/s) {
$opinion->{canAnalyze} = 0;
$opinion->{isSafe} = "UNKNOWN";
$opinion->{evilInput} = ["TIME-OUT"];
} else {
$opinion->{canAnalyze} = 1;
$opinion->{isSafe} = "YES";
}
} else {
$opinion->{canAnalyze} = 0;
$opinion->{isSafe} = "UNKNOWN";
$opinion->{evilInput} = ["COULD-NOT-PARSE"];
}

&log("\n\n--------------------\n\nOutput:\n$out");

# Update $pattern.
$pattern->{opinion} = $opinion;

# Emit.
print STDOUT encode_json($pattern) . "\n";

#####################

# input: ($cmd)
# output: ($rc, $out)
sub cmd {
my ($cmd) = @_;
&log("CMD: $cmd");
my $out = `$cmd`;
return ($? >> 8, $out);
}

sub log {
my ($msg) = @_;
print STDERR "$msg\n";
}

# input: %args: keys: file contents
# output: $file
sub writeToFile {
my %args = @_;

open(my $fh, '>', $args{file});
print $fh $args{contents};
close $fh;

return $args{file};
}

# input: ($exploitString) JSON object
# fields: separators[] pumps[] suffix
# separators and pumps have the same length
# output: ($translatedExploitString) hashref
# fields: pumpPairs[] suffix
# Each pumpPair is an object with keys: prefix pump
sub translateExploitString {
my ($es) = @_;

if (defined($es->{separators}) and defined($es->{pumps}) and defined($es->{suffix})) {
&log("exploitString looks valid");
}
else {
croak("Invalid exploitString: " . Dumper($es));
}

# Convert Weideman's format to something more sensible:
# pumpPairs[]
# suffix

my @separators = @{$es->{separators}};
my @pumps = @{$es->{pumps}};
if (scalar(@separators) ne scalar(@pumps)) {
croak("Invalid exploitString: " . Dumper($es));
}

my @pumpPairs;
for (my $i = 0; $i < scalar(@separators); $i++) {
push @pumpPairs, { "prefix" => $separators[$i],
"pump" => $pumps[$i]
};
}

my $suffix = $es->{suffix};

return { "pumpPairs" => \@pumpPairs,
"suffix" => $suffix
};
}

# input: %args: keys: file
# output: $contents
sub readFile {
my %args = @_;

open(my $FH, '<', $args{file}) or confess "Error, could not read $args{file}: $!";
my $contents = do { local $/; <$FH> }; # localizing $? wipes the line separator char, so <> gets it all at once.
close $FH;

return $contents;
}
Loading