How to have access to a shell without ssh or telnet using jQuery terminal

If you have, like I do, a virtual server on hosting service, that support cgi but don’t have access to a shell and php have passthru disabled you still can have access to a shell, even if you can’t install anything on that machine – like anyterm.

There is this project, which I did, called jQuery terminal, that emulate a Unix terminal. All you need is interpreter that will execute shell commands.

Lots of hosting services disable php passthru but leave cgi script, so if you have them you can create simple shell like this one:

UPDATE: If you don’t want to code it yourself (or look at working solution), you can check my project LEASH – Browser Shell. You if you prefer there is also available single file Shell using same library.

#!/bin/bash
echo -en "Content-Type: text/plain\r\n\r\n"
query=$(/usr/bin/python -c "import urllib; print urllib.unquote_plus('$QUERY_STRING')")
eval $query 2>&1

I use python subprocess to decode QUERY_STRING. Probably it can be done using some Unix tools.

On my server I use Python CGI script like this one:.

#!/usr/bin/python
import cgi
import subprocess as sub

try:
    from json import dumps as json_serialize
except ImportError:
    def json_serialize(obj):
        result = ''
        t = type(obj)
        if t == types.StringType:
            result += '"%s"' % obj.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n')
        elif t == types.NoneType:
            result += 'null'
        elif t == types.IntType or t == types.FloatType:
            result += str(obj)
        elif t == types.LongType:
            result += str(int(obj))
        elif t == types.TupleType:
            result += '[' + ','.join(map(json_serialize, list(obj))) + ']'
        elif t == types.ListType:
            result += '[' + ','.join(map(json_serialize, obj)) + ']'
        elif t == types.DictType:
            array = ['"%s":%s' % (k,json_serialize(v)) for k,v in obj.iteritems()]
            result += '{' + ','.join(array) + '}'
        else:
            result += '"unknown type - ' + type(obj).__name__ + '"'
        return result

class Singleton(object):
    __single = None
    def __init__(self):
        Singleton.__single = self

    def __new__(cls):
        if Singleton.__single:
            return Singleton.__single
        else:
            return super(Singleton, cls).__new__(cls)


class Form(Singleton):
    def __init__(self):
        super(Form)
        self.form = cgi.FieldStorage()

    def __getitem__(self, name):
        return self.form.getvalue(name)

def execv(command, path):
    command = 'cd %s && %s && pwd 1>&2' % (path, command)
    proc = sub.Popen(['/bin/bash', '-c', command],
                     stdout=sub.PIPE, stderr=sub.PIPE)
    stderr = proc.stderr.read()[:-1]
    stdout = proc.stdout.read()[:-1]
    if not os.path.exists(stderr):
        raise Exception(stdout, stderr)
    return {
        "cwd": stderr,
        "stdout": re.sub('.\x08', '', stdout)
    }

if __name__ == '__main__':
    print "Content-Type: text/plain"
    print
    form = Form()
    if token() == form['token']:
        response = {}
        try:
            response['result'] = execv(form['command'], form['path'])
        except Exception, e:
            response['error'] = e.args[1]
            response['result'] = {'stdout': e.args[0], 'cwd': form['path']}
        else:
            response['error'] = None
    else:
        response = {'error': 'You are not authorized', 'result': None}
    stdout.write(json_serialize(response))

You have enabled passthru in php you can write your shell in php.

Now when you have access to a shell you need to setup interface using jQuery terminal.

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>Shell</title>
    http://code.jquery.com/jquery-1.7.1.min.js
    http://terminal/js/jquery.mousewheel-min.js
    http://terminal/js/jquery.terminal.js
    <link href="terminal/css/jquery.terminal.css" rel="stylesheet"/>
    <style>
    .terminal a.ui-slider-handle:focus { outline: none; }
    body { margin: 0; padding: 0; }
    html { background-color: #000; }
    .clear { clear: both; }
    /* This works only in Safari and Google Chrome */
    @media screen and (-webkit-min-device-pixel-ratio:0) {
        .terminal, .terminal .terminal-output, .terminal .terminal-output div,
        .terminal .terminal-output div div, .cmd, .terminal .cmd span, .terminal .cmd div {
            font-weight: bold;
        }
    }
    </style>
<script>
$(function() {
    var pwd, last_dir, home_dir;
    var terminal = $('#shell').terminal(function(command, term) {
        if (command.replace(/^ *(.*) *$/, '$1') == '-') {
            pwd = last_dir;
            return;
        }
        term.pause();
        $.post("/cgi-bin/cmd.py", {
            token: term.token(),
            command: command.replace(/(\\)?~/, function($0,$1) {
                return $1 ? $0 : home_dir;
            }),
            path: pwd
        }, function(response) {
            term.echo(response.result.stdout);
            pwd = response.result.cwd;
            term.resume();
        });
    }, {
        login: function(user, password, authenticate) {
            $.post("/cgi-bin/cmd.py", {
                user: user,
                password: password
            }, function(token) {
                if (token) {
                    home_dir = pwd = last_dir = '/home/ + user;
                }
                authenticate(token);
            });
        },
        prompt: function(callback) {
            var username = terminal.login_name();
            var re = new RegExp("^" + '/home/' + username);
            var username = '[[;#44D544;]' + username + ']';
            var path = '[[;#5555FF;]' + pwd.replace(re, '~') + ']';
            callback(username + '[[;#989898;]:]' + path + '[[;#989898;]$] ');
        },
        name: 'shell'
    }).css({
        overflow: 'auto'
    });

    $(window).resize(function() {
       terminal.css('height', $(window).height()-20);
    }).resize();

});
</script>
</head>
<body>
  <div id="shell"></div>
</body>
</html>

Published by

jcubic

I'm a web developer from Poland with a focus on JavaScript.

One thought on “How to have access to a shell without ssh or telnet using jQuery terminal”

Leave a comment