和WEBSHELL学PHP之二
info
本期主要介绍webshell中文件操作类函数
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的文章供大家参考
-
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));