Contents
Introduction to REX
A First Example
Security and REX
Example Uses of REX
Framework Options
Command Line Interface
Web Interface
Coding REX Files
Using the Framework API
Introduction to REX
REX is a proof-of-concept
Remote
Execution framework for PHP.
In simple terms the REX framework downloads PHP source code (referred to as REX code owing to the specific markups and commands available) from a location (either from a local filebase or remotely via HTTP/S) and executes it.
This is similar to running any PHP script the difference being the source code is loaded as text from elsewhere and then executed by the client.
User REX Client Code Store
Request ----> REX Starts
Code Request ------> HTTP (or other)
Code Received <----- HTTP (or other)
Output <----- Code Executes
For example the user, via the CLI or Web interface might request the REX client to run code located at http://codeserver/rex/
The REX client will start and then request the code located at http://codeserver/rex, download the source code into memory and then execute it like any other PHP code. Output generated is then passed back to the user.
About the Documentation
Please note these docs were hammered out quickly and dirtily - they will no doubt contain endless spelling, grammer and sense errors.
Please forgive me.
Back to the topA First Example
As a first example let's create a
Hello World application.
For this example we'll assume the user is sitting at the CLI of a REX enabled Linux client (called rexclient). The code will be uploaded
onto the server (called codeserver) and published via apache in the directory http://codeserver/rex/
The REX Code
REX code is simply PHP but usually saved with a .rex extension (to stop it being executed by apache when requested).
Here is the sourcecode for our file index.rex:
<?php
echo "Hello World!"
?>
We save this file to the directory published by our codeserver as /rex/ (usually something like /var/www/html/rex in the codeserver file system).
Two key points:
- We name the file index.rex as (unless otherwise configured) REX will look for that file when given a directory as a codebase
- We save the file with a .rex extension not only so REX can find it but also if anyone browses manually to http://codeserver/rex/index.rex it will not be executed by codeserver as PHP (we don't want this - we want it to load into REX and be executed there).
Executing on the REX-enabled client
Now the code has been created and saved onto the codeserver we can request and execute it on the client:
$ php rex.php http://codeserver/rex/
Will start REX and tell it to download the file from http://codeserver/rex/ (because this is a directory it will try the file index.rex in that directory).
Expected Output
$ php rex.php http://codeserver/rex/
Hello World!
$
And there you have it - your first REX application.
Note a similar example (and others) are available hosted at PurplePixie for you to browse online or request via REX. You can find these on the
examples page.
Back to the topSecurity and REX
The REX framework offers
no built-in security.
Given the whole point of the system is to allow execution of arbitrary code it is potentially a serious security risk unless measures are taken to secure your implementation.
There are two primary risk factors when using REX:
Poisoning of Code Store
As REX downloads code from a store and executes it any unauthorised access to or modification of the code store could lead to malicious adulterated code being run on the client systems.
Of course this is a risk whenever code (or binaries) are downloaded from a remote site but with a traditional method (installing the program on the client) any poisoned code must be present at the point of download.
With REX as the code is loaded "fresh" on each execution the theoretical risks of compromise are higher.
So it is important to keep your code server secure and be careful where you are executing REX code from.
Execution of Arbitrary Code
If the REX executor (or your own implementation using the API) is exposed in a manner allowing filenames for execution to be passed then a malicious agent could simply setup their own "bad" code store and get your clients to download and execute it.
Always ensure your REX executor is secure, at the very least by limiting access to the web interface through HTTP-AUTH or specific HTTP access rules.
Back to the topExample Uses of REX
A remote execution system can be useful in a number of situations, primarily where you wish to commonly execute the same code over a wide number of client systems or have a specific need to execute code on a client without using alternative methods.
Large Number of Clients
In a network situation with a large number of clients it may be required for them all to execute the same code on a regular basis, for example checking if their installed applications are up-to-date or performing cleanup.
Although you could have the code installed on each and every client this can be problematic for maintenance (any changes must be replicated to all the clients).
Of course you could just store your code on a network-accessible location such as a NFS or CIFS share and have the clients run it from there - or you could use a remote execution framework such as REX to specifically pull down and execute code from a central location.
In this manner any changes made to the centrally stored code will be immediately reflected to all clients the next time they run it.
Execute Code on a Remote Client
If you have some code you wish to execute on one (or more) clients then the "normal" method would be to open an interactive session on the client (using SSH or RDP), copy the code up and then execute.
However if a client has the REX framework installed and web-accessible the code can be run on the client with a single HTTP request to the web interface of the REX executor.
Client-Specific Code
Of course because the code is executed on the actual client itself then you can have selection operations based upon the client - you could get the hostname or scan any type of configuration or files (for example to determine if the client was Win32 or Linux and take action accordingly).
Back to the topFramework Options
The following options can be set within the client framework either via the
CLI,
Web or
API dependent on how you are calling REX.
- Filename(s)
A filename (or directory name where the default filename will be tried) or filenames (if you are using the REX executor - calling via the CLI or Web not using the API) for REX to execute.
- Default
The default filename to try when a directory is requested (the default default is index.rex).
- Block
The size of blocks to load REX files as, defaults to 4096 bytes (4kB).
- Quiet
In quiet mode the REX framework won't output any syntax or other execution errors found with files. It defaults to off - errors will be output.
- Base
The Base directory. This defaults to blank and is set by the first file loaded (to the directory of the file) unless already set by the user. For example once you request a file at http://codeserver/rex/ then this directory is the default and any files then requested without a leading http(s): or file: are requested from this directory.
- Load HTTP Environment
Only available with the Web executor (or via a method call from the API) this option loads the standard HTTP environment variables into the REX Environment stack for easy use by REX code.
Executor Options
The following options are available only if you are using the REX executor (the CLI or Web client). If you are using the framework directly via the API then your code will handle these instead.
- Mute
With mute on the executor won't output any errors processing the REX files (default is off - errors are shown).
- Continue
With continue on the executor will continue to execute any more REX files in it's filename list even if one has encountered an error. With it off (the default) then execution of following files will cease once an error is encountered.
Please note: this does not affect the behaviour of files called from within REX code using the ExecFile() method - just ones listed for the executor to execute one after the next
- Debug
The Debug option when enabled (default off) has the executor output lots of debug information such as the initial state, each file executed etc.
Back to the topCommand Line Interface
The REX executor (rex.php) offers a Command-Line Interface (CLI) to fetch and execute remote code.
Example:
$ php rex.php http://rex.purplepixie.org/examples/hello-world/
The above instructs REX to fetch the code from rex.purplepixie.org/examples/hello-world/index.rex and execute it displaying any output to the screen.
In addition to the file (or files) specified there are a number of switches and options you can provide.
These control the settings detailed in the
framework options section of the documentation.
CLI Switches and Options
- --help
Display command help and exit
- --quiet=1 (or --quiet=0)
Turn on (1) or off (0) the quiet mode (parsing errors are output or not)
- --mute=1 (or --mute=0)
Turn on (1) or off (0) muting of the executor errors (similar to quiet but quiet is for the framework not the executor)
- --blocks=X
Set the block size (fetching blocks) to this size in bytes
- --base=X
Set the base directory to X. The base is normally set as the first directory used so any unqualified files then requested are loaded from that directory. This overrides that and sets the base, allowing you to specify just filenames to execute (the X can be enclosed in quotes if required for spaces in the path name)
- --default=X
Sets the default file to look for if just a directory is called (the default is index.rex)
- --continue=1 (or --continue=0)
Continues requesting and executing the files listed even if an error has occured (default is off so execution stops)
- --debug=1 (or --debug=0)
Turns on the debug mode including outputting all the states before execution and showing each file starting and finishing execution.
REX Stack and Environment Variables
The CLI can be used to specify REX stack variables and REX environment variables on startup.
- --set variable=value
Sets a stack variable of name 'variable' to 'value'
- --env variable=value
Sets an environment variable of name 'variable' to 'value'
Note that both the variable names and values can be quoted strings if required to include data with spaces.
Filename(s)
Anything passed and not prefixed with -- will be treated as a filename to be loaded into the list for execution. These are done in order.
Please note however that the options are set once at runtime and not parsed in order with the filenames so, for example, if you call a filename then set the base and then call another filename both will have the base set when executed.
Examples
$ php rex.php --debug=1 http://codeserver/rex/
Will execute the file http://codeserver/rex/index.rex with debug output
$ php rex.php --base="http://codeserver/my files/" code1.rex code2.rex
Will execute first code1.rex then code2.rex from the directory "http://codeserver/my files/"
$ php rex.php --set somevar="something or other" --default=rexcode.php http://codeserver/rex/
Will set the variable somevar to be "something or other" in the variable stack and then execute http://codeserver/rex/rexcode.php
Back to the topWeb Interface
The REX executor (rex.php) offers the ability to make requests to a REX client via HTTP.
An example script calling REX with the relevant variables is provided in the download package as index.php.
REX options (as detailed in the
framework options section of the documentation are set through HTTP variables (using either GET or POST).
If a specific option or variable is not found then it is ignored.
HTTP Option Variables
- rex_debug
If set to 1 will turn on debug output from the executor
- rex_load_http
If set to 1 will load the contents of common HTTP environment variables ($_SERVER, $_REQUEST, $_POST, $_GET and $_COOKIE) into REX environment variables
- rex_continue
If set to 1 will continue executing files after an error has occurred
- rex_quiet
The REX framework won't output syntax or other parse errors if set to 0
- rex_mute
The rex executor won't output errors encountered if set to 0
- base
Set the base to the directory specified in the variable
Filenames to Execute
Pass the variable rex_filename either as a single variable or as an array i.e. rex_filename[0]=file1.rex rex_filename[1]=file2.rex
Stack and Environment Variables
To set stack variables pass an array of rex_set[] for the variable names with the values in rex_val[] corresponding for each entry. For example rex_set[0]=hello and rex_val[0]=world will set the variable hello to equal world.
Environment variables use exactly the same system but with the names rex_env_set[] and rex_env_val[].
Back to the topCoding REX Files
REX files are simply PHP files with access to a specific framework to support their operation.
PHP code in the files must be prefixed with a <?php and closed with a ?> (like any PHP script).
As the code is executed with a function any PHP variables (other than super-globals) you wish to access or work with must be defined with the global command.
The REX framework object itself is available to REX scripts as $REX and they can access all the the
API methods and properties that are available.
Executing Other Files
A REX should make use of the REX::ExecFile() method to call another file. If not prefixed with a protocol (such as http:// or file://) then the file is executed relative to the base directory.
Usually this means the same directory the current REX file is executing from.
<?php
// Do some stuff...
$REX->ExecFile("another.rex");
?>
The above script will "do some stuff" and then execute the file another.rex (if the base directory property is unmodified this will be called from the same directory as the original script.
Variables
REX files can make use of standard PHP variables that are in scope. In effect this means super-globals and anything that is specifically named in a global statement.
Variables persist through the execution of future files so can be used to pass data.
As well as using native PHP variables there are two sets of REX variables; the variable stack and environment variables.
Stack variables are set with REX::Set() and recalled with REX::Get()
<?php
$REX->Set("my_variable","hello");
echo $REX->Get("my_variable");
?>
The above code will set my_variable to be "hello" and then echo it back in the output.
To see more detail on this and also the environment variables please see the
API documentation.
A good example showing use of variables is the counter example (available from the
examples page.
Back to the topUsing the Framework API
You can access REX and use it directly in your own client-side code via the API.
The executor that ships with REX (rex.php) should provide a fairly good example of how you can use REX from code.
Include the REX Framework and Instantiate the Object
<?php
include("rex.inc.php");
$REX = new REX();
?>
This will include the framework code and create a REX object ($REX).
REX Properties
- $Base (string)
Base directory to be used for execution (if left blank it will be automatically assigned as the directory of the first codefile executed)
- $Error (bool)
Indicates an error has been encountered
- $ErrorList (array[ string ])
Textual list of errors encountered
- $Quiet (bool)
If set do not show parsing errors (default false)
- $Blocks (int)
Size of blocks to fetch from remote files (default 4096)
- $Default (string)
File to try and load if a directory is passed to execute (defaults to index.rex)
- $Env (array[ mixed ])
Array of environment variables (best not to operate on directly - use the methods)
- $Var (array[ mixed ])
Array of stack variables (best not to operate on directly - use the methods)
REX Methods
- mixed REX::Get( variable[string] )
Get the named variable from the stack (returns false if not found)
- null REX::Set( variable[string], value[mixed] )
Set the named variable in the stack to the value passed
- null REX::EnvLoadArray( array[array], optional name[string] )
Loads an array into the environment variables. If a name is passed the array is loaded under that name, if no name then the array is processed and each key in it is loaded into the environment variables as its own variable.
- null REX::EnvLoadHTTP()
Load common HTTP environment arrays ($_SERVER, $_COOKIE, $_REQUEST, $_GET and $_POST) into REX environment variables under the name _SERVER etc.
- null REX::EnvLoad( variable[string], value[mixed] )
Load a named variable into the environment variables with the value specified
- bool REX::Exec( filename[string] )
Fetch and execute filename (note this is the correct method for calls from the API, not REX::ExecFile() which is used from within REX code). Returns true if that file has executed without errors.
- bool REX::ExecFile( filename[string] )
Fetch and execute file, to be used in REX code not the API (the API call is REX::Exec())
Back to the top