Request 庫
get 方法
Python requests 庫的 get()方法非常常用,可以用於獲取網頁的源碼等信息,該方法的語法為:
requests.get(url, params=None, **kwargs)
參數 | 說明 |
---|---|
url | 擬獲取頁面的url鏈接 |
params | url中的額外參數,字典或字節流格式,可選 |
**kwargs | 12個控制訪問的參數 |
除了 get() 方法,常用的方法有:
方法 | 說明 |
---|---|
requests.get() | 獲取 HTML 網頁的主要方法,對應於 HTTP 的 GET |
requests.head() | 獲取 HTML 網頁頭信息的方法,對應於 HTTP 的 HEAD |
requests.post() | 向 HTML 網頁提交 POST 請求的方法,對應於 HTTP 的 POST |
Request 對象
當我們使用 get() 方法時,就會構造一個向服務器請求資源的 Request 對象。Request 對象的作用是與客戶端交互,收集客戶端的 Form、Cookies、超鏈接,或者收集服務器端的環境變量。request 對象是從客戶端向服務器發出請求,包括用戶提交的信息以及客戶端的一些信息。客戶端可通過 HTML 表單或在網頁地址后面提供參數的方法提交數據,然后服務器通過 request 對象的相關方法來獲取這些數據。
Response 對象
Response 對象用於動態響應客戶端請示,控制發送給用戶的信息,並將動態生成響應。get() 方法將會返回一個包含服務器資源的 Response 對象,response 對象的屬性如下:
屬性 | 說明 |
---|---|
r.status_code | HTTP請求的返回狀態,200表示連接成功,404表示失敗 |
r.text | HTTP響應內容的字符串形式,即,url對應的頁面內容 |
r.encoding | 從HTTP header中猜測的響應內容編碼方式 |
r.apparent_encoding | 從內容分析出的響應內容編碼方式(備選編碼方式) |
r.content | HTTP響應內容的二進制形式 |
session 會話對象
會話對象讓你能夠跨請求保持某些參數,它也會在同一個 Session 實例發出的所有請求之間保持 cookie。所以如果你向同一主機發送多個請求,底層的 TCP 連接將會被重用,從而帶來顯著的性能提升。會話也可用來為請求方法提供缺省數據,這是通過為會話對象的屬性提供數據來實現的。定義一個 Session 實例語法為:
s = requests.Session()
正則匹配
Python 支持正則表達式,使用正則表達式可以匹配所需要的數據。下面看 2 個常用的方法。
re.match() 方法
re.match() 方法可以從字符串的起始位置匹配一個模式,如果不是起始位置匹配成功的話 match() 就返回 none。
re.match(pattern, string, flags=0)
參數 | 說明 |
---|---|
pattern | 匹配的正則表達式 |
string | 要匹配的字符串 |
flags | 標志位 |
匹配成功 re.match() 方法返回一個匹配的對象,可以使用 group(num) 或 groups() 匹配對象函數來獲取匹配得到表達式。
re.search() 方法
re.search 掃描整個字符串並返回第一個成功的匹配,匹配成功 re.search() 方法返回一個匹配的對象,否則返回 None。同樣是使用 group(num) 或 groups() 匹配對象函數,來獲取匹配得到表達式。
re.search(pattern, string, flags=0)
參數 | 說明 |
---|---|
pattern | 匹配的正則表達式 |
string | 要匹配的字符串 |
flags | 標志位 |
re.match 只匹配字符串的開始,如果字符串開始不符合正則表達式則匹配失敗.而re.search 匹配整個字符串,直到找到一個匹配。
例題:bugku-web 基礎 $_POST
題目的源碼如下,需要用 POST 方法提交一個參數 what,值為 "flag"。
$what = $_POST['what'];
echo $what;
if($what == 'flag')
echo 'flag{****}';
用 Python 的 requests 庫的 post 方法提交一個字典上去,然后將對象輸出查看。
import requests
value = {"what":"flag"}
r = requests.post("http://123.206.87.240:8002/post/",value)
r.text
例題:bugku-速度要快
打開網頁,首先打開 F12,題目需要我們用 POST 提交一個什么東西。
按照套路看一下響應頭信息,其中有一個 flag 字段,值明顯是個 base64 加密。
拿去解密,這個應該就是需要交的東西了,不過它也是個 base64 加密后的字符串,二次解密得到 “553635”。
使用 HackBar 用 POST 方法提交 margin 參數,但是沒有得到答案?
根據提示是我們操作得不夠快,多抓幾次包可以發現 flag 字段中的數據是不斷在變化的。也就是說我們上傳的 margin 參數需要和變化之前的 flag 值相匹配才行,若速度太慢 flag 值發生變化就無法得到答案。
這個變化速度靠人力是做不到的,所以考慮使用 Python 爬蟲。首先爬取網頁獲取響應頭信息 headers,在 Python 中返回的是字典,使用 “flag” 為“鍵”可以獲取該字段的值。
接着使用 Python 自帶的 base64 庫使用 b64decode() 方法進行 base64 解碼,截取 flag 部分進行二次解碼。最后使用 post() 方法提交 margin 參數,輸出返回的文本即可。
import requests
import base64
r = requests.Session()
headers = r.get("http://123.206.87.240:8002/web6/").headers
str = repr(base64.b64decode(headers['flag']))
str = base64.b64decode(str[str.find(":") + 2:])
data= {'margin':str}
flag = r.post("http://123.206.87.240:8002/web6/",data = data)
print(flag.text)
此處注意 2 個地方,第一是要先用 repr() 函數將對象轉化為供解釋器讀取的形式,這樣才能保證后面的代碼可以處理數據。第二是需要用 requests.Session() 創建一個 session 對象,session 對象能夠讓我們跨 http 請求保持某些參數,即讓同一個 session 對象發送的請求頭攜帶某個指定的參數。爬蟲和提交參數時,需要用同一個 session 對象來實現。
例題:bugku-秋名山老司機
打開題目,題目要求在 2s 之內計算出表達式的值。
如果是人力來做的話非常困難。此時我們考慮用爬蟲把網頁爬下來,然后用正則表達式提取其中的表達式。
但是怎么提交呢?繼續刷新頁面,得到提示用 POST 方法提交一個 value 變量。
現在我們來寫 Python 腳本,首先要用 requests.Session() 創建一個 session 對象,並且使用 get() 方法把網頁爬下來。接下來進行正則表達式匹配,使用 re.search() 方法實現。匹配的正則表達式書寫格式為:r 表示字符串為原始字符串("" 不認為是轉義字符),“\d+” 匹配一個或者多個字符,“[+-*]” 匹配加號,加號,乘號,因為式子里面包含這三種運算,"-“ 在中括號里面為特殊符號,使用”"轉義,最后 “\d+” 再匹配一個字符或者多個字符就滿足了式子格式。當然因為這個算式是在 div
標簽中的,因此正則匹配 div 標簽也可以。綜上所述,匹配的正則表達式為:
r'(\d+[+\-*])+(\d+)'
<div>(.*?)</div>
eval() 函數用來執行一個字符串表達式,並返回表達式的值。使用 eval() 函數計算出匹配到的表達式的值之后,用 post() 方法上傳。
import requests
import re
s = requests.Session()
r = s.get("http://123.206.87.240:8002/qiumingshan/")
expression = re.search(r'(\d+[+\-*])+(\d+)', r.text)
value = eval(str(expression.group()))
data = {"value": value}
r = s.post("http://123.206.87.240:8002/qiumingshan/", data = data)
print(r.text)
例題:bugku-cookies 欺騙
首先打開網頁,顯示了一堆沒用的東西,嘗試過幾種解碼后放棄。注意到 url 中的 filename 的值為一段 base64 編碼,解碼后是 “keys.txt”。
考慮到一般情況下在 index.php 之類的文件中有源碼,因此把 “index.php” 的 base64 編碼結果 “aW5kZXgucGhw” 當做參數穿過去。
怎么還是什么都沒有?注意到還有個 line 參數,根據字面意義來理解這個應該是指源碼的行數。將 line 設置為 1,成功返回一句源碼。
也就是說,現在要通過設置 line 參數的值,以此獲取源碼。由於不知道具體有幾行,可以寫一個 Python 腳本來獲取源碼的所有行。
import requests
s = requests.Session()
for i in range(50):
r = s.get("http://123.206.87.240:8002/web11/index.php?line="+ str(i) + "&filename=aW5kZXgucGhw")
print(r.text)
成功獲得源碼如下,源碼中知道了還有個 key.php 可以訪問,但是需要 cookie 的內容為 margin = margin時才能訪問。
<?php
error_reporting(0);
$file = base64_decode(isset($_GET['filename'])?$_GET['filename']:"");
$line = isset($_GET['line'])?intval($_GET['line']):0;
if($file=='')
header("location:index.php?line=&filename=a2V5cy50eHQ=");
$file_list = array('0' =>'keys.txt','1' =>'index.php',);
if(isset($_COOKIE['margin']) && $_COOKIE['margin']=='margin'){
$file_list[2]='keys.php';
}
if(in_array($file, $file_list)){
$fa = file($file);
echo $fa[$line];
}
此時把 filename 的參數設置為 key.php 的 base64 編碼 “a2V5LnBocA==”,然后用 HackBar 傳遞一個 cookie 過去,打開 F12 得到 flag。