0

Overview: I am trying to develop a Python Flask web aplication named (SSL Scanner) which uses the “testssl.sh script” by Mr. Dirk Wetter (as available on Github) as backend. The application is supposed to be hosted on IIS and on the web-page there is an input field where the hostname (with or without port number) or an ip-address can be entered by the user and a button “Begin Scan”. Upon clicking the button “Begin Scan”, the hostname is extracted from the html form in the file named “app.py” and is passed on as a parameter to the command that I wish to execute with respect to the testssl.sh script for scanning the server configuration of the entered hostname.

I have extracted the ubuntu executable (ubuntu.exe) and have added it to the path of the Environment and System Variables, so that I am able to run shell commands (in this case testssl.sh script) just by appending the word "bash" in front of the actual command.

Here firstly while executing the flask application, the directory is changed to where the testssl.sh script is stored and then the scan can be performed by executing the “testssl.sh” script as can be seen from the code written in the file named “app.py”. The “testssl.sh” script generates a html file which gets stored in a particular directory and is finally served as a download link on the web interface along with a proper message once the scan is completed. There is one other thing that I wish to mention that, while the scan is running, i.e. after entering the hostname and then after clicking on the “Begin Scan” button, a message is supposed to be shown on the web interface “Scanning the server configuration of entered hostname, and once the scan is completed, this message is replaced by the message "Scan completed for entered hostname along with the download link for the report is shown on the web interface".

Problem: Now, when I am running this flask application in localhost:5000 (flask development server), it is running successfully as per expectations, but after deploying it on IIS, when I access the website (the one deployed on the IIS) the “Internal_scanner.html” template is being rendered properly as expected, but after entering the hostname and clicking on the “Begin Scan” button, the message “Scanning the server configuration of entered hostname” is being shown for around 1 second and then the message "Scan completed for entered hostname along with the download link for the report is shown on the web interface" is shown but the scan does not take place.

Note: In the app.log file I was getting the error as mentionined in this StackOverflow post text but with flask and IIS, not Django and IIS and there is only one solution given here, which I have applied. And still the issue persists, but now atleast this error is not getting logged in the app.log file.

I am attaching the "Internal_Scanner.html", "app.py", and the "web.config" file for your generous reference. I will be highly obliged if anyone could kindly assist me in this regard, so that I am able to host it on IIS and make it accessible on other devices by sharing the URL.

(Modified)Flask Application app.py

from flask import Flask, render_template, request, jsonify, url_for, send_file
import os
import datetime
import subprocess

app = Flask(__name__)  # app: Test SSL Interface

dateandtime = datetime.datetime.now().strftime("%H%M")

def create_app():
    @app.route("/")
    def index():
        return render_template("Internal_Scanner.html")
    
    
    @app.route("/scan", methods=["POST"])
    def scan():
        # Get the hostname from the form data
        hostname = request.form["hostname"]
    
        if len(hostname) > 50:
            return jsonify({"return_output": "The hostname is too long."})
        elif hostname.find(" ") != -1:
            return jsonify(
                {
                    "return_output": "The entered hostname is not valid as it has a whitespace."
                }
            )
        else:
            if hostname.find(":") == -1:
                htmlfilename = f"{hostname}_{dateandtime}.html"
            else:
                htmlfilename = f"{(hostname.split(':'))[0]}_{dateandtime}.html"
    
            # Call the backend code to scan the given hostname
            os.chdir(r"C:\inetpub\wwwroot\scanner\testssl.sh-3.1dev")
    
            # Execute the testssl.sh script with the URL and redirect the output to a folder        
            subprocess.run(f"bash ./testssl.sh --htmlfile ../reports/scans/{htmlfilename} -p -s -f -P -S -h -H -I -T -BB -R -C -B -O -W -F -D -4 --openssl-timeout 5 {hostname}")    
            download_link = f"/api/download/{htmlfilename}"
            return jsonify({"return_output": f"Scan completed for hostname: {hostname}.", "download_link": download_link})
    
    
    @app.route("/api/download/<string:filename>", methods=["GET"])
    def download_report(filename):
        # Serve the file from the reports directory
        # r"C:\WSL\reports\scans"
        file_path = os.path.join(r"C:\inetpub\wwwroot\scanner\reports\scans", filename)
        if os.path.isfile(file_path):
            return send_file(file_path, as_attachment=True)
        else:
            return jsonify({"error": "File not found"})
    
    return app

Template Internal_Scanner.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
  <title>SSL Scanner</title>
  <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
  <link rel="shortcut icon" type="image/x-icon" href="{{ url_for('static', filename='images/icons.ico') }}" />
</head>

<body>
  <div id="header">
    <div id="logo">
      <img src="{{ url_for('static', filename='images/image.jpeg') }}" width="150" height="50" alt="Logo"
        title="Scanner Logo" />
    </div>
    <div>
      <a href="" id="documentation">Documentation</a>
      <a href="mailto:prateek406@gmail.com" id="contactUs">Contact Us</a>
    </div>
  </div>

  <div id="submitBox">
    <form method="POST" action="/scan" style="color: rgb(255, 119, 1)">
      <label for="hostname"><b>Hostname</b></label>
      <input type="text" name="hostname" id="hostname" required />
      <button id="submit-btn" type="submit" style="
            color: rgb(227, 216, 203);
            font-weight: bold;
            background-color: rgb(255, 119, 1);
            border-radius: 5px;
            padding: 8px 12px;
            border: none;
            cursor: pointer;
          ">
        Begin Scan
      </button>
      <label style="margin-left: 5px">Do not refresh or go back during an ongoing scan.</label>
    </form>
  </div>

  <div id="loading" style="
        display: none;
        padding: 30px;
        text-align: center;
        color: rgb(255, 119, 1);
      ">
    Scanning the server configuration of <span id="hostname-text"></span>...
  </div>
  <div id="return_output" style="padding: 10px; text-align: center; color: rgb(255, 119, 1)">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
      $("form").submit(function (event) {
        event.preventDefault();
        var hostname = $("#hostname").val();
        $("#hostname-text").text(hostname);
        $("#submit-btn").attr("disabled", true);
        $("#loading").show();
        $("#return_output").empty(); // Remove previous output message and download link when a hostname is submitted
        $.ajax({
          type: "POST",
          url: "/scan",
          data: $("form").serialize(),
          success: function (response) {
            // Display the output message
            $("#return_output").text(response.return_output);
            // Check if download link is available in response
            if (response.download_link) {
              // Create a download link element
              var downloadLink = $("<a>")
                .attr("href", response.download_link)
                .attr("download", "report.html")
                .text("   Download Scan Report");
              // Append download link element to the return_output div
              $("#return_output").append(downloadLink);
            }
          },
          error: function (error) {
            console.log(error);
          },
          complete: function () {
            $("#submit-btn").attr("disabled", false);
            $("#loading").hide();
          },
        });
      });
    </script>
  </div>
</body>

</html>

(Modified) web.config

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <handlers>
            <add name="TesterHttpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" />            
        </handlers>
        <httpPlatform stdoutLogEnabled="true" stdoutLogFile="C:\inetpub\wwwroot\scanner\logs" processPath="C:\inetpub\wwwroot\scanner\env\Scripts\python.exe" arguments="-m flask run --port %HTTP_PLATFORM_PORT%">
            <environmentVariables>
                <environmentVariable name="Ubuntu_Executable" value="C:\inetpub\wwwroot\Ubuntu\Ubuntu_2004.2021.825.0_x64" />
            </environmentVariables>
        </httpPlatform>
    </system.webServer>    
</configuration>
PeaBee
  • 3
  • 4
  • Two red flags. 1) Python apps should be hosted via HttpPlatformHandler, https://halfblood.pro/running-flask-web-apps-on-iis-with-httpplatformhandler/ 2) Web apps run on IIS differently, https://halfblood.pro/web-application-differences-in-visual-studio-and-iis-60fec7e311b3 so the same code might fail even if you test with your own account in your own logon session. – Lex Li May 15 '23 at 19:05
  • Thank you @LexLi for your assistance, but unfortunately the problem still persists. The changes are: 1) Modified the web.config and the flask code (I have attached those once again) 2) Added **Module Mapping** in the Handler Mappings section of the website in IIS having requested path as '*' and Module as 'httpPlatformHandler', the executable field is empty and have give it a name. Under the 'Request Restrictions', I have unchecekd the "Invoke Handler only..." clause. 3) Currently in the log file it shows that "WSL has no installed distributions error", although I have installed WSL. – PeaBee May 16 '23 at 07:59
  • May you can refer to this case: https://stackoverflow.com/questions/44829878/trying-to-use-bash-on-windows-and-got-no-installed-distributions-message. Or you can try to reinstall. – TengFeiXie May 16 '23 at 09:16
  • @TengFeiXie Actually, installation of WSL is not the problem based on my limited knowledge, because, if the absence of WSL distros were to be a problem then that should have been reflected while running the application locally as well, which it isn't. (Although as per your suggestion I will try to reinstall the WSL). I am not sure but I think I am making mistakes either with the web.config file or with Local IIS or I don't know maybe with the code that I have written. – PeaBee May 16 '23 at 11:07

0 Answers0