1. 程式人生 > >詳解 CTF Web 中的快速反彈 POST 請求

詳解 CTF Web 中的快速反彈 POST 請求

目錄

檢視請求頭

檢視響應頭

0x00 前言

在 CTF Web 的基礎題中,經常出現一類題型:在 HTTP 響應頭獲取了一段有效期很短的 key 值後,需要將經過處理後的 key 值快速 POST 給伺服器,若 key 值還在有效期內,則伺服器返回最終的 flag,否則繼續提示“請再加快速度!!!”

如果還執著於手動地獲取 key 值,複製下來對其進行處理,最後用相應的工具把 key 值 POST 給伺服器,那麼對不起,因為 key 值的有效期一般都在 1 秒左右,除非有單身一百年的手速,否則不要輕易嘗試。顯然,這類題不是通過純手工完成的,幸好 Python 提供了簡單易用、功能強大的 HTTP ,幫助我們輕鬆解決關於 HTTP 的大部分問題。

0x01 Python Requests

關於 Requests 庫的詳細功能請見官方文件,本文只列出解題中需要用到的部分功能。

安裝並匯入 requests 模組

在安裝了 Python 的終端下輸入以下命令安裝 requests:

$ pip install requests

安裝完使用以下命令匯入 requests:

>>> import requests

傳送 GET 請求與 POST 請求

以 Github 官網為例,對其發起 GET 請求;

>>> r = requests.get('https://github.com/')

對其發起 POST 請求:

>>> r = requests.post('https://github.com/')

檢視請求頭

對 Github 官網發起請求,以檢視 GET 請求的請求頭為例,POST 請求同理:

>>> r = requests.get('https://github.com/')
>>> r.request.headers
{'Connection': 'keep-alive', 'Accept-Encoding': 'gzip, deflate',...

檢視請求頭的某一屬性:

>>> r.request.headers['Accept-Encoding']
'gzip, deflate'

檢視響應頭

對 Github 官網發起請求,以檢視 GET 請求的響應頭為例,POST 請求同理:

>>> r = requests.get('https://github.com/')
>>> r.headers
{'Status': '200 OK', 'Expect-CT': 'max-age=2592000, report-uri=...

檢視響應頭的某一屬性:

>>> r.headers['Status']
'200 OK'

檢視響應內容

對 Github 官網發起請求,檢視伺服器返回頁面的內容,以檢視 GET 請求的響應內容為例,POST 請求同理:

>>> r = requests.get('https://github.com/')
>>> r.text
u'\n\n\n\n\n\n<!DOCTYPE html>\n<html lang="en">\n  <head>\n    <meta charset="utf-8">\n...

傳遞 GET 請求引數

GET 請求引數作為查詢字串附加在 URL 末尾,可以通過 requests.get() 方法中的 params 引數完成。例如,我要構建的 URL 為 https://github.com/?username=ciphersaw&id=1,則可以通過以下程式碼傳遞 GET 請求引數:

>>> args = {'username': 'ciphersaw', 'id': 1}
>>> r = requests.get('https://github.com/', params = args)
>>> print(r.url)
https://github.com/?username=ciphersaw&id=1

其中 params 引數是 dict 型別變數。可以看到,帶有請求引數的 URL 確實構造好了,不過注意,這裡的 usernameid 是為了說明問題任意構造的,傳入 Github 官網後不起作用,下同。

傳遞 POST 請求引數

POST 請求引數以表單資料的形式傳遞,可以通過 requests.post() 方法中的 data 引數完成,具體程式碼如下:

>>> args = {'username': 'ciphersaw', 'id': 1}
>>> r = requests.post('https://github.com/', data = args)

其中 data 引數也是 dict 型別變數。由於 POST 請求引數不以明文展現,在此省略驗證步驟。

如果想傳遞自定義 Cookie 到伺服器,可以使用 cookies 引數。以 POST 請求為例向 Github 官網提交自定義 Cookie(cookies 引數同樣適用於 GET 請求):

>>> mycookie = {'userid': '123456'}
>>> r = requests.post('https://github.com/', cookies = mycookie)
>>> r.request.headers
...'Cookie': 'userid=123456',...

其中 cookies 引數也是 dict 型別變數。可以看到,POST 請求的請求頭中確實包含了自定義 Cookie。

會話物件 Session()

Session 是儲存在伺服器上的相關使用者資訊,用於在有效期內保持客戶端與伺服器之間的狀態。Session 與 Cookie 配合使用,當 Session 或 Cookie 失效時,客戶端與伺服器之間的狀態也隨之失效。

有關 Session 的原理可參見以下文章:

requests 模組中的 會話物件 Session() 能夠在多次請求中保持某些引數,使得底層的 TCP 連線將被重用,提高了 HTTP 連線的效能。

Session() 的建立過程如下:

>>> s = requests.Session()

在有效期內,同一個會話物件發出的所有請求都保持著相同的 Cookie,可以看出,會話物件也可以通過 getpost 方法傳送請求,以傳送 GET 請求為例:

>>> r = s.get('https://github.com/')

0x02 writeups

介紹完 requests 模組的基本使用方法,下面藉助幾道題來分析講解。另外,在 HTTP 響應頭中獲取的 key 值通常是經過 base64 編碼的,所以還需要引入內建模組 base64 用於解碼。以下程式碼均在 Python 3.6 環境下執行。

【實驗吧 CTF】 Web —— 天下武功唯快不破

此題是 Web 型別快速反彈 POST 請求的基礎題,結合 requests 模組與 base64 模組寫一個 Python 指令碼即可實現快速反彈 POST 請求。相關連結如下:

syb_fast_question

進入解題連結,發現如下提示:

syb_fast_page

“沒有一種武術是不可擊敗的,擁有最快的速度才能保持長勝,你必須竭盡所能做到最快。” 換句話說,如果我們沒有天下第一的手速,還是藉助工具來解題吧。再看看原始碼有沒什麼新發現:

syb_fast_page_source

提示說請用 POST 請求提交你發現的資訊,請求引數的鍵值是 key。最後按照常規思路看看響應頭:

syb_fast_response_header

結果發現有一個 FLAG 屬性,其值是一段 base64 編碼。在用 Python 指令碼解題之前,為了打消部分同學的疑慮,先看看純手工解碼再提交 POST 請求會有什麼效果:

syb_fast_submit_key

將 FLAG 值進行 base64 解碼後,在 Firefox 下用 New Hackbar 工具提交 POST 請求:

syb_fast_fail_page

提示需要你再快些,顯然必須要用程式語言輔助完成了。下面直接上 Python 指令碼解題:

import requests

import base64

url = 'http://ctf5.shiyanbar.com/web/10/10.php'

headers = requests.get(url).headers

key = base64.b64decode(headers['FLAG']).decode().split(':')[1]

post = {'key': key}

print(requests.post(url, data = post).text)

第 4 行是 URL 地址的字串;
第 5 行是獲得 GET 請求的響應頭;
第 6 行是先將響應頭中 FLAG 屬性的值 用base64 解碼,得到的結果為 bytes-like objects 型別,再用 decode() 解碼得到字串,最後用 split(':') 分離冒號兩邊的值,返回的 list 物件中的第二個元素即為要提交的 key 值;
第 7 行是構造 POST 請求中 data 引數的 dict 型別變數;
第 8 行提交帶有 data 引數的 POST 請求,最終列印響應頁面的內容。

執行完指令碼後,即可看到返回的最終 flag:

syb_fast_flag

【Bugku CTF】 Web —— Web6

此題是上一題的升級版,除了要求快速反彈 POST 請求,還要求所有的請求必須在同一個 Session 內完成,因此會話物件 Session() 就派上用場了。相關連結如下:

bugku_fast_question

進入解題連結,直接檢視原始碼:

bugku_fast_page_source

發現 POST 請求引數的鍵值為 margin,最後看看響應頭:

bugku_fast_response_header

發現 flag 屬性,其值同樣是一段 base64 編碼。這裡就不手工解碼再提交 POST 請求了,直接用上一題的 Python 指令碼試試:

此處注意第 6 行的 base64 解碼,因為經過第一次 base64 解碼後,仍然還是一段 base64 編碼,所以要再解碼一次。解題過程中,要自行動手檢視每一次解碼後的值,才能選擇合適的方法去獲得最終 key 值。

import requests

import base64

url = 'http://120.24.86.145:8002/web6/'

headers = requests.get(url).headers

key = base64.b64decode(base64.b64decode(headers['flag']).decode().split(":")[1])

post = {'margin': key}

print(requests.post(url, data = post).text)

結果如下,果然沒那麼容易得到 flag:

bugku_fast_fail_without_session

嗯,眉頭一緊,發現事情並不簡單。下面看看 GET 請求與 POST 請求的請求頭與響應頭是否內有玄機:

import requests

import base64

url = 'http://120.24.86.145:8002/web6/'

get_response = requests.get(url)

print('GET Request Headers:\n', get_response.request.headers, '\n')

print('GET Response Headers:\n', get_response.headers, '\n')

key = base64.b64decode(base64.b64decode(get_response.headers['flag']).decode().split(":")[1])

post = {'margin': key}

post_responese = requests.post(url, data = post)

print('POST Request Headers:\n', post_responese.request.headers, '\n')

print('POST Response Headers:\n', post_responese.headers, '\n')

不出所料,結果如下,原來是 GET 請求和 POST 請求的響應頭都有 Set-Cookie 屬性,並且值不相同,即不在同一個會話中,各自響應頭中的 flag 值也不等:

bugku_fast_fail_headers

接下來引入會話物件 Session(),稍作修改就能保證 GET 請求與 POST 請求在同一個會話中了:

import requests

import base64

url = 'http://120.24.86.145:8002/web6/'

s = requests.Session()

headers = s.get(url).headers

key = base64.b64decode(base64.b64decode(headers['flag']).decode().split(":")[1])

post = {"margin":key}

print(s.post(url, data = post).text)

與上一題程式碼的區別是:此處用會話物件 Session() 的 getpost 方法,而不是直接用 requests 模組裡的,這樣可以保持 GET 請求與 POST 請求在同一個會話中。將同一會話中的 key 值作為 POST 請求引數提交,最終得到 flag:

bugku_fast_flag

雖然到此即可結束,但為了驗證以上兩次請求真的在同一會話內,我們再次檢視請求頭與響應頭:

import requests

import base64

url = 'http://120.24.86.145:8002/web6/'

s = requests.Session()

get_response = s.get(url)

print('GET Request Headers:\n', get_response.request.headers, '\n')

print('GET Response Headers:\n', get_response.headers, '\n')

key = base64.b64decode(base64.b64decode(get_response.headers['flag']).decode().split(":")[1])

post = {'margin': key}

post_responese = s.post(url, data = post)

print('POST Request Headers:\n', post_responese.request.headers, '\n')

print('POST Response Headers:\n', post_responese.headers, '\n')

結果如下,GET 請求中響應頭的 Set-Cookie 屬性與 POST 請求中請求頭的 Cookie 屬性相同,表明兩次請求確實在同一會話中。

bugku_fast_success_headers

既然只需要保持兩次請求中 Cookie 屬性相同,那能不能構造 Cookie 屬性通過普通的 getpost 方法完成呢?答案是可以的。請見如下程式碼:

import requests

import base64

url = 'http://120.24.86.145:8002/web6/'

headers = requests.get(url).headers

key = base64.b64decode(base64.b64decode(headers['flag']).split(":")[1])

post = {"margin": key}

PHPSESSID = headers["Set-Cookie"].split(";")[0].split("=")[1]

cookie = {"PHPSESSID": PHPSESSID}

print(requests.post(url, data = post, cookies = cookie).text)

第 8 行獲得 GET 請求響應頭中 Set-Cookie 屬性的 PHPSESSID 值,該語句如何構造請自行分析 Set-Cookie 屬性字串值的結構;
第 9 行用第 7 行相同方法構造 POST 請求中 cookies 引數的 dict 型別變數;
第 10 行提交帶有 data 引數與 cookies 引數的 POST 請求,最終列印響應頁面的內容。

毫無疑問,以上程式碼的結果也是最終的 flag。

【Bugku CTF】 Web —— 秋名山老司機

前面兩題均是對響應頭中與flag相關的屬性做解碼處理,然後快速反彈一個 POST 請求得到 flag 值。而本題要求計算響應內容中的表示式,將結果用 POST 請求反彈回伺服器換取 flag 值。實際上換湯不換藥,依舊用 Python 寫個指令碼即可解決。

bugku_qiuming_driver

開啟解題連線,老規矩先看原始碼:

bugku_qiuming_page_source

題意很明確,要求在 2 秒內計算給出表示式的值…呃,然後呢?重新整理頁面再看看,噢噢,然後再將計算結果用 POST 請求反彈回伺服器,請求引數的 key 值為 value

bugku_qiuming_hint

從頁面內容中擷取表示式,可以用 string 自帶的 split() 函式,但必須先要知道表示式兩邊的字串,以其作為分隔符;也可以用正則表示式,僅需知道表示式本身的特徵即可。此處用正則表示式更佳。先放上題解指令碼,再來慢慢解析:

import requests

import re

url = 'http://120.24.86.145:8002/qiumingshan/'

s = requests.Session()

source = s.get(url)

expression = re.search(r'(\d+[+\-*])+(\d+)', source.text).group()

result = eval(expression)

post = {'value': result}

print(s.post(url, data = post).text)

有關 requests 的部分此處不細講,唯一要注意的是,與上一篇 writeup 一樣,要利用會話物件 Session(),否則提交結果的時候,重新生成了一個新的表示式,結果自然錯誤。

第 7 行是利用正則表示式擷取響應內容中的算術表示式。首先引入 re 模組,其次用 search() 匹配算術表示式,匹配成功後用 group() 返回算術表示式的字串。(想掌握正則表示式,還是要多看、多想、多練,畢竟應用場合非常之廣)

search() 的第一個引數是匹配的正則表示式,第二個引數是要匹配的字串。其中 \d+代表一個或多個數字;[+\-*] 匹配一個加號,或一個減號,或一個乘號,注意減號在中括號內是特殊字元,要用反斜槓轉義;(\d+[+\-*])+代表一個或多個由數字與運算子組成的匹配組;最後再加上剩下的一個數字 (\d+)

第 9 行在獲得算術表示式的字串後,直接利用 Python 的內建方法 eval() 來計算出結果,簡單、暴力、快捷。

執行完上述指令碼,就有一定的概率可以獲得 flag 了:

bugku_qiuming_flag

為什麼說是一定概率呢?讀者們自行嘗試便知,據我觀察,當計算結果超出一定長度時,伺服器就不響應了。在此猜想:可能客戶端 Python 指令碼計算錯誤,也可能伺服器端 PHP 指令碼對大數計算有誤差,還可能在 POST 請求過程中令大整數發生改變。至於是哪種,還請高手解答。

相關推薦

CTF Web 快速反彈 POST 請求

目錄 檢視請求頭 檢視響應頭 0x00 前言 在 CTF Web 的基礎題中,經常出現一類題型:在 HTTP 響應頭獲取了一段有效期很短的 key 值後,需要將經過處理後的 key 值快速 POST 給伺服器,若 k

AJAXget和post請求

一、get()和post()基本區別1.get 是把引數資料佇列加到提交表單的ACTION屬性所指的URL中,值和表單內各個欄位一一對應,在URL中可以看到。post是通過HTTP post機制,將表單內各個欄位與其內容放置在HTML HEADER內一起傳送到ACTION屬

ServletContextListener使用以及web容器獲取spring容器

ServletContextListener 介面是servlet的一個介面,它能夠監聽 ServletContext 物件的生命週期,實際上就是監聽 Web 應用的生命週期(Tomcat的啟動與關閉)。 伺服器啟動時,ServletContextListener 的 co

Ajaxget與post請求

5.<form method="get" action="a.asp?b=b">跟<form method="get" action="a.asp">是一樣的,也就是說,method為get時action頁面後邊帶的引數列表會被忽視;而<form method="post" a

《Unity3D 實戰核心技術》書關於矩陣的錯誤

不同的 linear 應該 印刷 幫助 tar 線性代數 計算 計算機圖形學 最近一直在學習實時渲染,不免要接觸線性代數。而渲染中,一定會用到矩陣,當我再次去復習我之前看的書時,發現《Unity3D 實戰核心技術詳解》關於矩陣就有幾處錯誤 ,特標註出來。 書的第一章《3

xml約束schema約束

開始時間:2018年9月27日20:56:14 結束時間:2018年9月27日23:18:30 累計時間:2小時 xml自定義:  一: 約束  1: dtd約束:   2: schema 約束:   優點:  

TCP/IP--TCP連線TIME WAIT狀態過多

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

AndroidJNI使用(1)---EclipseNDK配置So檔案生成

1、NDK下載和配置 NDK下載地址:http://www.androiddevtools.cn/ NDK下載完成後,選擇Eclipse上方Window選單Preferences - Android - NDK 在NDK&nb

Asp.Net MVC WebAPI的建立與前臺Jquery ajax後臺HttpClient呼叫 Asp.Net對操作Sql Server 簡單處理的SqlDB類

1、什麼是WebApi,它有什麼用途?           Web API是一個比較寬泛的概念。這裡我們提到Web API特指ASP.NET MVC Web API。在新出的MVC中,增加了WebAPI,用於提供REST風格的WebService,新生成的W

【python】collections模組OrderedDict的使用

很多人認為python中的字典是無序的,因為它是按照hash來儲存的,但是python中有個模組collections(英文,收集、集合),裡面自帶了一個子類OrderedDict,實現了對字典物件中元素的排序。 import collections print("Regular d

jquery外掛;(function ( $, window, document, undefined )的作用

詳解jquery外掛中;(function ( $, window, document, undefined )的作用 在jquery外掛中我們經常看到以下這段程式碼 1 2 3 ;(function ( $, windo

原型鏈的prototype和 __proto__

前言 本文主要是徹底講明白prototype和__proto__ 是幹嘛的,相信很多同學也跟我一樣,傻傻的分不清楚兩者應該如何使用?、在原型鏈中到底起到什麼作用?、 在繼承中起到什麼作用?、javascript為什麼會設計出這兩個屬性?等一系列問題,網上查遍許多資料,看的也是雲裡霧裡一頭霧

Javascript函式with

Javascript函式中with,with函式方便用來引用某個物件中已有的屬性,但是不能用來給物件新增屬性,要給物件建立新的屬性,下面通過程式碼給大家講解。 js函式中with函式的用法分析 定義 方便用來引用某個物件中已有的屬性,但是不能用來給物件

Objective-C委託和協議

Objective-C委託和協議本沒有任何關係,協議如前所述,就是起到C++中純虛類的作用,對於“委託”則和協議沒有關係,只是我們經常利用協議還實現委託的機制,其實不用協議也完全可以實現委託。 AD:51CTO 網+ 第十二期沙龍:大話資料之美_如何用資料驅動使用者體驗

python tsfresh特徵中文(更新

  tsfresh是開源的提取時序資料特徵的python包,能夠提取出超過64種特徵,堪稱提取時序特徵的瑞士軍刀。最近有需求,所以一直在看,目前還沒有中文文件, 有些特徵含義還是很難懂的,我把我已經看懂的一部分放這,沒看懂的我只寫了標題,待我看懂我添加註解。 tsfresh.fea

Java Web專案啟動執行順序

一、web.xml配置節點簡介 (1) context-param 格式定義 <context-param> <param-name>contextConfigLocation</param-name> <param-value>c

Immutable 及 React 實踐 (轉載)

轉載自:https://zhuanlan.zhihu.com/p/20295971,今天看到這篇文章後情不自禁的轉載過來了,真的非常值得收藏的一篇文章 Shared mutable state is the root of all evil(共享的可變狀態是萬惡之源) -- Pete Hunt

(譯)在React跨元件分發狀態的三種方法

英文原文連結:https://engineering.hexacta.com/three-approaches-to-distribute-the-state-across-components-in-react-da4db5a389e0 當我問自己第一百次時,我正在研究一個典型的CRUD螢幕:“我應該將

java程式設計會遇到的三種錯誤

1、語法錯 在程式設計和執行的過程中,發生錯誤是不可避免的。 語法錯(編譯錯)違反語法規範的錯誤,通常在編譯時發現。 Java編譯器能夠發現所有語法錯,給出錯誤的位置和性質。 2、語義錯(執行錯) 如果程式在語法上正確,但在語義上存在錯誤。 不能被編譯系統發現,只能到程式執行

網頁開發的頁面除錯【主要是谷歌瀏覽器的F12除錯功能】

做過網頁開發的都知道,不過你是用php還是asp.net以及java開發的網站,在開發過程中,web網頁展示頁面肯定會出現或多或少的問題。這裡我推薦使用谷歌瀏覽器進行Web網頁的除錯以及錯誤資訊定位。 使用谷歌瀏覽器除錯網頁,一般無需另外下載瀏覽器外掛,使用谷歌瀏覽器自帶的F12除錯功能即可完成大部分的除錯