On a POSIX system, is there a possibility for a file which is non-executable and read-only (aka with a mode 444) to run malicious code on a machine?
If so, can you explain how it would do so?
Yes, something just has to execute it. The X flag hints to the shell that it can be directly executed, but that doesn't stop other programs from executing it if they know how.
For example, if you have a file
a.sh which is not executable to the shell, you can execute it by calling
bash a.sh (which tells
bash explicitly to execute it). If you have a non-executable file
a.py, you can execute it by calling
python a.py. I'd imagine there's also a way to tell the OS to execute a binary ELF file, but I don't know the command off hand.
There are also a whole class of things which don't require you to do anything in particular to make it execute malicious code. PDFs and Adobe Flash files in particular have had some well-known holes which allowed the simple act of reading a file to execute malicious code. There are also some files which, in specific places, and be auto-executed (especially on Windows). Also, if the file is compressed, it may contain a buffer-overflow virus for the decompressor. The file also may be even more malicious, taking advantage of a yet-unknown bug in the file system or something else really low-level.
Bottom line: the only way to guarantee something won't infect your computer is to never do anything with anything.
Let's say you have the file myscript containing the following:
#!/bin/bash echo "Hello, World!"
If you make this file executable and run it with ./myscript, then the kernel will see that the first two bytes are #!, which means it's a script-file. The kernel will then use the rest of the line as the interpreter, and pass the file as its first argument. So, it runs:
and bash reads the file and executes the commands it contains. another way of executing a file without execute bit set is:
a dot followed by a space and then the name of file.
Thus, for bash (or whatever interpreter your script requires) to "execute" the script, it only needs to be able to read the file.
So, for scripts, the execute bit just makes it a bit more convenient to execute it. As long as bash is executable, you can always run bash with the script file as argument
This can be true not only with scripts like the other examples shown on all answers. Basically, if the software that reads a file have a bug, every file is an vector for malicious code execution, using a pretty vast range of techniques(overflow, mem corruption, arbitraty software execution...). Examples:
A file itself may not be dangerous just sitting there. But depending on the program the will try to read it, it could cause problems. As well if the file can be read it can be copied. So it could be copied and renamed into a executable file.
So say you are using DOS/Windows and you have a file something.txt and you copy it to a file called something.bat you can now execute it.
In theory if the file is just sitting there it could be still dangerous, if by somehow the file allocation table (FAT) for the file system has some issues, and is executing a program then due to improper fragmentation it could jump to where your malicious file is and run such code. This type of actions on some Operating Systems can be done via a buffer overflow.
Yes you can. The classic example for binaries is to use the ELF-interpreter as in the example below.
$ cp /bin/ls /tmp/ls $ chmod a-x /tmp/ls $ /lib/ld-linux.so.2 /tmp/ls
Or for x86-64 systems:
$ /lib64/ld-linux-x86-64.so.2 /tmp/ls
The same goes for shell scripts.
$ echo "#!/bin/bash" > /tmp/hello $ echo "echo 'hello world" >> /tmp/hello $ bash /tmp/hello
These are basic example when we have a new auditor or risk manager or security officer who thinks we need to tidy up things. The resolve this is to go the path of SELinux on Linux systems as it also restricts other escalation paths as others also mentioned.