info

本期主要介绍webshell中文件操作类函数

imgs

function

filew(file_write写文件)

实现代码

function filew($filename, $filedata, $filemode)
{
    if ((!is_writable($filename)) && file_exists($filename)) {
        chmod($filename, 0666);
    }
    $handle = fopen($filename, $filemode);
    $key = fputs($handle, $filedata);
    fclose($handle);
    return $key;
}

功能用途

用于webshell写入文件

实现思路

首先判断了文件是否存在,如果存在不可写,赋予文件读写权限

知识点

  • chmod 0666 改变文件权限

在这里科普一下777 在Linux下,文件类型包含如下几类:

d:目录directory

l:符号链接link

s:套接字socket

c:字符设备char

p:命名管道pipe

-:其他,不属于以上几类

文件创建后,有三种访问方式

读(read):显示内容

写(write):编辑内容,删除文件

执行(execute):执行文件

针对用户,文件有三类权限:

创建人(user)权限:创建文件的人

组(group)用户权限:和拥有者处于同一用户组的其他人

其他(other):用户权限

首先要清楚一点mode是一个3位八进制数:

第一位表示创建者权限

第二位表示组用户权限

第三位表示其他用户权限

再举一个具体的例子:

400:创建者可读

200:创建者可写

100:创建者可执行

040:组用户可读

020:组用户可写

010:组用户可执行

004:其他用户可读

002:其他用户可写

001:其他用户可执行

所以 chmod($filename, 0666); 是将文件权限变更为所有人都拥有读写权限

拥有者(4 write +2 read)

组用户(4 write + 2 read)

其他用户(4 write + 2 read)
  • is_writable(判断文件是否可写)

    通过上述知识科普,只要nginx/apche/tomcat所属组有文件的写权限即可。

    php还有类似
    is_readable(文件是否可读)

    is_executable(文件是否可执行)

  • file_exists(判断文件是否存在)

    这里有一篇当file_exists遇到eval的文章供大家参考

    https://www.freebuf.com/articles/web/53656.html

  • fopen fclose

    如第一个参数可控,可能存在SSRF漏洞,漏洞利用老哥们可搜索论坛帖子。

    $handle = fopen("/home/rasmus/file.txt", "r");
    
    $handle = fopen("http://www.example.com/", "r");
    
    $handle = fopen("file:///etc/passwd", "r");
    
    $handle = fopen("ftp://user:password@example.com/somefile.txt", "w");

    medium上面有大佬对SSRF进行介绍的帖子,介绍的非常详细,还请各位看官移步medium

    https://medium.com/@madrobot/ssrf-server-side-request-forgery-types-and-ways-to-exploit-it-part-1-29d034c27978
    
    https://medium.com/@madrobot/ssrf-server-side-request-forgery-types-and-ways-to-exploit-it-part-2-a085ec4332c0
    
    https://medium.com/@madrobot/ssrf-server-side-request-forgery-types-and-ways-to-exploit-it-part-3-b0f5997e3739

filer(file_read 读文件)

实现代码

function filer($filename)
{
    $handle = fopen($filename, 'r');
    $filedata = fread($handle, filesize($filename));
    fclose($handle);
    return $filedata;
}

功能用途

用于webshell文件读取

实现思路

直接读取文件

知识点

  • fsopen

    关于mode值的介绍,如果是文件读取的话,一般都是用fopen($filename, 'r')

    具体关于mode的介绍如下所示:

     r". 只读方式打开,将文件指针指向文件头。
    "r+"  读写方式打开,将文件指针指向文件头。
    "w"   写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
    "w+" 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
    "a"   写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
    "a+" 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
    "x"   创建并以写入方式打开,将文件指针指向文件头。如果文件已存在,则 fopen() 调用失败并返回 FALSE,并生成一条 E_WARNING 级别的错误信息。如果文件不存在则尝试创建之。

fileu(file_upload 文件上传)

实现代码

function fileu($filenamea, $filenameb)
{
    $key = move_uploaded_file($filenamea, $filenameb) ? true : false;
    if (!$key) {
        $key = copy($filenamea, $filenameb) ? true : false;
    }
    return $key;
}

功能用途

用于webshell实现文件上传

实现思路

尝试将POST的文件,即$_FILE["file"]["tmp_name"]上传到服务器.如果移动失败使用copy方法进行复制。

知识点

  • move_uploaded_file copy

    初步看了一下官方文档说明,思考move_uploaded_file和copy有什么区别。move_uploaded_file,如果不是通过HTTP POST上传的合法文件,不予移动,所以这里作者又补充了copy写法。

    我们再看一个写法,如果move_uploaded_file和copy方法的第二个参数可控,思考一下会发生什么?

    // $dest = $_GET['dest'];
    $dest = 'xxx/tmp/123.php';
    copy('./xxx.jpg', $dest);
  • POST

    这是说到了文件上传,初学者在开发的过程中可能会遇到?为什么我用_$POST取不到前端POST的数据呢?

    答案是因为前端发送数据格式josn,而使用$_POST数据取不到,应该使用\file_get_contents("php://input")

    $_POST只能取到appilicat/x-www-form-urlencoded和form-data的数据,取不到application/json数据

    如果是application/json POST的数据只可以通过,file_get_contents("php://input")取到。

    具体源码分析详见

    https://segmentfault.com/a/1190000016868502?utm_source=tag-newest

filed(file_download 文件下载)

实现代码

function filed($filename)
{
    if (!file_exists($filename)) return false;
    ob_end_clean();
    $name = basename($filename);
    $array = explode('.', $name);
    header('Content-type: application/x-' . array_pop($array));
    header('Content-Disposition: attachment; filename=' . $name);
    header('Content-Length: ' . filesize($filename));
    @readfile($filename);
    exit;
}

功能用途

用于webshell下载文件

实现思路

首先判断文件是否存在,清空缓存区,根据文件后缀判指定文件类型实现文件下载。

知识点

  • ob_start ob_end_clean readfile

    缓冲区相关内容,不在这里详细赘述。

    reafile将文件内容读取缓冲区

  • @错误抑制符

    试想一下,如果在生产环境,如果服务器在运行中产生了一个错误,会报XX/xxx.php. xxx 行出现问题,这样就会泄露一些服务器的一些敏感信息,这样的结果通常是我们不希望看见的。所以可以在函数前面增加@符,隐藏这些错误信息。

    和@符类似的方法还有

    error_reporting(0);
    
    ini_set('display_errors', 'Off');
  • Mime-Type

    互联网媒体类型,也叫做MIME类型。最初MIME是用于电子邮件系统的,后来HTTP也采用了这一方案。

    Mime-Type 对照表

    http://tool.oschina.net/commons/

    在测试上传功能点时老生常谈了,某些开发只校验了Mime-Type,并没有在后端校验文件后缀,直接导致了get-shell.或者见过一些开发连文件后缀都不会取。

    既然在这里提到了文件上传,那就稍微补充一点,我自己认为文件上传存在的风险点了:

    1、中间件漏洞,apache、nginx、IIS、ImageMagick
    2、加载远程图片,限制host和协议,避免出现ssrf
    3、路径穿越
    4、并发竞争
    5、大文件上传造成Dos
    6、上传视频耗费CDN流量费

    在这里给出几种取文件后缀名的demo,当然了代码写的并不健壮,还需各位去补充判断是否为array、isset参数名是否存在等等。

    $ext = strrchr($filename, '.');
    
    $ext = pathinfo($filename)['extension'];
    
    $ext = array_pop(explode('.', $filename));

发表评论

电子邮件地址不会被公开。 必填项已用*标注