0

Despite the keywords PHP // Javascript // Load // URL showing lots of results on the web, I am not able to make things clear in my head. I am trying to learn with real cases studies, but I'm a bit lost right now.

I built a web interface for some command line scripts.
1 - The user enters a word in an HTML form,
2 - The form is submited to a PHP script,
3 - The script executes the command on the server and returns the result in the HTML page.
So far so good.

But the scripts may take a long time to process, so I would like to change the way the result is displayed.
I would like to use [ttyd][1], a terminal sharing utility that would allow me to get a beautiful result in realtime:
1 - The user enters a word in an HTML form,
2 - The form is submitted to a PHP script,
3 - The script executes the command on the server, opens a port, and wait for an HTTP connexion,
4 - The HTTP connexion is made and the result appears in an iframe.
and returns the result on the HTML page.

Here is my PHP sample:

<?php

require __DIR__ . "/vendor/autoload.php";

ini_set("display_errors", "1");
ini_set("display_startup_errors", "1");
error_reporting(E_ALL);

//Securize user inputs 
function securize($p)
{
    $v = htmlspecialchars($p); //Convertir les caractères spéciaux
    $v = trim($v); //Supprimer les espaces dans la requête
    $v = rtrim($v); //Supprimer les espaces à la fin de la requête
    $v = strtolower($v); //Tout mettre en minuscule
    $v = strip_tags($v); //Supprimer les balises html dans la requête
    $v = stripslashes($v); //Supprimer les slash dans la requête
    $v = stripcslashes($v); //Supprimer les backslash dans la requête
    $v = escapeshellcmd($v); //Protège les caractères spéciaux du Shell
    return $v;
}

//Tool selector
if (isset($_POST["search"])) {
    $term = securize($_POST["search"]);
    if (!empty($term)) {
        $port = rand(5000, 6000); //Get a random port for ttyd
        if ($_POST["tool"] == "ghunt") { 
            if (mailValidate($term)) {
                $tool = "python3 ghunt.py email";
                $cli = (craft($tool, $term, $port));
                passthru($cli);
                die();
            } else {
                echo "<script type='text/javascript'>alert('$term n\'est pas une adresse mail valide');</script>";
                die();
            }
        }
}

// Mail validation
// Takes user input
function mailValidate($p)
{
    if (filter_var($p, FILTER_VALIDATE_EMAIL)) {
        $v = true;
    } else {
        $v = false;
    }
    return $v;
}


// Command prep
// Takes command line + User input + port
function craft($tool, $term, $port)
{   
    $v = "ttyd -o -R -p $port $tool $term";
    return $v;
}

?>

So far, all my attempts failed.
I tried to set a PHP redirection header("Location: http://ipadress:$port/")
But the PHP script seems to stop right after executing the command. So I also tried to use Javascript and Jquery with the load() function, but do not know where to put things.

Isn't PHP able to handle multiple "actions" like executing and redirect?

Here is a sample of my HTML form:

<body>
    <form method="POST" action="search.php" target="iframe">
        <fieldset>
            <legend>Recherchez un mail, un téléphone ou un pseudo...</legend>
            <input type="search" placeholder="mail, téléphone ou pseudo" name="search">
            <br>
                <div class="fields">
                <fieldset>
                    <legend>Email</legend>
                    <input type="radio" name="tool" value="ghunt">
                    <label for="ghunt"><b><div class="tooltip">Ghunt<span class="tooltiptext">Informations d'un compte google</span></div></b></label>
                </fieldset>
                </div>
            <input class="button" type="submit" name="submit" value="Seek">
        </fieldset>
    </form>
        <iframe id="ttyd" name="iframe"> </iframe>
</body>

Many thanks for your lights [1]: https://github.com/tsl0922/ttyd

MrNonoss
  • 21
  • 7
  • A PHP scripts runs, collects output, then finishes. The output is then sent to the browser as the server's response (and displayed, unless ajax was used to request the URL). ttyd however creates a web server that displays a remote terminal. To use that in your web app you need to pick a fixed port, then set the iframe src to `yourserver:fixed_port`., That header line redirects to the ttyd server, so it should work in theory; how is the form sent to the server? Can you show the form's HTML? How does the iframe matter here? Are you using jQuery to submit the form? –  Nov 02 '21 at 11:40
  • Thanks for your quick reply @ChrisG, I would like to avoid a fixed port to make several users at the same time. I will add the HTML form in the first post. However, if I set the iframe with an adress that do not exists yet, it will display an error right? – MrNonoss Nov 02 '21 at 11:54
  • Yes, you'll get a timeout / unable to connect error. –  Nov 02 '21 at 11:58
  • Sending that form will make the browser navigate to the result. Therefore the iframe is gone, and the browser will simply show the output of your PHP script. It's not outputting anything though. You probably need to use ajax to send the request, then set the iframe src to the ttyd server url. This requires knowing the port that PHP chose though. –  Nov 02 '21 at 12:19
  • Not sure I understand. Without the TTYD "enhancement" the result appears correctly in the given iframe. If I need to use ajax to submit the request, why can't I define the random port here? – MrNonoss Nov 02 '21 at 14:43
  • I don't see how the result could appear in the iframe given your HTML. Again, are you already using ajax to submit the form and show the result in the iframe? –  Nov 02 '21 at 17:07
  • Thank you again for your help. Probably because of the "target" in ```
    ``` The form is only submit with an ```input type="submit"```, so no ajax yet, because I do not know how to deal with it.
    – MrNonoss Nov 02 '21 at 17:21
  • I see, I missed that. If you run `die($cli);` instead you should see the console command inside the iframe. Can you reproduce that? –  Nov 02 '21 at 19:12
  • No problem ^^ Yes, ``die($cli)`` instead of ``passthru($cli)`` shows the command line in the iframe. But this way, the command is not executed. – MrNonoss Nov 03 '21 at 07:58
  • Ok, so basically everything works as expected. Now call passthru and try the header() command again to redirect, what happens in the iframe? –  Nov 03 '21 at 10:13
  • If I add `header("Location: http://ipaddr:$port");` before OR after passthru the server executes `$cli` but nothing appears in the iframe – MrNonoss Nov 03 '21 at 12:29
  • Ok, but you are not literally redirecting to `ipaddr`, right? You do have the actual ip address in there? –  Nov 03 '21 at 12:52
  • Yes sure :) The IP of my webserver this IP is correct, because if I enter it manually in the browser it works – MrNonoss Nov 03 '21 at 13:17
  • Ok, try something like `header("Location: https://google.com");` does that work? –  Nov 03 '21 at 13:20
  • It is the exact same result – MrNonoss Nov 03 '21 at 13:47
  • What do you mean, does the iframe stay blank? –  Nov 03 '21 at 13:48
  • Yes, blank, and no errors are shown in dev tools, nor in PHP. Nothing appears inside the – MrNonoss Nov 03 '21 at 13:50
  • If that's the case, something else is wrong. When I try this I get the warning about google not being allowed to be embedded in another website. –  Nov 03 '21 at 13:57
  • Well I got something if I kill the ttyd process launched by `passthru` – MrNonoss Nov 03 '21 at 14:15
  • Right, try using exec() instead and find a way to run the command so that PHP doesn't wait for the process to end –  Nov 03 '21 at 14:29
  • Well... I did massive change adding a PHPSession in order to create a random port and 3 javascript functions to: 1-`formSubmit()`: submit the form, 2-`changeUrl()`: adding src inside the iframe while grabbing the port, 3-`bind()`: a bind function containing the two above. The issue now is: If I use the bind function, changeUrl wait for formSubmit to terminate (which is not possible since it waits for incoming connexion). If I use two separate buttons, it works fine. Don't know how to deal with this now :/ – MrNonoss Nov 05 '21 at 10:25
  • The basic idea is: 1. send form data to server 2. PHP runs ttyd without waiting for it to finish, then replies with chosen port 3. JS reads reply, changes iframe src according to port from reply –  Nov 05 '21 at 10:28
  • Thanks again! Almost that. I provide ttyd with an already chosen port and `changeUrl()` sets a correct src. The thing is I do not know how to make PHP go on and reply. Every things I tried doesn't work (adding `2>/dev/null` or `&` at the end of the command). I don't get why this function is executed first, event if I ask `changeUrl()` first, like `onclick="changeUrl(); formSubmit();"` – MrNonoss Nov 05 '21 at 11:01
  • Did you try the exact syntax found [here](https://stackoverflow.com/questions/3819398/php-exec-command-or-similar-to-not-wait-for-result)? Can you post your exec line (add it to the question)? –  Nov 05 '21 at 11:30

1 Answers1

0

Thanks to @chris-g and lots of debug sessions, I was able to find both solutions to achieve my goal and troubleshoot the issues.

Here are the steps achieved:

  • Start a PHP session and set a random port as variable.
  • Grab this PHP variable in a JavaScript variable.
  • Create an external JavaScript file containing 3 functions: formSubmit(), changeUrl() and bind() (form submission, iframe src and bind of the two functions).
  • Create a button that calls bind().
  • Adapt PHP script to also grab the port and to use it in the exec().

That was supposed to work since formSubmit() sends the data to the PHP script and changeUrl() adds the correct src in the iframe, but for some reason, it was not.
The iframe was only loading if the "ttyd" process was terminated.

Here are the code samples:

index.php

<?php
// Start the session and set a random port in the "ttyd" variable
session_start();
$_SESSION["ttyd"] = rand(5000, 6000);
?>

<!DOCTYPE html>
<html>
  <head>
    [...]
  </head>
  <body>
    <script>var site = '<?php echo "http://51.91.108.124:$_SESSION[ttyd]"; ?>'</script> <!-- Grab session "ttyd" var in JavaScript -->
    <script src="js.js"></script> <!-- Include JS script file -->
    <form name="osint" method="POST" action="search.php" target="iframe">
      <fieldset>
        <legend>Recherchez un mail, un téléphone ou un pseudo...</legend>
        <input type="search" placeholder="mail, téléphone ou pseudo" name="search">
            [...]
        <div class="fields">
          <fieldset>
            <legend>Email</legend>
            <input type="radio" name="tool" value="ghunt">
            <label for="ghunt"><b>
                <div class="tooltip">Ghunt<span class="tooltiptext">Informations d'un compte google</span></div>
              </b></label>
          </fieldset>
            [...]
        </div>
            [...]
        <input type="button" class="button" onclick="bind();" value="Seek!">
      </fieldset>
    </form>
    <iframe class="iframe" name="iframe"> </iframe>
  </body>
</html>

js.js

//Form submission
function formSubmit() {
    document.forms["osint"].submit();
}
//Load iframe
function changeUrl() {
    document.getElementsByName("iframe")[0].src = site;
}
//Bind functions
function bind(){
    changeUrl(); formSubmit();
}

search.php

<?php
session_start();
require __DIR__ . "/vendor/autoload.php";

//Securize user inputs
function securize($p)
{
    $v = htmlspecialchars($p); //Convertir les caractères spéciaux
    $v = trim($v); //Supprimer les espaces dans la requête
    $v = rtrim($v); //Supprimer les espaces à la fin de la requête
    $v = strtolower($v); //Tout mettre en minuscule
    $v = strip_tags($v); //Supprimer les balises html dans la requête
    $v = stripslashes($v); //Supprimer les slash dans la requête
    $v = stripcslashes($v); //Supprimer les backslash dans la requête
    $v = escapeshellcmd($v); //Protège les caractères spéciaux du Shell
    return $v;
}
//Mail validation
function mailValidate($p)
{
    if (filter_var($p, FILTER_VALIDATE_EMAIL)) {
        $v = true;
    } else {
        $v = false;
    }
    return $v;
}
//Command line crafting
function craft($port, $loc, $tool, $term)
{   
    $ttyd = "ttyd -o -R -t rendererType=webgl -t disableLeaveAlert=true -t disableReconnect=true -p $port";
    $v = "$loc && $ttyd $tool $term";
    exec($v);
}
//Choix de l'outil
if (isset($_POST["search"])) {
    $term = securize($_POST["search"]);
    if (!empty($term)) {
        if (isset($_POST["tool"])) {
            if ($_POST["tool"] == "ghunt") {
                if (mailValidate($term)) {
                    $location = "cd /home/www-data/GHunt";
                    $tool = "python3 ghunt.py email";
                    $cli = (craft($_SESSION["ttyd"], $location, $tool, $term));
                    die();
                } else {
                    echo "<script type='text/javascript'>alert('$term n\'est pas une adresse mail valide');</script>";
                    die();
                }
        } else {
        echo "<script type='text/javascript'>alert('On ne fais pas de magie. Choisissez un outil !');</script>";
        die();
        }
    } else {
        echo "<script type='text/javascript'>alert('La nature a horreur du vide, entrez un terme de recherche');</script>";
        die();
    }
}

?>

The issue was related to the iframe. Both the form (target="iframe") and changeUrl() were supposed to input data inside the same iframe, causing troubles.

Since the form isn't supposed to return data, I only had to change the target of the form and send it to an hidden iframe.

MrNonoss
  • 21
  • 7