-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
execscan: detect arbitrary file open #8009
Conversation
@@ -66,6 +66,10 @@ const std::string kTripWire = "/tmp/tripwire"; | |||
const std::string kInjectionError = "Shell injection"; | |||
// Shell corruption bug detected based on syntax error. | |||
const std::string kCorruptionError = "Shell corruption"; | |||
// The magic string that we'll use to detect arbitrary file open | |||
const std::string kFzAbsoluteDirectory = "/fz/"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! I wonder if we could gneralize this though to be able to catch these without having to rely on a fixed string.
Maybe we could report a bug on any access on a root /dir that doesn't exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it sounds like a good start for a sanitizer!
One similar idea come across Jonathan and I while we were brainstorming yesterday was to secretly create some files in common dirs (etc/
) without telling the program under test and check if any of them are opened.
We could also use dictionaries if we want to check a particular file.
How to avoid false positive remains an open questions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could report a bug on any access on a root /dir that doesn't exist?
Just pushed that @oliverchang
secretly create some files in common dirs (
etc/
) without telling the program under test and check if any of them are opened.
Like if some program lists all the files in a directory and opens them ?
Not sure about the attack scenario behind this...
How to avoid false positive remains an open questions
I think this is a per fuzz-target config option
My yaml fuzz target will try to open arbitrary files with some include command and it is ok
But I do not think my png or pcap fuzz target should do that...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could report a bug on any access on a root /dir that doesn't exist?
Has anyone tested to see if this isn't normal? If / is in a program's PATH (not super common, but a reasonable example IMO), calling Popen
will always look for files that don't exist in /
to try to execute them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there should be a trailing slash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has anyone tested to see if this isn't normal? If / is in a program's PATH (not super common, but a reasonable example IMO), calling Popen will always look for files that don't exist in / to try to execute them.
It seems pretty rare that '/' would be in a program's PATH, and even if it is, it'd probably go through stat
first before we call open
(if at all -- we'll probably just call execve on it instead). We could also check that it's a dir, and not any random file.
Either way, we can experiment and see what results we get back. I expect projects will need some config/opt-out mechanism for the things we add (even for the command injection one).
@@ -66,6 +66,10 @@ const std::string kTripWire = "/tmp/tripwire"; | |||
const std::string kInjectionError = "Shell injection"; | |||
// Shell corruption bug detected based on syntax error. | |||
const std::string kCorruptionError = "Shell corruption"; | |||
// The magic string that we'll use to detect arbitrary file open | |||
const std::string kFzAbsoluteDirectory = "/fz/"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could report a bug on any access on a root /dir that doesn't exist?
Has anyone tested to see if this isn't normal? If / is in a program's PATH (not super common, but a reasonable example IMO), calling Popen
will always look for files that don't exist in /
to try to execute them.
const std::string kFzAbsoluteDirectory = "/fz/"; | ||
// Arbitrary file open in /fz/ | ||
const std::string kArbitraryFileOpenError = "Arbitrary file open"; | ||
// Assuming we will a shorter top dir. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment isn't complete. I'm not sure what it is trying to say.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
improved it indeed.
@@ -66,6 +66,10 @@ const std::string kTripWire = "/tmp/tripwire"; | |||
const std::string kInjectionError = "Shell injection"; | |||
// Shell corruption bug detected based on syntax error. | |||
const std::string kCorruptionError = "Shell corruption"; | |||
// The magic string that we'll use to detect arbitrary file open | |||
const std::string kFzAbsoluteDirectory = "/fz/"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there should be a trailing slash.
if (found != std::string::npos) { | ||
std::string path_absolute_topdir = path.substr(0, found); | ||
struct stat dirstat; | ||
if (stat (path_absolute_topdir.c_str(), &dirstat) != 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
delete the space after stat
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
#include <string> | ||
#include <iostream> | ||
|
||
extern "C" int LLVMFuzzerTestOneInput(char* data, size_t size) { | ||
std::string str(data, size); | ||
std::cout << "INPUT" << str << std::endl; | ||
system(str.c_str()); | ||
FILE *fp = fopen(str.c_str(), "r"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be in a seperate target?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 let's create a new one rather than re-use this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@catenacyber mind addressing the comments?
I've created google/clusterfuzz#2739 so we should be able to experiment with crazier checks in a way that doesn't bother oss-fuzz project maintainers.
I will look into these next week |
@oliverchang I just pushed an update |
report_bug(kArbitraryFileOpenError); | ||
} | ||
if (path[0] == '/' && path.length() > 1) { | ||
size_t found = path.find('/', 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: s/found/root_dir_end/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
} | ||
if (path[0] == '/' && path.length() > 1) { | ||
size_t found = path.find('/', 1); | ||
if (found != std::string::npos) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if found == std::string::npos
, can we just take the entire string?
i.e. we should be able to detect both /blahdgfdf
and /blahdgfdf/
. Handling the former makes it slightly easier to discover.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done :-)
Ci failure seems to be a network failure |
Thanks @catenacyber. I've opened google/clusterfuzz#2750 to track the required ClusterFuzz-side work to get this working. |
* execscan: detect arbitrary file open * Checks for unknown top dir * move the file open test to its own fuzz target * Fixups from PR review
cc @oliverchang