Cody Butz
Cody Butz

Reputation: 294

Compile, Run, and Return output of C++ program, securly from PHP

I am making a program that will run a persons C++ code from PHP. How can I compile, run, return the output of a C++ program securly? By securly I mean how can I do it without making my system vulnerable to hackers? I guess I would just use the exec comman to GCC to compile the program then run the program using exec. But how can I do this securly?

Upvotes: 0

Views: 2973

Answers (4)

user187291
user187291

Reputation: 53940

check out http://codepad.org, esp. the "how it works" section http://codepad.org/about

Upvotes: 1

arifwn
arifwn

Reputation: 131

I've done it before, but instead running the resulting binary it output the binary as download. The user can compile but they need to download the binary and run it in their computer. Letting users compile and run arbitrary code on your server is a big vulnerability IMO.

Anyway, this is my quick implementation:

index.php

<?php
include 'compiler-gcc-mingw.php';

$docompile = intval($_REQUEST['docompile']);
if($docompile){
    //compile
    setUpDirectory();
    compile();
    if(IsError()){
        //
        cleanUp();
    }
    else {
        getExecutable();
        cleanUp();
        downloadExecutable();
        exit();
    }
} 

$defaultSource = "
#include <iostream>

using namespace std;
int main(){
    cout<<\"Hello Word!\"<<endl;
    getchar();
    return 0;
}
";
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Online C++ Compiler v1.0</title>
<link href="style.css" rel="stylesheet" type="text/css" />       
</head>
<body>
<h1>Online Compiler v1.0</h1>
<p>Online Windows C++ compiler using Mingw compiler </p>

<?php
if(IsError()){
    $error = getError();
    echo '<p><b>Compilation Error! Check your source code</b></p>';
    echo '<p>'.$error.'</p>';
}
?>
<form name="compile" method="post">
<input type="hidden" name="docompile" value="1" />
<p><b>Source Code:</b></p>
<textarea name="source" rows="20" cols="80"><?php 
if($docompile) echo stripslashes($_REQUEST['source']);
else echo $defaultSource;
?>
</textarea>   <br />
<input type="submit" name="Submit" value="Compile">
</form>

</body>
</html>

compiler-gcc-mingw.php

<?php
    $dir = '';
    $exeData;
    $errorFlag;
    $errorDetail = array();

    function makeDir(){
        //
        global $dir;
        $count = 0;
        do{
            $count++;
            $dir = 'source/data'.$count;
        }while(!@mkdir($dir));
    }

    function setUpDirectory(){
        //make source dir : source001, source 002 etc
        //make source file
        global $dir;
        makeDir();
        $source = stripslashes($_REQUEST['source']);
        file_put_contents($dir.'/source.cpp', $source);
    }

    function compile(){
        // compile, get error message, assuming the compiler is in the system PATH
        // cd to compile dir
        global $dir;
        $compileString = 'g++ '.$dir.'/source.cpp -o '.$dir.'/a.exe ';
        global $errorFlag;
        global $errorDetail;
        $output = exec($compileString, $errorDetail, $errorFlag);

    }

    function isError(){
        // if error return true
        global $errorFlag;
        return $errorFlag;
    }

    function getError(){
        // get error detail
        global $errorDetail;
        $data = '';
        foreach($errorDetail as $key=>$value){
            $data .= $value.'<br />';
        }
        return $data;
    }

    function getExecutable(){
        // retrieve exe data to memory
        global $exeData;
        global $dir;
        $exeData = @file_get_contents($dir.'/a.exe');
    }

    function cleanUp(){
        // delete all temporary files
        global $dir;
        $alist = scandir($dir); 
        foreach($alist as $key => $value){
            if(is_file($dir.'/'.$value)) {  
                unlink($dir.'/'.$value);
            }
        }

        rmdir($dir);
    }

    function downloadExecutable(){
        // download to browser
        global $exeData;
        outputFile('program.exe', $exeData);
    }

    /**
    * download content
    * return value: false=cannot output the header, true=success
    */
    function outputFile($filename, $data){
        //Download file
        if(ob_get_contents())
            return false;
        if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE'))
            header('Content-Type: application/force-download');
        else
            header('Content-Type: application/octet-stream');
        if(headers_sent())
            return false;
        header('Content-Length: '.strlen($data));
        header('Content-disposition: attachment; filename="'.$filename.'"');
        echo $data;
    }
?>

Basically it compile the code written in the text area, save it into a temporary file in a temporary folder, compile it (I use mingw compiler), read the resulting binary, remove all temporary files (including *.o and the binary *.exe files) and offer the resulting binary as download to user.

Upvotes: 0

Klark
Klark

Reputation: 8280

Check this project. In short, that entire project is what you are trying to achieve (and the svn subdir I gave you is the sandbox part you need. If you want to do it on your own this is where you should start: chroot. If you are on the windows you need to rely heavily on the windows API. Search msdn for that.

Upvotes: 0

Aurel B&#237;l&#253;
Aurel B&#237;l&#253;

Reputation: 7973

This is something just a little bit more advanced that something you would be able to do with a non-custom server. Most likely. Because most server providers won't let you execute processes, and even if, they SURELY won't let you install GCC there and run potentially unsafe code on their machine (executing processes, remember?)...

I thought of doing something like this (online development tools with multi-language compilers, sandboxes, SVC...) someday, but just because I have a lot of space as well as a custom server right next to my normal computer.

IF you've got a custom server (supposing it's Linux, and most likely LAMP):

  • have gcc or even better g++ installed
  • have a Virtual Machine (such as Bochs) installed, along with a basic install of any Linux (other platform) - this is just for C and D steps
  • put the files on some temporary place
  • use proc_open to start the g++ with the files listed, but make sure you run this as a dedicated user - for example gccuser - a user with NO permissions but to read from the directory where the files are stored
  • A) read out the output (success or the errors / warnings) from g++
  • B) let the user download the result file
  • C) put the result file through a linker, and then write that file on the VM's HDD - THEN run it, and you can show the output to user
  • D) (most advanced) - make a GUI kinda thing (terminal) in JavaScript / AS to talk to the running file in real-time - add a time limit... this however also means you need more than one Bochs running at once (more users) - that means you need to copy a Bochs to a temp directory, together with an empty HDD (with only OS), run it on THAT one, when finished - delete

proc_open - http://php.net/manual/en/function.proc-open.php

Upvotes: 2

Related Questions