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 提交一个什么东西。
ctf-web:Python爬虫应用_正则表达式
按照套路看一下响应头信息,其中有一个 flag 字段,值明显是个 base64 加密。
ctf-web:Python爬虫应用_python_02
拿去解密,这个应该就是需要交的东西了,不过它也是个 base64 加密后的字符串,二次解密得到 “553635”。
ctf-web:Python爬虫应用_python_03
使用 HackBar 用 POST 方法提交 margin 参数,但是没有得到答案?
ctf-web:Python爬虫应用_正则表达式_04
根据提示是我们操作得不够快,多抓几次包可以发现 flag 字段中的数据是不断在变化的。也就是说我们上传的 margin 参数需要和变化之前的 flag 值相匹配才行,若速度太慢 flag 值发生变化就无法得到答案。
ctf-web:Python爬虫应用_字符串_05
ctf-web:Python爬虫应用_php_06
ctf-web:Python爬虫应用_python_07
这个变化速度靠人力是做不到的,所以考虑使用 Python 爬虫。首先爬取网页获取响应头信息 headers,在 Python 中返回的是字典,使用 “flag” 为“键”可以获取该字段的值。
ctf-web:Python爬虫应用_客户端_08
接着使用 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 之内计算出表达式的值。
ctf-web:Python爬虫应用_python_09
如果是人力来做的话非常困难。此时我们考虑用爬虫把网页爬下来,然后用正则表达式提取其中的表达式。
ctf-web:Python爬虫应用_正则表达式_10
但是怎么提交呢?继续刷新页面,得到提示用 POST 方法提交一个 value 变量。
ctf-web:Python爬虫应用_python_11
现在我们来写 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”。
ctf-web:Python爬虫应用_客户端_12
考虑到一般情况下在 index.php 之类的文件中有源码,因此把 “index.php” 的 base64 编码结果 “aW5kZXgucGhw” 当做参数穿过去。
ctf-web:Python爬虫应用_python_13
怎么还是什么都没有?注意到还有个 line 参数,根据字面意义来理解这个应该是指源码的行数。将 line 设置为 1,成功返回一句源码。
ctf-web:Python爬虫应用_正则表达式_14
也就是说,现在要通过设置 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。
ctf-web:Python爬虫应用_正则表达式_15