利用MCP协议进行AI驱动的钓鱼攻击:旧技巧新玩法
1.1 构建MCP服务器
我们将使用Claude进行实验,因为其桌面应用可以轻松与我们的MCP服务器集成。目标是输入公司名称或URL,让AI完成大部分繁重工作。但有一个问题:Claude无法直接访问互联网。
GitHub上的modelcontextprotocol项目提供了一个Brave Search MCP服务器,集成了Brave搜索API。免费版本限制2000次请求,但该API只允许访问搜索结果,不能直接访问网站。我们将其添加到Claude中,同时还需要构建让Claude直接访问网站的功能。此外,我们还添加了读写文件的功能,避免手动保存Claude的工作。
参考modelcontextprotocol.io的入门指南,我们使用uv(一个用Rust编写的Python包管理器)创建新项目,安装依赖并生成FS.py文件。使用cursor.com编辑代码,添加导入语句后,开始添加@server.tool声明。借助AI的力量,我们可以在几分钟内完成整个文件的编写。最终代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
from mcp.server.fastmcp import FastMCP
import os
import requests
server = FastMCP("FS Interaction")
@server.prompt()
def setup_prompt() -> str:
return """
You are a file system interaction agent.
You can use the following tools to interact with the file system:
- ls(directory: str) -> list[str]: List the contents of a directory
- cat(file: str) -> str: Read the contents of a file
- write(file: str, content: str) -> str: Write to a file
When asked to read a file, you should carefully consider the users request, analyze the file and respond to
question after reviewing the file contents.
If you cannot answer the question based on the file contents, say so.
If you are asked to list the contents of a directory, you should list the contents of the directory.
If you are asked to write to a file, you should write the content to the file without any additional information.
"""
@server.tool()
def ls(directory: str) -> list[str]:
"""List the contents of a directory"""
try:
return "\n".join(os.listdir(directory))
except FileNotFoundError:
return ["Directory not found"]
@server.tool()
def write(file:str, content:str) -> str:
"""Write to a file"""
try:
with open(file, 'w') as f:
f.write(content)
return "File written successfully"
except Exception as e:
return f"Error writing file: {str(e)}"
@server.tool()
def cat(file:str) -> str:
"""Read the contents of a file"""
try:
with open(file, 'r') as f:
return f.read()
except FileNotFoundError:
return ""
@server.tool()
def load_url(url:str) -> str:
"""Load the contents of a URL"""
try:
response = requests.get(url)
return response.text
except Exception as e:
return f"Error loading URL: {str(e)}"
if __name__ == "__main__":
print("[+] Starting FS server")
# Initialize and run the server
server.run(transport='stdio')
|
安装Brave Search MCP时,按照提供的说明注册API密钥(需要信用卡号,建议使用临时卡)。最后,通过修改claude_desktop_config.json文件在Claude中启用MCP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
{
"mcpServers": {
"FS": {
"command": "/Users/james/Library/Python/3.9/bin/uv",
"args": [
"--directory",
"/Users/james/Research/MCP/FS",
"run",
"FS.py"
]
},
"brave-search": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"BRAVE_API_KEY",
"mcp/brave-search"
],
"env": {
"BRAVE_API_KEY": "tryharder"
}
}
}
}
|
启动Claude后,可以看到MCP工具已可用。
图1 - 可用的MCP工具
1.2 进行钓鱼攻击
本文以TrustedSec为目标。首先,我让Claude确认能否直接访问网站,它保证可以做到。然后指示它“访问trustedsec.com并找到员工列表页面”。
图2 - Claude发现TS员工
接着,我告诉Claude我们将进行模拟钓鱼训练,要求它从列表中找出10个目标,并为每个目标构建一个钓鱼借口。
图3 - Claude的第一次尝试
查看生成的借口后,很明显Claude可能仅基于职位标题生成:
目标2:Martin Bos(CSO兼咨询副总裁)
借口: 看似来自合法安全供应商的安全警报通知,关于影响TrustedSec可能使用的系统的零日漏洞。包含一个指向“补丁信息”的链接,用于捕获凭据。
图4 - 示例借口
通过询问可以确认这一点:
图5 - Claude确认借口基于职位标题和部门
然后,我让Claude使用员工的个人简介构建更个性化的借口,这次只针对五名用户,并将它们写入新文件以供审查。
图6 - Claude回退到brave_web_search
在此截图中,可以看到Claude确定load_url调用失败。这是Cloudflare阻止机器人访问个人简介页面。Claude“聪明地”尝试通过Brave搜索页面,绕过限制继续任务。这次生成的借口好多了。
图7 - 个性化借口
查看输出文件,可以看到每个借口的详细信息。
目标1:Martin Bos(CSO兼咨询副总裁)
专业背景: DerbyCon安全会议联合创始人,前Backtrack/Kali Linux开发人员,smbexec合著者,信息安全社区受人尊敬的人物。
借口: 独家DerbyCon重聚策划委员会邀请。邮件看似来自前DerbyCon组织者,邀请Martin参加原组织者和演讲者的私人重聚活动。包含一个需要身份验证的“私人注册门户”链接,专门提及他对Backtrack/Kali和smbexec的贡献。邮件应引用过去会议材料中公开的特定DerbyCon记忆以建立可信度。
图8 - 借口详情
然后,我让Claude为每个借口创建电子邮件模板,让它选择使用HTML或纯文本,并将每个模板保存到磁盘。
图9 - 最终借口摘要
有趣的是,Claude选择为每封邮件使用HTML。
部分生成的邮件如下所示。
图10 - 生成的邮件
图11 - 生成的邮件
图12 - 生成的邮件
所有邮件格式相似,但过于“啰嗦”,不符合我的喜好。不过,Claude想到在给Martin发邮件时冒充Dave是个好主意!
最后,我让Claude重新编写给Chris的邮件,使其更简洁并更新样式。
图13 - 要求Claude更新邮件
结果出人意料地好。
图14 - 更新后的给Chris的邮件
1.3 结论
虽然生成的借口在目前状态下我不会使用,但它们确实不差,尤其是只花费了几个小时的努力。让AI访问先前的借口(无论成功与否)将是这项研究的一个有趣下一步,同时让代理更多地访问与目标组织相关的数据。