Sometimes, while investigating incident implying emails, you can get a copy of the original message in EML format. Reading an EML file is not easy with all the SMTP headers and the mulitple MIME parts it may contain. But it may also contain suspicious code that is dangerous to be executed from sensitive environments. EMLRender is a Python script that provides a REST API to render submitted EML files into PNG images. This way, it's easy to have a clear overview of the mail content.
Here is a sample of generated image:
EMLRender is based on the wkhtmltoimage tool to render HTML code. It consists of a single Python script that provides an HTTP interface or REST API. The best way to use it is to run it in a Docker container. This way, it can be easily deployed.
Use the provide Dockerfile to build your image:
$ git clone https://github.com/xme/emlrender
$ cd emlrender
$ docker build -t emlrender:latest .
An image is ready to use on hub.docker.com: https://hub.docker.com/r/rootshell/emlrender/
EMLRender is a stand-alone container that does not have any dependency.
$ docker run emlrender:latest
Once started, it will listing to port 443. Note: a self-signed certificate is generated when the container is created.
EMLRender requires user authentication to render EML files. The first action is to generate the users database and an admin account:
$ curl -k -X POST -d '{"password":"strongpw"}' https://127.0.0.1/init
[{"message": "Users database successfully initialized"}, {"password": "strongpw"}]
If you don't specify a password, a random one will be generated:
$ curl -k -X POST -d '{}' https://127.0.0.1/init
[{"message": "Users database successfully initialized"}, {"password": "1o03uqjm6w"}]
New users can be added with the following request:
$ curl -k -u admin:secretpw -X POST -d '{"username":"john", "password":"strongpw"}' https://127.0.0.1/users/add
[{"message": "Account successfully created", "username": "john", "password": "strongpw"}]
Note: If no password is provided, a random one will be generated.
Account passwords can be changed with the following request:
$ curl -k -u admin:secretpw -X POST -d '{"username":"john", "newpassword":"verystrongpw"}' https://127.0.0.1/users/resetpw
[{"message": "Password successfully updated", "username": "john", newpassword": "verystrongpw"}]
Note: Regular users can change their own password.
Users can be deleted with the following request:
$ curl -k -u admin:secretpw -X POST -d '{"username":"john"}' https://127.0.0.1/users/delete
[{ "message" : "Account successfully deleted" }]
A list of users can be fetched with the following request:
$ curl -k -u admin:secretpw https://127.0.0.1/users/list
[{"message": "Success"}, {"username": "admin"}, {"username": "john"}]
A simple help page is available when you point your browser to the following URL: https://127.0.0.1/help
To submit an EML file, use the following request:
$ curl -k -u john:strongpw -F file=@"spam.eml" -o spam.png https://127.0.0.1/upload
The generated picture will be saved into spam.png. It is possible to submit a ZIP archive containing the EML file (encrypted archives are supported):
$ curl -k -u john:strongpw -F file=@"malicious.zip" -F password=infected -o malicious.png https://127.0.0.1/upload
Point your browser to https://127.0.0.1/upload to submit your EML file via a normal HTML form.
A classic HTTP log is available via the Docker interface:
$ docker logs -f emlrender
* Running on https://0.0.0.0:443/ (Press CTRL+C to quit)
172.17.0.1 - - [18/May/2018 11:59:43] "POST /init HTTP/1.1" 200 -
172.17.0.1 - - [18/May/2018 12:06:36] "POST /users/add HTTP/1.1" 200 -
172.17.0.1 - - [18/May/2018 12:12:08] "GET /users/list HTTP/1.1" 200 -
The application log is available via the api.log file in the container:
$ docker exec -it emlrender tail -f api.log
[2018-May-18 11:59] 172.17.0.1 POST /init? [INFO] Init page requested
[2018-May-18 11:59] 172.17.0.1 POST /init? [INFO] Users database successfully initialized
[2018-May-18 12:06] 172.17.0.1 POST /users/add? [INFO] AddUser page requested
[2018-May-18 12:06] 172.17.0.1 POST /users/add? [INFO] Admin authentication successful
[2018-May-18 12:06] 172.17.0.1 POST /users/add? [INFO] Account john successfully created
[2018-May-18 12:12] 172.17.0.1 GET /users/list? [INFO] ListUsers page requested
[2018-May-18 12:12] 172.17.0.1 GET /users/list? [INFO] Accounts list successfully returned
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] User john successfully authenticated
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] Date: Fri, 18 May 2018 10:38:26 +0200
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] From: <redacted>
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] To: <redacted>
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] Subject: <redacted>
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] Message-Id: <redacted>
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] Created headers 6346209b0e191fe5e9a740db4e7a2db6.png
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] Multipart found, continue
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] Found MIME part: text/plain
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] Decoded 693d8cdec2a72278157ac4bb107b44b0.png
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] Found MIME part: text/html
[2018-May-18 12:24] 172.17.0.1 POST /upload? [INFO] Decoded 461c46fefa1b887857398ba17446b822.png