Permalink
Browse files

Add bearer token authentication to API server

  • Loading branch information...
Bun committed Sep 5, 2018
1 parent 58eb8f0 commit 56a21ab6390e0613330af3f0c7a782ad65ff94ca
@@ -6,23 +6,25 @@
import datetime
import hashlib
import io
import logging
import multiprocessing
import os
import socket
import tarfile
import zipfile

from flask import Flask, request, jsonify, make_response
from flask import Flask, request, jsonify, make_response, abort

from cuckoo.common.config import config, parse_options
from cuckoo.common.files import Files, Folders
from cuckoo.common.utils import parse_bool
from cuckoo.common.utils import parse_bool, constant_time_compare
from cuckoo.core.database import Database, Task
from cuckoo.core.database import TASK_REPORTED, TASK_COMPLETED, TASK_RUNNING
from cuckoo.core.rooter import rooter
from cuckoo.core.submit import SubmitManager
from cuckoo.misc import cwd, version, decide_cwd, Pidfile

log = logging.getLogger(__name__)
db = Database()
sm = SubmitManager()

@@ -646,7 +648,29 @@ def exit_api():
else:
return jsonify(message="Server stopped")

@app.errorhandler(401)
def api_auth_required(error):
return json_error(
401, "Authentication in the form of an "
"'Authorization: Bearer <TOKEN>' header is required"
)

@app.before_request
def check_authentication():
token = config("cuckoo:cuckoo:api_token")
if token:
expect = "Bearer " + token
auth = request.headers.get("Authorization")
if not constant_time_compare(auth, expect):
abort(401)

def cuckoo_api(hostname, port, debug):
if not config("cuckoo:cuckoo:api_token"):
log.warning(
"It is strongly recommended to enable API authentication to "
"protect against unauthorized access and CSRF attacks."
)
log.warning("Please check the API documentation for more information.")
app.run(host=hostname, port=port, debug=debug)

if os.environ.get("CUCKOO_APP") == "api":
@@ -11,7 +11,7 @@

from cuckoo.common.exceptions import CuckooConfigurationError
from cuckoo.common.objects import Dictionary
from cuckoo.common.utils import parse_bool
from cuckoo.common.utils import parse_bool, random_token
from cuckoo.misc import cwd

log = logging.getLogger(__name__)
@@ -222,6 +222,7 @@ class Config(object):
exists=True, writable=True, readable=False,
allow_empty=True
),
"api_token": String(random_token(), allow_empty=True),
"rooter": Path(
"/tmp/cuckoo-rooter",
exists=False, writable=False, readable=False
@@ -3,6 +3,7 @@
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.

import base64
import bs4
import chardet
import datetime
@@ -58,6 +59,20 @@ def convert_to_printable(s):
return s
return "".join(convert_char(c) for c in s)

def random_token():
"""Generate a random token that can be used as a secret/password."""
token = base64.urlsafe_b64encode(os.urandom(16))
return token.rstrip(b"=").decode("utf8")

def constant_time_compare(a, b):
"""Compare two secret strings in constant time."""
if not a or not b or len(a) != len(b):
return False
result = 0
for x, y in zip(a, b):
result |= ord(x) ^ ord(y)
return result == 0

def validate_hash(h):
"""Validates a hash by length and contents."""
if len(h) not in (32, 40, 64, 128):
@@ -713,6 +713,9 @@ def _205_206(c):

def _206_210(c):
c["auxiliary"]["replay"]["certificate"] = "bin/cert.p12"
# We'd like to provide a secure default, but let's not inconvenience
# upgrading users.
c["cuckoo"]["cuckoo"]["api_token"] = ""
return c

migrations = {
@@ -4,6 +4,12 @@
# one available.
version_check = {{ cuckoo.cuckoo.version_check }}

# The authentication token that is required to access the Cuckoo API, using
# HTTP Bearer authentication. This will protect the API instance against
# unauthorized access and CSRF attacks. It is strongly recommended to set this
# value to a secure value.
api_token = {{ cuckoo.cuckoo.api_token }}

# If turned on, Cuckoo will delete the original file after its analysis
# has been completed.
delete_original = {{ cuckoo.cuckoo.delete_original }}
@@ -4,6 +4,12 @@
# one available.
version_check = yes

# The authentication token that is required to access the Cuckoo API, using
# HTTP Bearer authentication. This will protect the API instance against
# unauthorized access and CSRF attacks. It is strongly recommended to set this
# value to a secure value.
api_token =

# If turned on, Cuckoo will delete the original file after its analysis
# has been completed.
delete_original = no
@@ -20,6 +20,15 @@ those values, you can use the following syntax::
$ cuckoo api --host 0.0.0.0 --port 1337
$ cuckoo api -H 0.0.0.0 -p 1337

To allow only authentication access to the API, the ``api_token`` in
``cuckoo.conf`` must be set to a secret value. In new Cuckoo installations,
a random token is automatically generated for you.
To access the API, you must send the ``Authorization: Bearer <token>`` header
with all your requests using the token defined in the configuration.
Note that if you want to access the API over an insecure network such as the
Internet, you should run the API server behind `nginx`_ described in the
next section and enable HTTPS.

Web deployment
--------------

@@ -188,7 +197,7 @@ Adds a file to the list of pending tasks. Returns the ID of the newly created ta

**Example request**::

curl -F file=@/path/to/file http://localhost:8090/tasks/create/file
curl -H "Authorization: Bearer S4MPL3" -F file=@/path/to/file http://localhost:8090/tasks/create/file

**Example request using Python**..

@@ -198,10 +207,11 @@ Adds a file to the list of pending tasks. Returns the ID of the newly created ta
REST_URL = "http://localhost:8090/tasks/create/file"
SAMPLE_FILE = "/path/to/malwr.exe"
HEADERS = {"Authorization": "Bearer S4MPL3"}
with open(SAMPLE_FILE, "rb") as sample:
files = {"file": ("temp_file_name", sample)}
r = requests.post(REST_URL, files=files)
r = requests.post(REST_URL, headers=HEADERS, files=files)
# Add your code to error checking for r.status_code.
@@ -252,7 +262,7 @@ Adds a file to the list of pending tasks. Returns the ID of the newly created ta

.. code-block:: bash
curl -F url="http://www.malicious.site" http://localhost:8090/tasks/create/url
curl -H "Authorization: Bearer S4MPL3" -F url="http://www.malicious.site" http://localhost:8090/tasks/create/url
**Example request using Python**.

@@ -262,9 +272,10 @@ Adds a file to the list of pending tasks. Returns the ID of the newly created ta
REST_URL = "http://localhost:8090/tasks/create/url"
SAMPLE_URL = "http://example.org/malwr.exe"
HEADERS = {"Authorization": "Bearer S4MPL3"}
data = {"url": SAMPLE_URL}
r = requests.post(REST_URL, data=data)
r = requests.post(REST_URL, headers=HEADERS, data=data)
# Add your code to error checking for r.status_code.
@@ -316,25 +327,27 @@ submit ID as well as the task IDs of the newly created task(s).
.. code-block:: bash
# Submit two executables.
curl http://localhost:8090/tasks/create/submit -F files=@1.exe -F files=@2.exe
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/tasks/create/submit -F files=@1.exe -F files=@2.exe
# Submit http://google.com
curl http://localhost:8090/tasks/create/submit -F strings=google.com
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/tasks/create/submit -F strings=google.com
# Submit http://google.com & http://facebook.com
curl http://localhost:8090/tasks/create/submit -F strings=$'google.com\nfacebook.com'
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/tasks/create/submit -F strings=$'google.com\nfacebook.com'
**Example request using Python**.

.. code-block:: python
import requests
HEADERS = {"Authorization": "Bearer S4MPL3"}
# Submit one or more files.
r = requests.post("http://localhost:8090/tasks/create/submit", files=[
("files", open("1.exe", "rb")),
("files", open("2.exe", "rb")),
])
], headers=HEADERS)
# Add your code to error checking for r.status_code.
@@ -350,6 +363,7 @@ submit ID as well as the task IDs of the newly created task(s).
]
r = requests.post(
"http://localhost:8090/tasks/create/submit",
headers=HEADERS,
data={"strings": "\n".join(urls)}
)
@@ -395,7 +409,7 @@ Returns list of tasks.

.. code-block:: bash
curl http://localhost:8090/tasks/list
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/tasks/list
**Example response**.

@@ -475,7 +489,7 @@ Returns list of tasks for sample.

.. code-block:: bash
curl http://localhost:8090/tasks/sample/1
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/tasks/sample/1
**Example response**.

@@ -532,7 +546,7 @@ Returns details on the task associated with the specified ID.

.. code-block:: bash
curl http://localhost:8090/tasks/view/1
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/tasks/view/1
**Example response**.

@@ -596,7 +610,7 @@ is 1).

.. code-block:: bash
curl http://localhost:8090/tasks/reschedule/1
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/tasks/reschedule/1
**Example response**.

@@ -629,7 +643,7 @@ Removes the given task from the database and deletes the results.

.. code-block:: bash
curl http://localhost:8090/tasks/delete/1
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/tasks/delete/1
**Parameters**:

@@ -654,7 +668,7 @@ Returns the report associated with the specified task ID.

.. code-block:: bash
curl http://localhost:8090/tasks/report/1
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/tasks/report/1
**Parameters**:

@@ -704,7 +718,7 @@ Re-run reporting for task associated with the specified task ID.

.. code-block:: bash
curl http://localhost:8090/tasks/rereport/1
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/tasks/rereport/1
**Example response**.

@@ -736,7 +750,7 @@ Add a reboot task to database from an existing analysis ID.

.. code-block:: bash
curl http://localhost:8090/tasks/reboot/1
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/tasks/reboot/1
**Example response**.

@@ -820,7 +834,7 @@ Returns details on the file matching either the specified MD5 hash, SHA256 hash

.. code-block:: bash
curl http://localhost:8090/files/view/id/1
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/files/view/id/1
**Example response**.

@@ -865,7 +879,7 @@ Returns details on the file matching either the specified MD5 hash, SHA256 hash

.. code-block:: bash
curl http://localhost:8090/files/get/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 > sample.exe
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/files/get/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 > sample.exe
**Status codes**:

@@ -885,7 +899,7 @@ Returns the content of the PCAP associated with the given task.

.. code-block:: bash
curl http://localhost:8090/pcap/get/1 > dump.pcap
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/pcap/get/1 > dump.pcap
**Status codes**:

@@ -905,7 +919,7 @@ Returns a list with details on the analysis machines available to Cuckoo.

.. code-block:: bash
curl http://localhost:8090/machines/list
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/machines/list
**Example response**.

@@ -952,7 +966,7 @@ Returns details on the analysis machine associated with the given name.

.. code-block:: bash
curl http://localhost:8090/machines/view/cuckoo1
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/machines/view/cuckoo1
**Example response**.

@@ -1016,7 +1030,7 @@ Unix!)

.. code-block:: bash
curl http://localhost:8090/cuckoo/status
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/cuckoo/status
**Example response**.

@@ -1074,7 +1088,7 @@ Returns VPN status.

.. code-block:: bash
curl http://localhost:8090/vpn/status
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/vpn/status
**Status codes**:

@@ -1094,7 +1108,7 @@ Shuts down the server if in debug mode and using the werkzeug server.

.. code-block:: bash
curl http://localhost:8090/exit
curl -H "Authorization: Bearer S4MPL3" http://localhost:8090/exit
**Status codes**:

0 comments on commit 56a21ab

Please sign in to comment.