-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add helpfuldesk writeup * Update title --------- Co-authored-by: Eric Daigle <[email protected]> Co-authored-by: Lydxn <[email protected]>
- Loading branch information
1 parent
1af38c8
commit 3628ad1
Showing
1 changed file
with
105 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
--- | ||
layout: post | ||
title: "[NahamCon CTF 2024] Helpful Desk" | ||
author: edaigle | ||
--- | ||
|
||
## Problem Description | ||
|
||
> HelpfulDesk is the go-to solution for small and medium businesses who need remote monitoring and management. Last night, HelpfulDesk released a security bulletin urging everyone to patch to the latest patch level. They were scarce on the details, but I bet that can't be good... | ||
This was categorized as a web challenge, although most of my time on it was spent reverse engineering. | ||
|
||
Difficulty: easy | ||
|
||
## Initial Research | ||
|
||
Opening up the URL, we see this is supposed to be a login to a remote access | ||
software. There's a note at the top telling us to download the latest update | ||
for important security fixes. Clicking the note brings us to an "updates" page | ||
with a list of releases we can download. Presumably the current instance is | ||
running the old insecure version, so let's download it and the latest and | ||
find the difference. | ||
|
||
## Exploring the codebase | ||
|
||
Downloading the two versions and unzipping them, we see this is a .NET server. | ||
Running diff on the folders, we see the only thing that has changed is | ||
HelpfulDesk.dll. | ||
|
||
## Decompiling | ||
|
||
Let's decompile the old and new versions of the DLL with AvaloniaILSpy. | ||
Renaming the dlls for convenience to HelpfulDesk-old and HelpfulDesk-new, | ||
we can conveniently export the decompiled code to a flat text file by | ||
right-clicking each dll and choosing "Save Code." | ||
|
||
Now we can open both files in Emacs and use ediff to find the changes. | ||
After skipping through the filenames and a few uninteresting hashes, we | ||
only find one significant change: | ||
|
||
### HelpfulDesk-new.dll | ||
|
||
``` c# | ||
public IActionResult SetupWizard() | ||
{ | ||
//IL_0018: Unknown result type (might be due to invalid IL or missing references) | ||
//IL_001d: Unknown result type (might be due to invalid IL or missing references) | ||
if (File.Exists(_credsFilePath)) | ||
{ | ||
PathString path = ((ControllerBase)this).get_HttpContext().get_Request().get_Path(); | ||
string text = ((PathString)(ref path)).get_Value().TrimEnd('/'); | ||
if (text.Equals("/Setup/SetupWizard", StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
return (IActionResult)(object)((Controller)this).View("Error", (object)new ErrorViewModel | ||
{ | ||
RequestId = "Server already set up.", | ||
ExceptionMessage = "Server already set up.", | ||
StatusCode = 403 | ||
}); | ||
} | ||
} | ||
return (IActionResult)(object)((Controller)this).View(); | ||
} | ||
``` | ||
|
||
### HelpfulDesk-old.dll | ||
|
||
``` c# | ||
public IActionResult SetupWizard() | ||
{ | ||
//IL_0018: Unknown result type (might be due to invalid IL or missing references) | ||
//IL_001d: Unknown result type (might be due to invalid IL or missing references) | ||
if (File.Exists(_credsFilePath)) | ||
{ | ||
PathString path = ((ControllerBase)this).get_HttpContext().get_Request().get_Path(); | ||
string value = ((PathString)(ref path)).get_Value(); | ||
if (value.Equals("/Setup/SetupWizard", StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
return (IActionResult)(object)((Controller)this).View("Error", (object)new ErrorViewModel | ||
{ | ||
RequestId = "Server already set up.", | ||
ExceptionMessage = "Server already set up.", | ||
StatusCode = 403 | ||
}); | ||
} | ||
} | ||
return (IActionResult)(object)((Controller)this).View(); | ||
} | ||
``` | ||
|
||
## Exploit | ||
|
||
I don't know the exact mechanisms here, but at | ||
a high level it seems to be controlling access to the /Setup/SetupWizard | ||
endpoint. If the credential file exists, it denies access to the endpoint. | ||
Presumably the SetupWizard lets us reset credentials, so this ensures only the | ||
admin doing the initial setup can access it. | ||
|
||
The difference between the function in the old and new files is that the new one | ||
strips trailing slashes from /Setup/SetupWizard. We can see the security flaw: if | ||
we navigate to the path with the trailing slash, the value.Equals() won't be triggered, | ||
but ASP.NET will ignore the slash and serve us the Setup page. | ||
|
||
Giving it a try, this works! I get the setup page and reset the login credentials. I | ||
then login and find the flag on the first connected computer's desktop. |