文件上传——内容逻辑数组绕过
文件上传——内容逻辑数组绕过
1.Pass—14(图片码,判断文件类型)
有些上传网站是通过判断文件的前两个字节从而判断文件类型:
- Png:89 50 4E 47 0D 0A 1A 0A
- Jpg:FF D8
- Gif:47 49 46 38 39|37 61
- Bmp:42 4D
一般操作是使用HEX的软件,我比较习惯用ida在合适的位置写入相关的代码,之后再cmd命令中,将图片和php文件进行拼接,首先建立一个后门代码和准备一个白名单的图片文件
1 |
|
然后使用命令copy将两个文件合成
1 |
|
然后查看文件包含的漏洞代码
看到是有用于测试图片码是否能正常运行的代码,我们只需要打开file为我们上传的png文件,将脚本进行相关执行就能达到目的
值得一提的是,我在操作的过程中,合并两个文件后,在生成的jpg文件中虽然有php代码,但是少了<,所以在运行时出现了下图的错误,也就是无法解析出完成的php语言代码出来,具体原因我没有查找出来,如果我直接加入<又会导致图片崩溃,所以可以尝试16进制打开png文件直接手动加入php代码,也是可以的
2.Pass—15(图片码,函数检测文件类型)
有的通过getimagesize()函数检查是否为图片文件
破解原理同pass-14,只是一个是代码解析一个是函数解析,只要了解函数的本质即可攻破,getimagesize()函数检查图片,如果上传的不是图片文件的话,后续的代码就无法执行,也就是上传无法成功,后续的上传思路也就无法进行
3.Pass—16(图片码,同时检查字节和后缀)
采用后缀和字节的方式同时检查是前面的几种结合情况,一般是通过通过exif_imagetype()函数,读取图像的第一个字节,并检查其后缀名。而返回值与getimage()函数返回的索引2相同
4.Pass—17(图片码,二次渲染)
综合判断了后缀名、content-type,以及利用 imagecreatefromgif/png/jpg 函数该函数调用了PHP GD库。二次渲染是由Gif文件或 URL 创建新图像,成功则返回图像资源,失败则返回错误提示,这样子我们地方图片码的数据就会丢失,上传失败
因此想要成功上传图片马,我们需要制作一个二次渲染后,把原图和他修改过的图片进行比较,看看哪个部分没有被修改。将php代码放到没有被更改的部分,使得恶意代码依旧存在的图片即可
我在上传的时候,发现普通的png和jpg能上传,但是使用合成了php代码的jpg文件却被提示不是jpg格式,所以可能存在检测内容的详细内容
5.Pass—18(条件竞争一)
这主要是对于条件竞争的研究
查看代码,他是先将图片上传,之后才进行判断后缀名、二次渲染。我们只需要在上传的瞬间访问上传的文件,那服务器就不能对文件进行删除和二次渲染。也就是我们打开一个文件后,如果删除文件系统就会提示我们,该文件正在运行中,无法删除一样的道理。
首先发送php文件
然后使用另一个浏览器一直访问上传的php地址,只要在上传的一瞬间,在他还没删除和修改之前访问出来就没问题
5.Pass—19(条件竞争二)
与第18关不同的是这关是先检查了后缀名,然后上传,再进行二次渲染。所以需要我上传图片码然后重复18关的爆破操作即可,也就是访问地址加文件包含漏洞
6.Pass—20(方法一:%00截断)
由于之前提到的方法,在move_uploaded_file()函数中的img_path是由、save_name控制的,所以我们那可以在save_name利用%00进行文件名的截断
6.Pass—20(方法二:/.)
move_uploaded_file()有这么一个特性,会忽略掉文件末尾的 /.,也就说,用这种方式命名文件名,能过如果黑名单的审查,但是上传后会忽略/.,直接变成php的文件存到文件夹中
7.Pass—21(数组绕过+目录命名:ctf比较常见的文件上传类型)
这一关是利用数组绕过验证,通过我们自己定义的不同的内容的数组从而绕过验证条件达到上传成功的目的,这种情况一般生活中是比较少见的,只有在代码审计中才能发现,不然就是一点点试了
这段代码相对于把一个文件名分为了2个,以liaoyue.jpg为例子,分为了liaoyue和jpg,然后检查jpg的字符内容,通过比对决定能否通过,其中的end就是截取文件的后几位,进行检查
当我们输入两个数组时间,例如在上传的时候将save_name改为多个数组,也就是save_name[0]=’xxx.php/.’,save_name[2]=’jpg’,这样子file={‘xxx.php/.’,’’,’jpg’},取后面的值也就直接取了jpg了,最后就能通过比对。
然后是代码中的**$file_name = reset($file) . ‘.’ . $file[count($file) - 1];**,这里count($file)为2,减去1为1,所以是$file[1],有因为我们上面赋值为空,所以上传时直接就等于xxx.php/.,而不是xxx.php/.jpg