CVE-2024-34695 (published 2024-05-14T15:39Z)

WOWS Karma is a reputation system for Wargaming\'s World of Warships. A user is able to click multiple times on "create" on a post creation prompt before the modal closes, which triggers sending several post creation API requests at once. Due to timing, sending multiple posts simultaneously requests bypasses the cooldown validation, however are not refreshing a user\'s metrics more than once, due to concurrent karma updates. This issue is fixed in 0.17.4.1.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/SakuraIsayeki/WOWS-Karma
commit_details: repo_url: https://github.com/SakuraIsayeki/WOWS-Karma
author: Sakura Akeno Isayeki
author_date: 2024-05-10 17:20:39+02:00
msg: feat(api): Add UserAtomicLock middleware to request pipeline

- Added `UserAtomicLock` attribute to the `CreatePost` action in `PostController.cs`
- Registered `UserAtomicLockMiddleware` as a singleton in `Startup.cs`
- Added `app.UseMiddleware<UserAtomicLockMiddleware>()` in the HTTP request pipeline
repository: repo_url: https://github.com/SakuraIsayeki/WOWS-Karma
repo_name: SakuraIsayeki/WOWS-Karma
description: Persistent and Comprehensive Karma system for World of Warships
repo_language: C#
owner: SakuraIsayeki
file_changes: filename: PostController.cs
change_type: ModificationType.MODIFY
diff: @@ -196,7 +196,7 @@ public sealed class PostController : ControllerBase
 	/// <response code="422">Attached replay is invalid.</response>
 	/// <response code="403">Restrictions are in effect for one of the targeted accounts.</response>
 	/// <response code="404">One of the targeted accounts was not found.</response>
-	[HttpPost, Authorize(RequireNoPlatformBans)]
+	[HttpPost, Authorize(RequireNoPlatformBans), UserAtomicLock]
 	[ProducesResponseType(201), ProducesResponseType(400), ProducesResponseType(422), ProducesResponseType(typeof(string), 403), ProducesResponseType(typeof(string), 404)]
 	public async Task<IActionResult> CreatePost(
 		[FromForm] string postDto, 

programming_language: C#
filename: Startup.cs
change_type: ModificationType.MODIFY
diff: @@ -10,7 +10,7 @@ using Nodsoft.WowsReplaysUnpack;
 using System.IdentityModel.Tokens.Jwt;
 using System.Net;
 using System.Reflection;
-using System.Text;
+using System.Text; 
 using Hangfire;
 using Hangfire.PostgreSql;
 using Hangfire.Tags.PostgreSql;
@@ -280,6 +280,8 @@ public sealed class Startup
 		services.AddResiliencePolicies();
 
 		services.AddSystemd();
+
+		services.AddSingleton<UserAtomicLockMiddleware>();
 	}
 
 	// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@@ -334,7 +336,8 @@ public sealed class Startup
 		app.UseAuthorization();
 
 		app.UseMiddleware<RequestLoggingMiddleware>();
-
+		app.UseMiddleware<UserAtomicLockMiddleware>();
+		
 		app.UseEndpoints(endpoints =>
 		{
 			endpoints.MapDefaultControllerRoute();

programming_language: C#
repo_url: https://github.com/SakuraIsayeki/WOWS-Karma
commit_details: repo_url: https://github.com/SakuraIsayeki/WOWS-Karma
author: Sakura Akeno Isayeki
author_date: 2024-05-10 17:18:55+02:00
msg: feat(api): Add UserAtomicLock attribute and middleware

This commit adds the `UserAtomicLock` attribute and middleware to the API. The `UserAtomicLock` attribute is used to lock concurrency on a given action to a specific user. The `UserAtomicLockMiddleware` provides the functionality to lock a given action to a specific user. This ensures that only one request from the same user can access the locked action at a time, preventing unwanted concurrent modifications.
repository: repo_url: https://github.com/SakuraIsayeki/WOWS-Karma
repo_name: SakuraIsayeki/WOWS-Karma
description: Persistent and Comprehensive Karma system for World of Warships
repo_language: C#
owner: SakuraIsayeki
file_changes: filename: UserAtomicLockAttribute.cs
change_type: ModificationType.ADD
diff: @@ -0,0 +1,10 @@
+﻿namespace WowsKarma.Api.Infrastructure.Attributes;
+
+/// <summary>
+/// Provides an attribute to lock concurrency on a given action to a given user.
+/// </summary>
+[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
+public class UserAtomicLockAttribute : Attribute
+{
+    public UserAtomicLockAttribute() { }
+}
\ No newline at end of file

programming_language: C#
filename: UserAtomicLockMiddleware.cs
change_type: ModificationType.ADD
diff: @@ -0,0 +1,64 @@
+﻿using System.Collections.Concurrent;
+using WowsKarma.Api.Infrastructure.Attributes;
+using WowsKarma.Common;
+
+namespace WowsKarma.Api.Middlewares;
+
+/// <summary>
+/// Provides a middleware to lock a given action to a given user.
+/// </summary>
+public class UserAtomicLockMiddleware : IMiddleware
+{
+    private static readonly ConcurrentDictionary<Tuple<string, uint>, object> Locks = new();
+    private static object ConcurrencyLock = new();
+
+    /// <inheritdoc />
+    public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
+    {
+        // Get the current user and endpoint
+        uint? uid = ctx.User.ToAccountListing()?.Id;
+        PathString path = ctx.Request.Path;
+
+        if (uid is null)
+        {
+            // Pass through.
+            await next(ctx);
+            return;
+        }
+
+        if (ctx.GetEndpoint()?.Metadata.GetMetadata<UserAtomicLockAttribute>() is null)
+        {
+            // Pass through.
+            await next(ctx);
+            return;
+        }
+
+        bool lockExists;
+        // lock (ConcurrencyLock)
+        // {
+             lockExists = Locks.TryGetValue(new(path, uid.Value), out _) || !Locks.TryAdd(new(path, uid.Value), new());
+        // }
+        
+        // Get or try to add the lock object.
+        if (lockExists)
+        {
+            // Lock is already taken.
+            ctx.Response.StatusCode = StatusCodes.Status429TooManyRequests;
+            return;
+        }
+        
+        // Hold the lock for the request's duration.
+        try
+        {
+            await next(ctx);
+        }
+        finally
+        {
+            // lock (ConcurrencyLock)
+            // {
+                // Release the lock.
+                Locks.TryRemove(new(path, uid.Value), out _);
+            // }
+        }
+    }
+}
\ No newline at end of file

programming_language: C#