terminal_case
terminal_case

Reputation: 134

Include from "php://memory" stream

I'm writing a system for a browser application that will store some particular php scripts in a database and then pull them out and execute them when needed. At first I tried using exec() and piping to php the output of a script that got the scripts out of the database and printed them. This worked in one use case, but not all, and feels brittle anyway, so I'm looking for a better way.

I'm now attempting to accomplish this through use of a PHP file stream in memory. For instance:

$thing = <<<'TEST'
<?php

$thing = array();

print "Testing code in here.";
var_dump($thing);

?>
TEST;

$filename = "php://memory";

$fp = fopen($filename, "w+b");
fwrite($fp, $thing);
//rewind($fp);

fclose($fp);

include "php://memory";

However, nothing is printed when the script is executed. Is this even possible by this means, and if not, is there another way to do this? I'm trying to avoid having to write temporary files and read from them, as I'm sure accessing the filesystem would slow things down. Is there a URL I can provide to "include" so that it will read the memory stream as if it were a file?

I don't think eval() would do this, as, if I remember correctly, it's limited to a single line.

Also, please no "eval = include = hell" answers. Non-admin users do not have access to write the scripts stored in the database, I know that this needs special treatment over the life-cycle of my application.

Upvotes: 10

Views: 12360

Answers (3)

hakre
hakre

Reputation: 198209

eval() and include are actually pretty the same. So eval() works with multiple lines - just FYI. However, I would prefer include here, I always think it's faster. Maybe I'm wrong, no Idea.

However, I think you should debug your code, I don't see a reason per-se why it should not work. You might need to rewind the pointer (you have commented that), but what you should check first-hand is, that your PHP configuration allows to include URLs. I know that that setting prevents using of the data:// URIs, so you might have this enabled.

Also you can always try if PHP can open the memory by using file_get_contents and dumping out. This should give you the code. If not, you already made some mistake (e.g. no rewind or something similar).

Edit: I've not come that far (demo):

<?php
/**
 * Include from “php://memory” stream
 * @link https://stackoverflow.com/q/9944867/367456
 */

$thing = <<<TEST
<?php
\$thing = array();
print "Testing code in here.";
var_dump(\$thing);
TEST;

$filename = "php://memory";

$fp = fopen($filename, "w+b");
fwrite($fp, $thing);
rewind($fp);

var_dump(stream_get_contents($fp));

This is what I found out:

  1. You should not close the "file". php://memory is a stream once closed it will disappear.
  2. You need to access the $fp as stream than, which is not possible for include out of the box AFAIK.
  3. You then would need to create a stream wrapper that maps a stream resource to a file name.
  4. When you've done that, you can include a memory stream.
  5. The PHP settings you need to check anyway. There are more than one, consult the PHP manual.

It might be easier to use the data URI (demo):

<?php
/**
 * Include from “php://memory” stream
 * @link https://stackoverflow.com/q/9944867/367456
 */

$thing = <<<TEST
<?php
\$thing = array();
print "Testing code in here.";
var_dump(\$thing);
TEST;

include 'data://text/plain;,'. urlencode($thing);

See as well: Include code from a PHP stream

Upvotes: 6

Xex
Xex

Reputation: 19

If there is a way to include from php://memory then this is a serious vulnerability. While it has many uses, eval is used quite often with code obfuscation techniques to hide malicious code.

(Every tool is a weapon if you hold it right.)

With that said, there (thankfully) doesn't appear to be any obvious way to include from php://memory

Upvotes: 1

pHiL
pHiL

Reputation: 1899

You need to use stream_get_contents to read from the php://memory stream. You cannot include it directly.

Upvotes: 6

Related Questions