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

execscan: detect arbitrary file open #8009

Merged
merged 4 commits into from
Aug 3, 2022
Merged

Conversation

catenacyber
Copy link
Contributor

@@ -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/";
Copy link
Collaborator

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?

CC @alan32liu @jonathanmetzman

Copy link
Contributor

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.

Copy link
Contributor Author

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...

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Collaborator

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/";
Copy link
Contributor

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.
Copy link
Contributor

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.

Copy link
Contributor Author

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/";
Copy link
Contributor

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) {
Copy link
Contributor

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

Copy link
Contributor Author

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");
Copy link
Contributor

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?

Copy link
Collaborator

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.

Copy link
Collaborator

@oliverchang oliverchang left a 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.

@catenacyber
Copy link
Contributor Author

I will look into these next week

@catenacyber
Copy link
Contributor Author

@oliverchang I just pushed an update

report_bug(kArbitraryFileOpenError);
}
if (path[0] == '/' && path.length() > 1) {
size_t found = path.find('/', 1);
Copy link
Collaborator

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/

Copy link
Contributor Author

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) {
Copy link
Collaborator

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :-)

@catenacyber
Copy link
Contributor Author

Ci failure seems to be a network failure

@oliverchang
Copy link
Collaborator

Thanks @catenacyber. I've opened google/clusterfuzz#2750 to track the required ClusterFuzz-side work to get this working.

MartinPetkov pushed a commit to MartinPetkov/oss-fuzz that referenced this pull request Aug 15, 2022
* execscan: detect arbitrary file open

* Checks for unknown top dir

* move the file open test to its own fuzz target

* Fixups from PR review
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants