Given that rolling one's own is not usually a great security idea, is there a crate or library function in Rust to sanitise a filename? The recent 'nul' crate demonstrates that there's a few OS-specific gotchas.
"Safe" mostly depends on your threat model.
If you simply want to avoid your filesystem from being corrupted through bad filenames, then there's good news, you don't need to do anything since filesystem APIs will reject invalid names with errors. Although if you want file names and not paths you either have to be careful to not use APIs that take AsRef<Path> or strip path separators (see std::path::is_separator), since they will accept absolute or relative paths.
If you need to handle relative paths from untrusted inputs you will at least have to strip .. paths to stop directory traversal attacks.
If you want to avoid attacks on the human instead of the software you will have to do a lot of sanitizing, such as removing Unicode text direction overrides which could mislead the user about file extensions.
The recent 'nul' crate demonstrates that there's a few OS-specific gotchas.
That was not an issue with things being unsafe on a particular platform. The issue here is one of cross-platform compatibility. Something that works on UNIX systems did not translate nicely to Windows systems. But the Windows systems failed safely, it simply caused an error and Cargo handled that error in a particular way (stopping the update). It could have chosen to handle the failure in a different way, e.g. by skipping that one particular crate or by mangling the filename.
Related
When use command "nm go_binary", I find the names of variables, functions and packages and even the directory where my code is located are all displayed, is there any way to obfuscate the binary generated by the command "go build" and prevent go binary from being exploited by hackers?
Obfuscating can't stop reverse engineering but in a way prevent info leakage
That is what burrowers/garble (Go 1.16+, Feb. 2021):
Literal obfuscation
Using the -literals flag causes literal expressions such as strings to be replaced with more complex variants, resolving to the same value at run-time.
This feature is opt-in, as it can cause slow-downs depending on the input code.
Literal expressions used as constants cannot be obfuscated, since they are resolved at compile time. This includes any expressions part of a const declaration.
Tiny mode
When the -tiny flag is passed, extra information is stripped from the resulting Go binary.
This includes line numbers, filenames, and code in the runtime that prints panics, fatal errors, and trace/debug info.
All in all this can make binaries 2-5% smaller in our testing, as well as prevent extracting some more information.
With this flag, no panics or fatal runtime errors will ever be printed, but they can still be handled internally with recover as normal.
In addition, the GODEBUG environmental variable will be ignored.
But:
Exported methods are never obfuscated at the moment, since they could be required by interfaces and reflection. This area is a work in progress.
I think the best answer to this question is here How do I protect Python code?, specifically this answer.
While that question is about Python, it applies to all code in general.
I was gonna mark this question as a duplicate, but maybe someone will provide more insight into it.
I'm designing a project for a college-level computer security course, and I'm trying to include a vulnerability where code which is "clean" by virtue of a number of risky packages being blacklisted (unsafe, os, ioutil, etc). The question is this: can you think of a way to use other non-obvious Go standard library packages to escape the sandbox? "Escape the sandbox" here means reading/writing files, making network connections, breaking memory safety (which would allow you to do any of the other things), etc.
Things I've tried so far that haven't worked:
Using the reflect package to do unsafe pointer conversions (the reflect package seems really safe against this sort of abuse)
Using the reflect package to get access to a reference held by a random stdlib package to some sensitive function like os.Open (I haven't found any that actually keep function pointers or anything like that)
I need to execute a compiled program which hardcodes various filesystem paths, with different values for those paths. For practical reasons, adjusting the source code of the program and recompiling it is not an option. Additionally, is is not acceptable to replace the hardcoded files with symlinks, or change the hardcoded files in any other way.
I can only think of two solutions: LD_PRELOAD hooks and patching the binary. The former seems easier and more reliable. Is there any better solution, or perhaps some existing software aiming to solve this problem?
P.S. I am aware I'm speaking of horrible hacks. The hardcoded software in question is widely distributed on Linux distributions, but it appears completely unmaintained, and I don't see any chance of getting a patch in, let alone having it hit distros, in the time I find acceptable.
I think a major design flaw in Linux is the shared object hell when it comes to distributing programs in binary instead of source code form.
Here is my specific problem: I want to publish a Linux program in ELF binary form that should run on as many distributions as possible so my mandatory dependencies are as low as it gets: The only libraries required under any circumstances are libpthread, libX11, librt and libm (and glibc of course). I'm linking dynamically against these libraries when I build my program using gcc.
Optionally, however, my program should also support ALSA (sound interface), the Xcursor, Xfixes, and Xxf86vm extensions as well as GTK. But these should only be used if they are available on the user's system, otherwise my program should still run but with limited functionality. For example, if GTK isn't there, my program will fall back to terminal mode. Because my program should still be able to run without ALSA, Xcursor, Xfixes, etc. I cannot link dynamically against these libraries because then the program won't start at all if one of the libraries isn't there.
So I need to manually check if the libraries are present and then open them one by one using dlopen() and import the necessary function symbols using dlsym(). This, however, leads to all kinds of problems:
1) Library naming conventions:
Shared objects often aren't simply called "libXcursor.so" but have some kind of version extension like "libXcursor.so.1" or even really funny things like "libXcursor.so.0.2000". These extensions seem to differ from system to system. So which one should I choose when calling dlopen()? Using a hardcoded name here seems like a very bad idea because the names differ from system to system. So the only workaround that comes to my mind is to scan the whole library path and look for filenames starting with a "libXcursor.so" prefix and then do some custom version matching. But how do I know that they are really compatible?
2) Library search paths: Where should I look for the *.so files after all? This is also different from system to system. There are some default paths like /usr/lib and /lib but *.so files could also be in lots of other paths. So I'd have to open /etc/ld.so.conf and parse this to find out all library search paths. That's not a trivial thing to do because /etc/ld.so.conf files can also use some kind of include directive which means that I have to parse even more .conf files, do some checks against possible infinite loops caused by circular include directives etc. Is there really no easier way to find out the search paths for *.so?
So, my actual question is this: Isn't there a more convenient, less hackish way of achieving what I want to do? Is it really so complicated to create a Linux program that has some optional dependencies like ALSA, GTK, libXcursor... but should also work without it! Is there some kind of standard for doing what I want to do? Or am I doomed to do it the hackish way?
Thanks for your comments/solutions!
I think a major design flaw in Linux is the shared object hell when it comes to distributing programs in binary instead of source code form.
This isn't a design flaw as far as creators of the system are concerned; it's an advantage -- it encourages you to distribute programs in source form. Oh, you wanted to sell your software? Sorry, that's not the use case Linux is optimized for.
Library naming conventions: Shared objects often aren't simply called "libXcursor.so" but have some kind of version extension like "libXcursor.so.1" or even really funny things like "libXcursor.so.0.2000".
Yes, this is called external library versioning. Read about it here. As should be clear from that description, if you compiled your binaries using headers on a system that would normally give you libXcursor.so.1 as a runtime reference, then the only shared library you are compatible with is libXcursor.so.1, and trying to dlopen libXcursor.so.0.2000 will lead to unpredictable crashes.
Any system that provides libXcursor.so but not libXcursor.so.1 is either a broken installation, or is also incompatible with your binaries.
Library search paths: Where should I look for the *.so files after all?
You shouldn't be trying to dlopen any of these libraries using their full path. Just call dlopen("libXcursor.so.1", RTLD_GLOBAL);, and the runtime loader will search for the library in system-appropriate locations.
When looking at a compiled relese .exe file binary I can find class/struct names in it! Which is odd - obviously there is no need in these symbols. What concerns me is such symbols can be used for reverse-engeniging my software, imposing a big risk to software license protection.
For example, I can find text .?AVCMySecureKeyManager (original class name is CMySecureKeyManager, it looks like to all names is added prefix ".?AV"), easy to guess what my code is doing, right?.. Looks like an open door for hackers.
Particularly, I can tell that I've enabled all possible optimizations the Visual C++ compiler/linker options, turned off all Browse/Debug Info generation, perhaps I'm missing something?
You're seeing RTTI (Run-time Type Information). If you don't use dynamic_cast or typeid in your code, you can usually turn it off safely. Please note that exceptions always use RTTI (for the catch statement matching) and it's not possible to disable it for them.
If you do need dynamic_cast, then you can scrub the names from the EXE after compilation. The code does not depend on the actual name strings, but just their addresses.
That said, the class names, while useful, are not critical in reverse-engineering. Don't rely on their absence as a guarantee.