zoukankan      html  css  js  c++  java
  • LD_PRELOAD & putenv() 绕过 disable_functions & open_basedir

    这次TCTF中一道题,给出了一个PHP一句话木马,设置了open_basedir,disable_functions包含所有执行系统命令的函数,然后目标是运行根目录下的/readflag,目标很明确,即绕过disable_functions和open_basedir,当然我还是一如既往的菜,整场比赛就会做个签到,这题也是赛后看WP才明白。

    LD_PRELOAD

    LD_PRELOAD是Unix中的一个环境变量,用于定义在程序运行前优先加载的动态链接库,LD和动态库有关,PRELOAD表示预加载,结合起来就是预先加载的动态库。通过这个环境变量,可以覆盖正常的函数库中的函数。

    写个验证密码是否正确的demo

    如果是不知道密码的情况下,就可以编写一个动态函数库来覆盖掉strcmp函数恒返回0以达到任意密码都返回Correct

    -fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code), 则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意 位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的

    设置环境变量后,任意密码都将返回Correct

    putenv

    PHP中putenv()函数用于设置服务器环境变量,仅存活于当前请求期间。 在请求结束时环境会恢复到初始状态。

    putenv ( string $setting ) : bool

    putenv("LD_PRELOAD=/tmp/evil.so");

    劫持系统函数绕过disable_functions

    这种方法已经存在了几年,它基于这样的概念:当系统试图调用某函数时,该函数位于特定的共享库(xxx.so)。因此,系统在调用之前将加载xxx.so。换句话说,如果我可以创建一个evil.so有了同名的函数,就能将其覆盖之。

    所以需要找到一个php函数,它将运行一个新进程以及触发上述过程。新进程的原因就像前面提到的一样,需要重新启动服务以更改LD_PRELOAD,这里用到PHP mail() 函数

    在Linux中,进程是不能直接去访问硬件设备的,比如读取磁盘文件、接收网络数据等,但可以将用户态模式切换到内核模式,通过系统调用来访问硬件设备。这时strace就可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间、调用次数,成功和失败的次数。

    可以看到 这段PHP代码(mail()函数)使用 execve 在父进程中 fork 了一个子进程,调用了 /usr/sbin/sendmail 

    接着看下sendmail中调用了getuid()函数,(可以用readelf查看sendmail中使用了哪些函数,如readelf -Ws /usr/sbin/sendmail,但我环境似乎有问题执行不成功,所以还是用到strace)

    所以可以编写一个动态库覆盖getuid()函数

    一开始我是这么写的,虽然也能成功执行命令

    但是服务器卡顿,一堆报错,然后关了SSH后就连接不上了,最后只得在控制台重启,应该是将程序中所有的getuid()都覆盖造成某些地方严重出错,所以改为如下,在第一次运行到我的写的getuid()函数时就unsetenv这个环境变量,保证之后的正常调用

    在PHP代码中设置环境变量,并调用mail函数触发getuid(),最后成功执行命令

    劫持共享库

    __attribute__ 是 GNU C 里一种特殊的语法,语法格式为:__attribute__ ((attribute-list)),若函数被设定为constructor属性,则该函数会在main()函数执行之前被自动的执行。类似的,若函数被设定为destructor属性,则该函数会在main()函数执行之后或者exit()被调用后被自动的执行。

    Q

    1. When exactly does it run?
    2. Why are there two parentheses?
    3. Is __attribute__ a function? A macro? Syntax?
    4. Does this work in C? C++?
    5. Does the function it works with need to be static?
    6. When does __attribute__((destructor)) run?

    A

    1. It's run when a shared library is loaded, typically during program startup.
    2. That's how all GCC attributes are; presumably to distinguish them from function calls.
    3. GCC-specific syntax.
    4. Yes, this works in C and C++.
    5. No, the function does not need to be static.
    6. The destructor is run when the shared library is unloaded, typically at program exit.

    以上回答说的很清楚,__attribute__((constructor)) 在加载共享库时就会运行。于是只要编写一个含__attribute__((constructor)) 函数的共享库,然后在PHP中设置LD_PRELOAD环境变量,并且有一个能 fork 一个子进程并触发加载共享库的函数被执行,那么就能执行任意代码达到bypass disable_functions。当然mail()函数满足条件。

    编写共享库

    执行PHP

    同理,当在linux中设置了环境变量后,那么使用任意命令都将触发

     通用payload

    evil.c

    #include<stdlib.h>
    __attribute__((constructor)) void l3yx(){
        unsetenv("LD_PRELOAD");
        system(getenv("_evilcmd"));
    }

    evil.php

    <?php
    putenv("_evilcmd=echo 1>/root/tmp/222222");
    putenv("LD_PRELOAD=./evil.so");
    mail('a','a','a','a');

    gcc -shared -fPIC -o evil.so evil.c


    参考:

    警惕UNIX下的LD_PRELOAD环境变量

    如何使用GCC生成动态库和静态库

    Bypass disable_functions with LD_PRELOAD

    How exactly does __attribute__((constructor)) work?

    无需sendmail:巧用LD_PRELOAD突破disable_functions

  • 相关阅读:
    vagrant 入门3
    vagrant 入门4
    vagrant 入门2
    Map、Debug追踪
    Comparator比较器 、Comparable接口
    File类
    Lambda表达式、函数式编程思想概述
    异常--异常体系、异常的处理、异常的捕获、finally语句块和自定义异常
    List集合、Set集合、Collection集合工具类
    数据结构---栈、队列、数组、链表和红黑树
  • 原文地址:https://www.cnblogs.com/leixiao-/p/10612798.html
Copyright © 2011-2022 走看看