Skip to content

Latest commit

 

History

History
713 lines (593 loc) · 45 KB

File metadata and controls

713 lines (593 loc) · 45 KB

Here is the combined list of vulnerabilities, formatted as markdown, with duplicates removed and descriptions consolidated:

Combined Vulnerability List

Vulnerability Name: Remote Code Execution (RCE) via WeChat Robot Command Injection

Description:

  1. The application integrates with WeChat using WeRoBot.
  2. The servermanager/robot.py module handles WeChat messages.
  3. If a WeChat user sends the message "ADMIN", the robot enters admin mode.
  4. The application checks for a hardcoded admin password defined in settings.WXADMIN (or '123' in testing mode).
  5. If the user provides the correct password after sending "ADMIN", they become an authenticated administrator within the WeChat robot session.
  6. Once authenticated, the administrator can send commands to the robot.
  7. The CommandHandler in servermanager/api/commonapi.py executes these commands using os.popen(cmd).read().
  8. Vulnerability: os.popen executes shell commands directly without any sanitization or input validation. A malicious authenticated administrator can inject arbitrary shell commands into the robot, leading to Remote Code Execution on the server.

Impact:

  • Critical - Successful exploitation allows a threat actor to execute arbitrary code on the server. This can lead to complete server compromise, data theft, data manipulation, denial of service, and further malicious activities.

Vulnerability Rank: Critical

Currently Implemented Mitigations:

  • None. The application relies on a hardcoded password for WeChat admin authentication, which is insecure and easily compromised if exposed or guessed. There is no input validation or sanitization of commands before execution.

Missing Mitigations:

  • Remove the hardcoded admin password and implement a secure authentication mechanism for WeChat admin access. Consider using API keys, OAuth, or other robust authentication methods.
  • Crucially: Sanitize and validate all commands received from WeChat before executing them. Implement a strict command whitelist or use parameterized command execution to prevent command injection. Avoid using os.popen or similar functions for executing external commands directly with user-provided input. If command execution is absolutely necessary, use safer alternatives and carefully control the input.
  • Implement proper authorization controls to restrict access to sensitive functionalities like command execution to only truly authorized users.

Preconditions:

  • WeChat robot functionality must be enabled and accessible.
  • An attacker needs to guess or obtain the hardcoded admin password (WXADMIN or '123' in testing).
  • The attacker needs to interact with the WeChat robot and authenticate as an administrator.

Source Code Analysis:

  • File: /code/servermanager/robot.py
  • Function: MessageHandler.handler
class MessageHandler:
    # ...
    def handler(self):
        info = self.message.content
        # ...
        if self.userinfo.isAdmin and self.userinfo.isPasswordSet:
            if self.userinfo.Command != '' and info.upper() == 'Y':
                return cmd_handler.run(self.userinfo.Command) # Command Execution
            else:
                if info.upper() == 'HELPME':
                    return cmd_handler.get_help()
                self.userinfo.Command = info
                self.save_session()
                return "确认执行: " + info + " 命令?"
        # ...
  • File: /code/servermanager/api/commonapi.py
  • Function: CommandHandler.run and CommandHandler.__run_command__
class CommandHandler:
    # ...
    def run(self, title):
        # ...
        if cmd:
            return self.__run_command__(cmd[0].command) # Calls __run_command__
        else:
            return "未找到相关命令,请输入hepme获得帮助。"

    def __run_command__(self, cmd):
        try:
            res = os.popen(cmd).read() # Vulnerable command execution
            return res
        except BaseException:
            return '命令执行出错!'
  • Vulnerability: The code directly passes the cmd variable, which can be influenced by WeChat user input after admin authentication, to os.popen. This allows for command injection.

Security Test Case:

  1. Set up and configure the WeChat robot to be accessible for testing.
  2. Initiate a conversation with the WeChat robot.
  3. Send the message "ADMIN" to the robot.
  4. The robot should prompt for the admin password. Send the hardcoded password (if known) or '123' if in testing mode. If WXADMIN is set in environment, use that value, otherwise try '123'.
  5. If authentication is successful, the robot should respond with "验证通过,请输入命令或者要执行的命令代码:输入helpme获得帮助".
  6. Now, send a malicious command to the robot. For example, to test command execution, try: ls -al ; id. This command attempts to list directory contents and then execute the id command to show user identity.
  7. Send 'Y' to confirm command execution when prompted by the robot ("确认执行: ... 命令?").
  8. Observe the response from the robot. If the command injection is successful, the response should include the output of both ls -al and id commands, indicating arbitrary command execution on the server. For example, you might see the output of ls -al followed by user and group ID information from id.
  9. To further confirm RCE, try more impactful commands like creating a file in a temporary directory (touch /tmp/rce_test) and then check if the file is created on the server.

Vulnerability Name: Unrestricted File Upload with Potential for Arbitrary File Overwrite and Code Execution

Description:

  1. The application provides a file upload endpoint at /upload (view function fileupload in blog/views.py).
  2. This endpoint is intended for image uploads (图床 functionality).
  3. Authentication is attempted using a sign parameter in the GET request, which is compared to a hash of settings.SECRET_KEY.
  4. If the sign is valid, the application proceeds to save the uploaded files.
  5. Files are saved under settings.STATICFILES/files/ or settings.STATICFILES/image/ directories, based on whether the uploaded file is detected as an image (by extension).
  6. Image files are processed using PIL (Pillow) library to re-save with quality optimization.
  7. Vulnerability:
    • Unrestricted File Upload: While there is a basic sign check, it's primarily for preventing unauthorized access, not for restricting file types or content. An attacker who obtains or bypasses the sign can upload arbitrary files.
    • Potential Arbitrary File Overwrite: Although os.path.normpath is used, if an attacker can craft a filename with directory traversal sequences that bypass normalization or exploit vulnerabilities in path handling, they might be able to overwrite existing files within the STATICFILES_DIRS location.
    • Potential Code Execution: If an attacker uploads a malicious executable file (e.g., .php, .py, .sh, .html with JavaScript) and can access its URL via the static file serving mechanism of the web server, they could potentially achieve code execution on the server or client-side execution depending on the file type and server configuration. The risk is higher if the STATICFILES_DIRS are served directly by the web server (e.g., Nginx, Apache) and not just by Django's staticfiles app in development.

Impact:

  • High - Successful exploitation can lead to arbitrary file upload, potentially overwriting existing static files. If malicious executable files are uploaded and accessible, it can lead to Remote Code Execution (depending on server configuration and file type). Even uploading HTML files with malicious JavaScript can lead to Cross-Site Scripting (XSS) if the static file serving does not set appropriate Content-Type and security headers.

Vulnerability Rank: High

Currently Implemented Mitigations:

  • Basic sign parameter check for authentication.
  • Using os.path.normpath for path normalization (partially mitigates path traversal, but may not be sufficient).
  • Image files are reprocessed using PIL (not a security mitigation, primarily for optimization).

Missing Mitigations:

  • Stronger Authentication and Authorization: Implement proper authentication and authorization mechanisms to control access to the file upload endpoint. Relying solely on a simple sign is not sufficient for security.
  • File Type Validation and Whitelisting: Implement strict file type validation to only allow intended file types (e.g., images). Use a whitelist approach and reject any file types not explicitly allowed. Validate file content (magic bytes, MIME type) in addition to file extensions, as extensions can be easily spoofed.
  • Filename Sanitization: Sanitize filenames to prevent directory traversal attempts and other malicious filenames. Avoid directly using user-provided filenames. Consider generating unique, random filenames or using a controlled naming scheme.
  • Path Traversal Prevention: Implement robust path traversal prevention measures beyond os.path.normpath. Ensure that the target directory for file uploads is securely configured and that uploaded files cannot be placed outside of the intended directory.
  • Security Headers: Configure the web server to serve static files with appropriate security headers (e.g., Content-Type: application/octet-stream for unknown files, Content-Disposition: attachment to force download, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Content-Security-Policy) to mitigate potential client-side attacks and ensure files are treated as static data, not executable content.

Preconditions:

  • The /upload endpoint must be accessible.
  • An attacker needs to obtain or bypass the sign parameter validation (which is relatively weak as it's based on SECRET_KEY hash but exposed in GET requests).

Source Code Analysis:

  • File: /code/blog/views.py
  • Function: fileupload
@csrf_exempt
def fileupload(request):
    # ...
    if request.method == 'POST':
        sign = request.GET.get('sign', None)
        if not sign:
            return HttpResponseForbidden()
        if not sign == get_sha256(get_sha256(settings.SECRET_KEY)): # Weak Authentication
            return HttpResponseForbidden()
        response = []
        for filename in request.FILES:
            timestr = timezone.now().strftime('%Y/%m/%d')
            imgextensions = ['jpg', 'png', 'jpeg', 'bmp']
            fname = u''.join(str(filename))
            isimage = len([i for i in imgextensions if fname.find(i) >= 0]) > 0
            base_dir = os.path.join(settings.STATICFILES, "files" if not isimage else "image", timestr)
            if not os.path.exists(base_dir):
                os.makedirs(base_dir)
            savepath = os.path.normpath(os.path.join(base_dir, f"{uuid.uuid4().hex}{os.path.splitext(filename)[-1]}")) # Path Normalization - potentially insufficient
            if not savepath.startswith(base_dir):
                return HttpResponse("only for post")
            with open(savepath, 'wb+') as wfile: # File Saving
                for chunk in request.FILES[filename].chunks():
                    wfile.write(chunk)
            if isimage:
                from PIL import Image
                image = Image.open(savepath)
                image.save(savepath, quality=20, optimize=True) # Image reprocessing - not security
            url = static(savepath)
            response.append(url)
        return HttpResponse(response)
    else:
        return HttpResponse("only for post")
  • Vulnerability: The code lacks proper file type validation, filename sanitization, and robust path traversal prevention. The sign based authentication is weak.

Security Test Case:

  1. Obtain the valid sign value. This might require knowing settings.SECRET_KEY or finding a way to extract it if it's inadvertently exposed. If SECRET_KEY is very strong and not exposed, bypassing the sign might not be directly feasible for an external attacker. However, for testing purposes, assume you can obtain a valid sign.
  2. Craft a malicious file. For example, create a simple HTML file with JavaScript (malicious.html):
<html>
<body>
<script>alert("XSS Vulnerability from File Upload");</script>
</body>
</html>
  1. Prepare a POST request to /upload?sign=[VALID_SIGN] with malicious.html as a file upload. Use curl, Postman, or a similar tool.
  2. Send the request to the application.
  3. The application should respond with a success status (HTTP 200) and return the URL of the uploaded file.
  4. Access the returned URL in a web browser.
  5. Check if the JavaScript alert box ("XSS Vulnerability from File Upload") appears. If it does, it confirms that arbitrary HTML/JS files can be uploaded and served as static content, leading to potential XSS.
  6. To test for potential code execution (if applicable to your server environment and file types), try uploading executable files (e.g., .php, .py, .sh) and attempt to access them via the returned URL. Observe the server's behavior to determine if code execution is possible. For example, if you upload a PHP file, try to access it via the browser and see if the PHP code is executed by the server.
  7. To test path traversal, craft a filename like ../../../evil.txt and upload it. Check if the file is saved outside the intended STATICFILES_DIRS or if you can overwrite existing files. This might be more challenging to exploit due to os.path.normpath, but it's worth testing different path traversal payloads.

Vulnerability Name: Open Redirect in OAuth Login

Description:

  1. An attacker can craft a malicious OAuth login URL by manipulating the next_url parameter.
  2. When a user clicks on this crafted link and successfully authenticates via OAuth, they will be redirected to the attacker-controlled URL instead of the intended page within the DjangoBlog application.
  3. This can be used for phishing attacks, where users are redirected to a fake login page or malicious website after OAuth authentication.

Impact:

  • High - Users can be redirected to malicious websites, potentially leading to credential theft, malware installation, or other security breaches. This can severely damage user trust and the reputation of the website.

Vulnerability Rank: High

Currently Implemented Mitigations:

  • None. The code attempts to validate the next_url by checking p.netloc, but this check is insufficient and can be bypassed.

Missing Mitigations:

  • Implement a robust whitelist of allowed hostnames or use Django's is_safe_url utility function to validate the next_url parameter.
  • Properly sanitize and validate the next_url parameter to ensure it is a safe URL within the application's domain.

Preconditions:

  • The application must have OAuth login functionality enabled.
  • An attacker needs to be able to craft URLs for OAuth login, which is typically the case.

Source Code Analysis:

  • File: /code/oauth/views.py
  • Function: get_redirecturl(request)
def get_redirecturl(request):
    nexturl = request.GET.get('next_url', None)
    if not nexturl or nexturl == '/login/' or nexturl == '/login':
        nexturl = '/'
        return nexturl
    p = urlparse(nexturl)
    if p.netloc:
        site = get_current_site().domain
        if not p.netloc.replace('www.', '') == site.replace('www.', ''):
            logger.info('非法url:' + nexturl)
            return "/"
    return nexturl
  • The code retrieves the next_url parameter from the GET request.
  • It attempts to validate the URL by parsing it using urlparse and checking if p.netloc (the network location) is present.
  • It then compares p.netloc (after removing 'www.') with the current site's domain (also after removing 'www.').
  • Vulnerability: This validation is flawed. An attacker can bypass this check by using URLs with different schemes or by encoding malicious characters in the hostname. For example, using a URL like http://[email protected] or http://yourdomain.com.attacker.com can bypass this check. The check only verifies if the netloc contains the domain, not if it starts with it or is a subdomain.
  • After the flawed check, the code returns the unsanitized nexturl, leading to an open redirect.

Security Test Case:

  1. Access the application's login page.
  2. Initiate an OAuth login flow (e.g., for Google, GitHub, etc.). Capture the OAuth authorization URL.
  3. Modify the redirect_uri parameter in the OAuth authorization URL to point to the application's OAuth authorize endpoint (e.g., /oauth/authorize?type=google) and append a malicious next_url parameter. For example, if using Google OAuth and the application domain is yourdomain.com, craft a URL like:
https://accounts.google.com/o/oauth2/v2/auth?client_id=YOUR_GOOGLE_CLIENT_ID&response_type=code&redirect_uri=https://yourdomain.com/oauth/authorize?type=google&scope=openid%20email&next_url=http://attacker.com

Replace YOUR_GOOGLE_CLIENT_ID with the actual client ID of the application. 4. Open the crafted URL in a browser. 5. Complete the OAuth login process using valid credentials. 6. After successful OAuth authentication, observe that you are redirected to http://attacker.com instead of the expected page on yourdomain.com.


Vulnerability Name: Potential Cross-Site Scripting (XSS) in RSS Feed via Markdown Rendering

Description:

  1. The application uses Markdown to render article content in RSS feeds.
  2. If the Markdown rendering process in the RSS feed does not properly sanitize HTML, an attacker can inject malicious JavaScript code into article content.
  3. When a user views the RSS feed through a feed reader or browser, the injected JavaScript code will be executed in their browser, potentially leading to session hijacking, cookie theft, or other malicious actions.

Impact:

  • High - Successful XSS can lead to account compromise, data theft, and further attacks against users viewing the RSS feed.

Vulnerability Rank: High

Currently Implemented Mitigations:

  • The project uses CommonMarkdown.get_markdown to render markdown, which uses markdown library and extensions like codehilite, toc, and tables. It's unclear from the provided files if output sanitization is performed after markdown rendering in the RSS feed generation context.

Missing Mitigations:

  • Ensure that the output of CommonMarkdown.get_markdown is properly sanitized before being included in the RSS feed.
  • Use a robust HTML sanitization library (like bleach which is already in utils.py but not explicitly used in feed generation) to remove or escape any potentially malicious HTML tags and attributes from the rendered Markdown content before including it in the RSS feed.

Preconditions:

  • RSS feed functionality must be enabled.
  • The application must use Markdown for article content.
  • An attacker needs to be able to create or modify article content, which is usually possible for authors/administrators.

Source Code Analysis:

  • File: /code/djangoblog/feeds.py
  • Function: item_description(self, item)
def item_description(self, item):
    return CommonMarkdown.get_markdown(item.body)
  • The code uses CommonMarkdown.get_markdown(item.body) to generate the item description for the RSS feed.
  • File: /code/djangoblog/utils.py
  • Function: CommonMarkdown.get_markdown(value)
class CommonMarkdown:
    @staticmethod
    def _convert_markdown(value):
        md = markdown.Markdown(
            extensions=[
                'extra',
                'codehilite',
                'toc',
                'tables',
            ]
        )
        body = md.convert(value)
        toc = md.toc
        return body, toc

    @staticmethod
    def get_markdown_with_toc(value):
        body, toc = CommonMarkdown._convert_markdown(value)
        return body, toc

    @staticmethod
    def get_markdown(value):
        body, toc = CommonMarkdown._convert_markdown(value)
        return body
  • CommonMarkdown.get_markdown simply converts Markdown to HTML using the markdown library without explicit sanitization in this function. While utils.py defines sanitize_html using bleach, it's not used in CommonMarkdown.get_markdown directly.
  • Vulnerability: If article body contains malicious Markdown that, when converted to HTML, includes JavaScript, and this HTML is not sanitized before being put into the RSS feed, then XSS is possible. RSS readers might execute JavaScript embedded in feed items.

Security Test Case:

  1. Log in to the DjangoBlog admin panel as an author or administrator.
  2. Create a new article or edit an existing one.
  3. In the article body, insert the following Markdown code to inject JavaScript:
<script>alert("XSS Vulnerability");</script>
  1. Publish the article.
  2. Access the RSS feed URL of the blog (e.g., /feed/ or /rss/).
  3. View the RSS feed source code or open it in an RSS reader.
  4. Observe if the <script>alert("XSS Vulnerability");</script> tag is present in the RSS feed item's description.
  5. If using a browser to view the RSS feed, check if the JavaScript alert box (XSS Vulnerability) appears. If using an RSS reader, check if it executes JavaScript (behavior might vary depending on the RSS reader, some might block JS execution). If the script tag is present and/or JavaScript executes, then the vulnerability is confirmed.

Vulnerability Name: Potential Server-Side Request Forgery (SSRF) and Path Traversal in Avatar Download Functionality

Description:

  1. The save_user_avatar function in djangoblog/utils.py downloads user avatars from URLs provided in OAuth profiles.
  2. If the provided URL is not properly validated and sanitized, an attacker could provide a malicious URL that points to:
    • Internal resources on the server (SSRF), potentially exposing sensitive information or internal services.
    • Local file paths (Path Traversal), potentially reading sensitive files on the server.
  3. This could allow an attacker to gain unauthorized access to internal resources or sensitive data.
  4. The sync_user_avatar management command in /code/blog/management/commands/sync_user_avatar.py uses this vulnerable save_user_avatar function to periodically synchronize user avatars. This command execution can be triggered by system administrators, making the vulnerability exploitable during routine maintenance tasks.

Impact:

  • High - SSRF and Path Traversal vulnerabilities can lead to the exposure of sensitive internal data, access to internal services, and potentially remote code execution in some scenarios.

Vulnerability Rank: High

Currently Implemented Mitigations:

  • The code checks the response status code and image extensions, but it lacks proper URL validation and sanitization to prevent SSRF and Path Traversal.

Missing Mitigations:

  • Implement strict validation of the avatar URL to ensure it points to a valid external image resource and not to internal resources or local file paths.
  • Whitelist allowed protocols (e.g., http, https) and hostnames for avatar URLs.
  • Prevent resolving to internal IP addresses or reserved hostnames.
  • Sanitize the URL to prevent path traversal attempts (e.g., block .. sequences).

Preconditions:

  • OAuth login functionality must be enabled.
  • The application must use the save_user_avatar function to download and store user avatars from OAuth providers.
  • An attacker needs to be able to control the avatar URL provided by the OAuth provider or manipulate the OAuth flow to inject a malicious avatar URL.

Source Code Analysis:

  • File: /code/djangoblog/utils.py
  • Function: save_user_avatar(url)
def save_user_avatar(url):
    '''
    保存用户头像
    :param url:头像url
    :return: 本地路径
    '''
    logger.info(url)

    try:
        basedir = os.path.join(settings.STATICFILES, 'avatar')
        rsp = requests.get(url, timeout=2) # requests.get without URL validation
        if rsp.status_code == 200:
            if not os.path.exists(basedir):
                os.makedirs(basedir)

            image_extensions = ['.jpg', '.png', 'jpeg', '.gif']
            isimage = len([i for i in image_extensions if url.endswith(i)]) > 0
            ext = os.path.splitext(url)[1] if isimage else '.jpg'
            save_filename = str(uuid.uuid4().hex) + ext
            logger.info('保存用户头像:' + basedir + save_filename)
            with open(os.path.join(basedir, save_filename), 'wb+') as file:
                file.write(rsp.content)
            return static('avatar/' + save_filename)
    except Exception as e:
        logger.error(e)
        return static('blog/img/avatar.png')
  • The code uses requests.get(url, timeout=2) to download the avatar from the provided url.
  • Vulnerability: There is no validation or sanitization of the url before making the requests.get call. This makes the application vulnerable to SSRF and Path Traversal. An attacker could provide URLs like:
    • http://127.0.0.1:8000/admin/ (SSRF to internal admin panel)
    • http://localhost/server-status (SSRF to server status page if running on localhost)
    • file:///etc/passwd (Path Traversal to read local files - may or may not work depending on requests library and OS, but still a risk)
    • http://username:[email protected]/ (Credential leakage if the application follows redirects to attacker's site with credentials in URL).
  • The check for image_extensions and rsp.status_code == 200 only happens after the potentially dangerous request is made, and doesn't prevent SSRF/Path Traversal.
  • File: /code/blog/management/commands/sync_user_avatar.py
  • Function: Command.handle
class Command(BaseCommand):
    # ...
    def handle(self, *args, **options):
        # ...
        for u in users:
            # ...
            url = u.picture
            if url:
                if url.startswith(static_url):
                    if self.test_picture(url):
                        continue
                    else:
                        if u.metadata:
                            manage = get_manager_by_type(u.type)
                            url = manage.get_picture(u.metadata) # Get avatar URL from OAuth provider
                            url = save_user_avatar(url) # Vulnerable function call
                        else:
                            url = static('blog/img/avatar.png')
                else:
                    url = save_user_avatar(url) # Vulnerable function call
            else:
                url = static('blog/img/avatar.png')
            # ...
  • The sync_user_avatar management command iterates through OAuth users and calls save_user_avatar with the user's avatar URL retrieved from their OAuth profile or directly from u.picture. This command execution makes the SSRF/Path Traversal vulnerability in save_user_avatar more relevant as it's used in a scheduled or manually triggered task.

Security Test Case:

  1. Set up a network traffic capture tool (e.g., Wireshark or tcpdump) or a request logging service (like webhook.site or requestbin.com).
  2. Initiate an OAuth login flow (e.g., using Google, GitHub, etc.).
  3. During the OAuth flow, if possible, manipulate or intercept the OAuth response to inject a malicious avatar URL. If direct manipulation isn't feasible, try to find an OAuth provider that allows setting a custom avatar URL and set it to a malicious URL before initiating the login. Example malicious URLs to test:
    • SSRF: http://127.0.0.1:8000/admin/ (replace 8000 with your application's port if different)
    • Path Traversal: file:///etc/passwd (for Linux-based servers) or file:///C:/Windows/win.ini (for Windows-based servers - might not work directly due to requests library limitations but worth testing). For testing SSRF to an external service, you can use a request logging service URL like https://webhook.site/your_unique_webhook_id.
  4. Complete the OAuth login process.
  5. Observe the network traffic capture or request logs.
  6. For SSRF: Check if there are requests made by the server to internal resources like 127.0.0.1:8000/admin/ or localhost/server-status or to your request logging service URL. If you see such requests originating from the server, SSRF is confirmed.
  7. For Path Traversal: Check server logs for any errors related to file access or if the application attempts to process or display content that might indicate local file access (less likely to be directly visible in this function's context, but server-side errors might indicate attempts). For testing Path Traversal more effectively, you might need to modify the code temporarily to log or expose the content read from the file, if possible, without breaking the application significantly. For the purpose of this test case as an external attacker, demonstrating SSRF is more readily achievable and sufficient to prove the vulnerability in the URL handling.
  8. Trigger Avatar Sync Command: Alternatively, or additionally, you can wait for the scheduled execution of sync_user_avatar command (if it's scheduled) or manually execute the command python manage.py sync_user_avatar on the server after setting a malicious avatar URL in your OAuth profile. Observe the server's network traffic or logs during the execution of this command to detect SSRF attempts.

Vulnerability Name: Insecure Default SECRET_KEY Configuration

Description:

  • The Django settings fall back to a hardcoded SECRET_KEY when the DJANGO_SECRET_KEY environment variable is missing. An external attacker who knows or can guess this default key can sign session cookies, CSRF tokens, or other signed data.
  • Steps to trigger:
    1. Deploy the application without setting DJANGO_SECRET_KEY.
    2. Read the repository to discover the hardcoded default key.
    3. Construct forged signed tokens (e.g. session cookies) using the default key and present them to the application.

Impact:

  • Forged tokens may enable session hijacking, CSRF bypass, and account impersonation by undermining cryptographic integrity.

Vulnerability Rank: High

Currently Implemented Mitigations:

  • The code attempts to load the key with os.environ.get('DJANGO_SECRET_KEY') but falls back to the hardcoded value.

Missing Mitigations:

  • There is no requirement that a production deployment provide an override; the insecure default remains in the source code.

Preconditions:

  • The application is deployed without setting DJANGO_SECRET_KEY so that the fallback (insecure default) is used.

Source Code Analysis:

  • In /code/djangoblog/settings.py, the assignment:
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY') or 'n9ceqv38)#&mwuat@(mjb_p%em$e8$qyr#fw9ot!=ba6lijx-6'

means that when DJANGO_SECRET_KEY is absent, the default value is used.

Security Test Case:

  1. Deploy the application without defining DJANGO_SECRET_KEY.
  2. Using the known default key, craft a signed token (e.g. a session cookie) by signing arbitrary data.
  3. Present the forged token and verify that the application accepts it (for example, by gaining authenticated access).

Vulnerability Name: Insecure ALLOWED_HOSTS Configuration Allowing Host Header Injection

Description:

  • ALLOWED_HOSTS is configured to include the wildcard ('*'), meaning that the application accepts requests with any Host header.
  • Steps to trigger:
    1. Send an HTTPS request to the deployed instance with a custom Host header (e.g. “evil.example.com”).
    2. Initiate functionality (such as a password reset) that constructs absolute URLs.
    3. Observe that the generated URLs use the attacker-controlled Host header.

Impact:

  • This can allow phishing attacks, cache poisoning, or redirects to malicious sites since absolute URLs include the attacker‑controlled host.

Vulnerability Rank: High

Currently Implemented Mitigations:

  • The project sets ALLOWED_HOSTS but includes '*', effectively disabling host restriction.

Missing Mitigations:

  • Remove the wildcard and list only the valid hostnames expected in production.

Preconditions:

  • The application is deployed with the current ALLOWED_HOSTS configuration.

Source Code Analysis:

  • In /code/djangoblog/settings.py the ALLOWED_HOSTS is defined as:
ALLOWED_HOSTS = ['*', '127.0.0.1', 'example.com']

so any Host header is accepted.

Security Test Case:

  1. Send an HTTP request with a custom Host header (e.g. “evil.example.com”).
  2. Trigger a feature that returns absolute URLs (such as a password reset email).
  3. Verify that the generated URL uses the attacker-controlled host, which could lead to phishing.

Vulnerability Name: Missing OAuth State Parameter in OAuth Authentication Flows

Description:

  • The OAuth managers (for providers such as Weibo, Google, GitHub, Facebook, and QQ) generate authorization URLs without including a state parameter—a key defense against CSRF in OAuth flows.
  • Steps to trigger:
    1. Initiate an OAuth login flow where an attacker modifies or omits the state value in the authorization URL.
    2. Intercept the redirect callback and supply a forged code.
    3. The application, lacking state validation, exchanges the bogus code and may be deceived into linking or hijacking the account.

Impact:

  • This flaw can allow CSRF attacks on the OAuth login process, enabling account linking hijack or bypassing standard authentication checks.

Vulnerability Rank: High

Currently Implemented Mitigations:

  • No state parameter is included in the authorization URLs.

Missing Mitigations:

  • Generate a random state parameter in the authorization request and validate it upon receiving the callback.

Preconditions:

  • The application is configured with one or more OAuth providers and an attacker can intervene in the OAuth flow.

Source Code Analysis:

  • In /code/oauth/oauthmanager.py, the code builds an authorization URL with parameters that do not include “state”:
params = {
    'client_id': self.client_id,
    'response_type': 'code',
    'redirect_uri': self.callback_url + '&next_url=' + nexturl
}
  • Similar omissions occur in other OAuth managers.

Security Test Case:

  1. Initiate an OAuth login using one of the providers (e.g. Weibo).
  2. Inspect the generated URL to confirm that no state parameter is present.
  3. Simulate a callback with an altered or missing state value and verify that the application does not reject the forged callback.

Vulnerability Name: DEBUG Mode Enabled by Default in Production

Description:

  • The Django settings use a helper (env_to_bool) to set the DEBUG flag, which defaults to True if DJANGO_DEBUG is not provided. When DEBUG is enabled, detailed error pages containing sensitive information (such as server config, file paths, and code snippets) are visible.
  • Steps to trigger:
    1. Deploy the application in a production environment without setting DJANGO_DEBUG to False.
    2. Trigger an error by accessing a non-existent URL or causing an exception in a view.
    3. The resulting detailed error page is shown to the attacker.

Impact:

  • Revealing detailed error messages and configuration data can help an attacker craft further targeted attacks.

Vulnerability Rank: High

Currently Implemented Mitigations:

  • The code reads DJANGO_DEBUG from the environment; however, its default is True.

Missing Mitigations:

  • Ensure that DEBUG is explicitly set to False in production (for example, by using a production settings file that hardcodes DEBUG = False) and remove any insecure defaults from the repository.

Preconditions:

  • The application is deployed in production without defining DJANGO_DEBUG or with it set to True.

Source Code Analysis:

  • In /code/djangoblog/settings.py, DEBUG is configured as follows:
DEBUG = env_to_bool('DJANGO_DEBUG', True)
  • This means that if DJANGO_DEBUG is not set, DEBUG remains enabled.

Security Test Case:

  1. Deploy the application without overriding DEBUG (or explicitly setting it to True).
  2. Force an application error by visiting an invalid URL.
  3. Confirm that the detailed Django error page (with sensitive details) is displayed.

Vulnerability Name: Unauthenticated File Upload via Weak Signature Verification

Description:

  • The /upload endpoint (implemented in /code/blog/views.py) is used to provide image hosting but is decorated with @csrf_exempt and does not require traditional authentication. Instead, it relies solely on a GET parameter (sign) that must equal a double‑hash of the application’s SECRET_KEY. If the application is deployed using an insecure (or known) SECRET_KEY, an attacker can compute the valid signature and upload arbitrary files.
  • Steps to trigger:
    1. Deploy the application without overriding the insecure default SECRET_KEY.
    2. Compute the valid sign value by applying the same double‑hash (i.e. obtain get_sha256(get_sha256(DEFAULT_SECRET_KEY))).
    3. Send a POST request to /upload with the computed sign and include a file (for example, a file with a permitted extension like “.jpg”) containing malicious content.
    4. Observe that the file is saved on the server in the configured static files directory.

Impact:

  • An attacker may upload malicious files that, if later executed (in case of misconfiguration of static file handling) or served to unsuspecting users, could lead to remote code execution, malware hosting, or further compromise of the server.

Vulnerability Rank: High

Currently Implemented Mitigations:

  • The endpoint checks for a valid sign derived from settings.SECRET_KEY. However, it:
    • Relies solely on this weak signature without additional authentication or content validations.
    • Uses only filename extensions (not file content or MIME type) to classify files as images.

Missing Mitigations:

  • – Require proper user authentication or an API key rather than relying only on a predictable signature.
  • – Validate file contents by checking MIME types and scanning for dangerous content.
  • – Consider implementing CSRF protection and storing uploads in a secure, non‐executable location.

Preconditions:

  • The application is deployed using the insecure default SECRET_KEY (or one that is guessable) and the file upload endpoint is publicly accessible.

Source Code Analysis:

  • In /code/blog/views.py, the fileupload view:
    • Is decorated with @csrf_exempt so no CSRF token is required.
    • Reads the GET parameter sign and compares it with get_sha256(get_sha256(settings.SECRET_KEY)).
    • Iterates over request.FILES and (based solely on the file’s extension) determines whether the file is an image before storing it under a dynamically generated path.
  • These measures are insufficient if the secret key is insecure or known from the repository, allowing an attacker to compute the sign value and upload files.

Security Test Case:

  1. Deploy the application without customizing the SECRET_KEY (so the default is used).
  2. Independently compute the valid sign by applying get_sha256 twice to the known default SECRET_KEY.
  3. Craft a POST request to /upload?sign=<computed-sign> with a file payload (e.g. a file named “malicious.jpg”) that contains potentially malicious content.
  4. Verify that the upload succeeds (for example, by checking that the file is saved to disk and that its URL is included in the response).
  5. Evaluate whether the saved file could be used to trigger further attacks given the server’s configuration.

Vulnerability Name: OAuth Account Takeover via Email Binding

Description:

  1. An attacker initiates an OAuth login (e.g., using GitHub, Google, QQ, Weibo, Facebook).
  2. If the OAuth provider does not return an email address for the user, or if the email is not verified, the application prompts the user to provide an email address to associate with their account.
  3. An attacker can enter any email address during this step, even one that does not belong to them.
  4. If the attacker enters an email address belonging to another user already registered on the DjangoBlog platform (either via standard registration or another OAuth provider), the OAuth account will be linked to the existing user account associated with that email address.
  5. The legitimate user's account is then effectively taken over by the attacker through the newly linked OAuth account. The attacker can log in using the OAuth provider and access the legitimate user's account.

Impact:

  • Account takeover: An attacker can gain complete control of another user's account on the DjangoBlog platform.
  • Data breach: The attacker can access and potentially modify or delete the legitimate user's blog posts, settings, and personal information.
  • Reputation damage: If the attacker misuses the compromised account, it can damage the reputation of both the user and the DjangoBlog platform.

Vulnerability Rank: Critical

Currently Implemented Mitigations:

  • None. The code in oauth/views.py allows binding any email address provided by the user during the OAuth email requirement step without proper verification against existing accounts or ownership validation.

Missing Mitigations:

  • Email ownership verification: When a user provides an email address during the OAuth email requirement step, the system should verify if the email is already associated with an existing account.
  • Account linking conflict resolution: If the email is already associated with an existing account, the system should prevent automatic linking and provide a mechanism for the user to prove ownership of the existing account or choose a different email.
  • Email verification: After the user provides an email, a verification email should be sent to the provided address, requiring the user to click a link to confirm ownership before the email is bound to the OAuth account.

Preconditions:

  • The target user must have an account on the DjangoBlog platform associated with an email address.
  • The attacker must initiate an OAuth login using a provider that does not automatically provide or verify the user's email address during the OAuth flow, leading to the "require email" step in DjangoBlog.

Source Code Analysis:

  • File: /code/oauth/views.py
  • Function: RequireEmailView.form_valid(self, form)
def form_valid(self, form):
    email = form.cleaned_data['email']
    oauthid = form.cleaned_data['oauthid']
    oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
    oauthuser.email = email # Vulnerable line: Directly assigns the provided email
    oauthuser.save()
    sign = get_sha256(settings.SECRET_KEY +
                      str(oauthuser.id) + settings.SECRET_KEY)
    site = get_current_site().domain
    if settings.DEBUG:
        site = '127.0.0.1:8000'
    path = reverse('oauth:email_confirm', kwargs={
        'id': oauthid,
        'sign': sign
    })
    url = "http://{site}{path}".format(site=site, path=path)

    content = _("""
           <p>Please click the link below to bind your email</p>

             <a href="%(url)s" rel="bookmark">%(url)s</a>

             Thank you again!
             <br />
             If the link above cannot be opened, please copy this link to your browser.
              <br />
             %(url)s
            """) % {'url': url}
    send_email(emailto=[email, ], title=_('Bind your email'), content=content) # Sends confirmation email, but after email is already assigned
    url = reverse('oauth:bindsuccess', kwargs={
        'oauthid': oauthid
    })
    url = url + '?type=email'
    return HttpResponseRedirect(url)
  • In the form_valid function of RequireEmailView, the email address provided by the attacker via the RequireEmailForm is directly assigned to the oauthuser.email field without checking if this email is already associated with another user.
  • The subsequent send_email function dispatches a signal to send a confirmation email to the provided address, but this is only for email verification after the email has been assigned. There is no check to prevent assigning an email already in use by another account before sending the confirmation email.
  • The emailconfirm view then proceeds to link the OAuth user to a BlogUser with the provided email, creating a new user if one doesn't exist or linking to an existing one if it does, effectively taking over the account associated with that email if it already exists.

Security Test Case:

  1. Create two user accounts on the DjangoBlog platform:
  2. As attacker, initiate an OAuth login using an OAuth provider that triggers the email requirement step (e.g., GitHub if email privacy settings prevent email disclosure).
  3. When prompted for an email address, enter the email address of User A: [email protected].
  4. Complete the OAuth email binding process.
  5. Log out of any existing DjangoBlog sessions.
  6. Log in to DjangoBlog using the OAuth account created in step 2.
  7. Observe that you are now logged in as User A, and can access and control User A's account.
  8. Repeat steps 2-7, but this time enter the email address of User B: [email protected].
  9. Observe that you are now logged in as User B, and can access and control User B's account.

This test case demonstrates that an attacker can take over any existing account by simply providing the target user's email address during the OAuth email binding process.